lundi 14 juin 2010
Voici un petit problème de programmation que j'ai rencontré en tentant de créer un énoncé SQL qui remplaçait des placeholders par les bonnes valeurs. Et c'est vraiment un pur hasard d'avoir trouvé ce bogue dans l'application car je faisais des tests sur un enregistrement spécifique qui m'a permis de déceler le problème.
J'ai simplifié l'exemple pour démontrer plus facilement ce qui se passe et j'ai assumé que j'avais vérifié et "escapé" les valeurs selon leur type pour m'en tenir uniquement à ce dont je voulais voir.
Selon vous, qu'est ce qui ne va pas dans le code PHP suivant :
$search = '/:param1/';À l'impression, l'énoncé SQL devient soudainement :
$value = "'The price is $1.50 USD'";
$sql = 'INSERT INTO table (field) VALUES (:param1)';
echo preg_replace($search, $value, $sql);
INSERT INTO table (field) VALUES ('The price is .50 USD')Le symbole $ est perdu de même que l'unité 1 sur le prix.
Et celle-ci fonctionne correctement :
$value = "'The price is 1.50$ CAD'";Pourquoi ? Je suppose que parce que j'utilise une expression régulière, la fonction preg_replace() peut s'attendre à avoir des back references sous la forme $1, $2, $3, etc., et il confond le symbole dans la valeur avec une référence arrière utilisée pour effectuer son remplacement (par une valeur vide).
Si j'appelle preg_quote() sur la variable $replace (avant d'appeler preg_replace()), j'obtiens cet énoncé :
INSERT INTO table (field) VALUES ('The price is $1\.50 USD')Une fois exécuté, le backslash devant le point n'est pas inséré dans la table. Mais Postgres me retourne un petit warning : Nonstandard use of escape in a string literal. Hint: Use the escape string syntax for escapes, e.g., E'\r\n.
Donc il est heureux si je l'exécute ainsi :
INSERT INTO table (field) VALUES ('The price is $1\.50 USD')Sinon mettre un double backslash (\\) devant les symboles $ fait aussi bien l'affaire et me permet de conserver le prix en entier dans le texte sans avoir à utiliser E' :
$value = str_replace('$', '\\$', $value);C'est ça qui arrive quand on joue dans le vieux code d'un autre, on trouve des belles petites perles qui nous font creuser nos méninges (et parfois sacrer un peu!).
Je suis confus quant à savoir l'interêt d'utiliser preg_replace dans ce type de code?
preg_replace est plus souvent utilisé afin de confronté des problèmes de performance versus son cousin str_replace(http://www.php-scripts.com/php_diary/011303.php3) ou pour pallier à des problème de complexité. Par contre, dans l'exemple ci-haut (qui semble être un dérivé du problème d'origine), ajouter un str_replace avant le preg_replace transforme le tout en un code largement moins efficaces qu'à l'origine (surtout si l'on utilise le tout de manière successive, comme sur un array).
Je suppose que tout le sens survient dans le code d'origine de l'erreur :)
Excellent article au passage :)
Ps: De plus, il serait nice d'escaper aussi les apostrophes possibles survanant dans la value :-P