4 votes

Pourquoi PDO permet-il d'utiliser un tableau indexé avec des placeholders nommés, mais seulement lorsque l'émulation est désactivée ?

J'ai appris récemment que PDO permettait d'utiliser indexé avec des espaces réservés nommés, comme ceci :

$stmt->prepare("INSERT INTO TABLE (one, two) VALUES (:one, :two)");
$stmt->execute([1,2]);

étonnamment, cela fonctionne, de même que d'autres méthodes plus familières.

$stmt->execute(["one" => 1, "two" => 2]);

Je me serais attendu à ce que ce code génère une erreur, ce qui est le cas, mais uniquement lorsque le mode d'émulation PDO est désactivé - c'est-à-dire lorsque des instructions préparées natives sont utilisées :

$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$stmt = $pdo->prepare("select :one one, :two two");
$stmt->execute([1,2]);
var_dump($stmt->fetch(PDO::FETCH_ASSOC));

$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);
$stmt = $pdo->prepare("select :one one, :two two");
$stmt->execute([1,2]);
var_dump($stmt->fetch(PDO::FETCH_ASSOC));

qui produit un tableau normal pour le premier extrait de code et une erreur pour le second :

Erreur fatale : SQLSTATE[HY093] : Numéro de paramètre invalide : le paramètre n'a pas été défini à la ligne : 9

Il semble que ce comportement soit commun à la plupart des pilotes de bases de données, à l'exception d'Oracle (qui ne prend pas en compte les deux extraits de code) et de Sqlite3 (qui autorise les deux).

Je ne pense pas que cette approche soit une bonne idée, mais j'aimerais comprendre comment c'est possible.

2voto

Dharman Points 1540

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X