Avant d'en venir à l'explication, il convient de rappeler quelques faits :
- MySQL ne prend pas en charge les espaces réservés nommés
- PDO prend en charge les caractères de remplacement nommés et positionnés, quels que soient le pilote de base de données et le mode d'émulation.
- En mode non émulé
prepare()
envoie la chaîne SQL à MySQL pour la préparation de la PS native ; en mode émulé, la chaîne SQL n'est envoyée qu'à l'adresse execute()
en remplaçant les valeurs par des caractères génériques
Explication
Lorsque vous appelez prepare()
en mode non émulé avec le pilote PDO_MySQL, PDO doit analyser le code SQL et l'envoyer à MySQL. Comme MySQL ne prend en charge que les caractères de remplacement positionnels, PDO remplace tous les caractères de remplacement nommés par des caractères de remplacement de type ?
. Par exemple, si vous préparez une requête SELECT :name AS foo
, l'AOP est obligé d'envoyer SELECT ? AS foo
.
Lorsque vous appelez execute()
PDO a besoin de lier les paramètres aux caractères de remplacement. Le problème à ce stade est que la requête SQL n'a plus de placeholders nommés. Mais PDO se souvient des placeholders nommés tels qu'ils apparaissaient dans la requête SQL, associés à leur numéro ordinal. Parce que la liaison des paramètres dans MySQL se fait en utilisant des paramètres positionnels, les paramètres nommés ont besoin d'être remappés à leurs numéros ordinaux, mais les paramètres positionnels n'ont pas besoin d'être remappés. Ils ont seulement besoin d'être vérifiés par rapport à la carte des paramètres positionnels pour s'assurer que l'index du tableau correspond à un paramètre positionnel valide. * .
Vous pouvez voir le code ici https://github.com/php/php-src/blob/master/ext/pdo/pdo_stmt.c#L44
Les préparations émulées fonctionnent un peu différemment. Il n'y a pas de liaison de paramètres, de sorte que toute cette logique ne s'applique pas. Lorsqu'une PS émulée est exécutée, l'analyseur syntaxique remplace simplement chaque espace réservé nommé par un espace cité (voir PDO::quote()
) de la chaîne de caractères tirée du tableau param. C'est la raison pour laquelle l'espace réservé nommé peut apparaître plusieurs fois dans le code SQL, car la position n'a pas d'importance.
Il convient également de souligner que certains pilotes de bases de données prennent en charge les paramètres nommés et/ou ont une syntaxe différente pour l'inclusion de paramètres dans le langage SQL.
Cette logique explique également pourquoi les espaces réservés nommés ne peuvent pas être réutilisés dans les préparations non émulées.
La liste des caractères de remplacement est stockée en interne sous la forme d'une liste de chaînes de caractères. Même si vous essayez d'utiliser deux fois des espaces réservés nommés avec des préparations natives, le code ne peut pas gérer la liaison. Il parcourra la liste et s'arrêtera à la première correspondance. Par exemple, avec un code SQL comme celui-ci :
SELECT :one, :two, :one
La carte se présente comme suit : [':one', ':two', ':one']
Si vous essayez ensuite de l'exécuter, il n'y aura que 2 paramètres execute([':one' => 1, 'two' => 2])
. Ils seront mis en correspondance avec les positions 1 et 2 respectivement. Cela signifie que la position 3 ne sera jamais associée à une valeur quelconque. Ceci devrait expliquer pourquoi il n'est pas possible d'utiliser des caractères génériques nommés avec des PS natifs.
* En fait, PDO redéfinit les paramètres positionnels en paramètres nommés, car différents pilotes lient les paramètres de différentes manières et il faut que ce soit robuste. Voir par exemple Oracle - Named Binds et Positional Binds