@@ -768,6 +768,17 @@ public function escapeIdentifiers(mixed $item): mixed
768768 return array_map ([$ this , 'escapeIdentifiers ' ], $ item );
769769 }
770770
771+ if ($ item instanceof Stringable) {
772+ $ item = (string ) $ item ;
773+ }
774+
775+ $ item = trim ($ item );
776+
777+ // Vérifier d'abord si c'est un appel de fonction SQL
778+ if ($ processed = $ this ->processSqlFunctionCall ($ item )) {
779+ return $ processed ;
780+ }
781+
771782 if (! isset ($ this ->escapeCache [$ item ])) {
772783 $ this ->escapeCache [$ item ] = $ this ->doEscapeIdentifiers ($ item );
773784 }
@@ -777,10 +788,6 @@ public function escapeIdentifiers(mixed $item): mixed
777788
778789 protected function doEscapeIdentifiers (string $ item ): string
779790 {
780- if ($ this ->isReserved ($ item ) || Utils::isSqlFunction ($ item )) {
781- return $ item ;
782- }
783-
784791 if (str_contains ($ item , '. ' )) {
785792 $ parts = explode ('. ' , $ item );
786793
@@ -795,13 +802,116 @@ protected function doEscapeIdentifiers(string $item): string
795802 */
796803 protected function escapeIdentifier (string $ item ): string
797804 {
805+ if ($ this ->isReserved ($ item ) || Utils::isSqlFunction ($ item )) {
806+ return $ item ;
807+ }
808+
798809 if ($ this ->isEscapedIdentifier ($ item )) {
799810 return $ item ;
800811 }
801812
802813 return $ this ->escapeChar . $ item . $ this ->escapeChar ;
803814 }
804815
816+ /**
817+ * Traite un appel de fonction SQL et échappe ses paramètres si nécessaire
818+ */
819+ protected function processSqlFunctionCall (string $ value ): ?string
820+ {
821+ // Pattern pour capturer: functionName(param1, param2, ...)
822+ if (preg_match ('/^(\w+)\s*\((.*)\)$/ ' , $ value , $ matches )) {
823+ $ functionName = $ matches [1 ];
824+ $ parameters = $ matches [2 ];
825+
826+ if (Utils::isSqlFunction ($ functionName )) {
827+ // Si pas de paramètres, retourner tel quel
828+ if (trim ($ parameters ) === '' ) {
829+ return $ value ;
830+ }
831+
832+ // Traiter chaque paramètre
833+ $ processedParams = [];
834+ $ params = $ this ->splitParameters ($ parameters );
835+
836+ foreach ($ params as $ param ) {
837+ $ param = trim ($ param );
838+ $ processedParams [] = $ this ->processSqlFunctionParameter ($ param );
839+ }
840+
841+ return $ functionName . '( ' . implode (', ' , $ processedParams ) . ') ' ;
842+ }
843+ }
844+
845+ return null ;
846+ }
847+
848+ /**
849+ * Traite un paramètre d'appel de fonction SQL
850+ */
851+ protected function processSqlFunctionParameter (string $ param ): string
852+ {
853+ // Si c'est un wildcard
854+ if ($ param === '* ' ) {
855+ return $ param ;
856+ }
857+
858+ // Si le paramètre contient un point, c'est un identifiant qualifié
859+ if (str_contains ($ param , '. ' )) {
860+ // Séparer les parties et échapper chaque partie individuellement
861+ $ parts = explode ('. ' , $ param );
862+ $ parts = array_map ($ this ->escapeIdentifier (...), $ parts );
863+
864+ return implode ('. ' , $ parts );
865+ }
866+
867+ // Si le paramètre est un identifiant simple
868+ if (preg_match ('/^[a-zA-Z_][a-zA-Z0-9_]*$/ ' , $ param )) {
869+ return $ this ->escapeIdentifier ($ param );
870+ }
871+
872+ // Si c'est un appel de fonction imbriqué
873+ if (preg_match ('/^(\w+)\s*\(.*\)$/ ' , $ param )) {
874+ return $ this ->processSqlFunctionCall ($ param ) ?? $ param ;
875+ }
876+
877+ // Sinon, garder tel quel (nombres, chaînes entre quotes, etc.)
878+ return $ param ;
879+ }
880+
881+ /**
882+ * Sépare les paramètres d'une fonction en gérant les virgules dans les parenthèses
883+ */
884+ protected function splitParameters (string $ parameters ): array
885+ {
886+ $ params = [];
887+ $ current = '' ;
888+ $ depth = 0 ;
889+ $ length = strlen ($ parameters );
890+
891+ for ($ i = 0 ; $ i < $ length ; $ i ++) {
892+ $ char = $ parameters [$ i ];
893+
894+ if ($ char === '( ' ) {
895+ $ depth ++;
896+ $ current .= $ char ;
897+ } elseif ($ char === ') ' ) {
898+ $ depth --;
899+ $ current .= $ char ;
900+ } elseif ($ char === ', ' && $ depth === 0 ) {
901+ $ params [] = trim ($ current );
902+ $ current = '' ;
903+ } else {
904+ $ current .= $ char ;
905+ }
906+ }
907+
908+ if (trim ($ current ) !== '' ) {
909+ $ params [] = trim ($ current );
910+ }
911+
912+ return $ params ;
913+ }
914+
805915 /**
806916 * Vérifie si un identifiant est réservé
807917 */
@@ -1133,7 +1243,11 @@ public function error(): array
11331243 public function lastId (?string $ table = null ): ?int
11341244 {
11351245 try {
1136- return (int ) $ this ->pdo ->lastInsertId ($ table );
1246+ if (-1 === $ id = $ this ->result ?->lastId() ?? -1 ) {
1247+ $ id = $ this ->pdo ->lastInsertId ($ table );
1248+ }
1249+
1250+ return (int ) $ id ;
11371251 } catch (PDOException ) {
11381252 return null ;
11391253 }
0 commit comments