Dans notre précédent article, nous avions vu plusieurs astuces pour simplifier nos conditions, en utilisant quelques règles de typage. Toutefois, il ne faut pas croire que tout soit aussi systématique. Il existe des pièges qu’il s’agit d’éviter.
L’occasion idéale pour un nouvel article de notre série Les petites choses marrantes en PHP, consacrées aux petites choses marrantes en PHP, avec quelques exemples de petites choses marrantes en matière de test sur des string.
Mais attention, que les choses soient bien claires: comme tous les articles de cette série, il ne s’agit pas de troll! Juste de faire gaffe…
Comment tester une string vide?
Vous voulez écrire une condition qui ne laisse passer que les string qui ne sont pas vides. Simple, me diriez-vous? Pas si sûr…
Test sur la variable
Commençons avec une écriture dépouillée: on teste juste une variable dans une condition.
1 2 3 | if($str){ ... } |
Ma string vaut ""
Que va-t-il se passer? Notre variable va être castée dynamiquement en booléen. Une string vide castée en booléen va effectivement retourner false. Le test est correct.
1 2 3 4 5 | $str = ''; if($str){ //ok, je n'entre pas dans la condition avec une string vide } |
Ma string vaut "0"
Nous sommes d’accord qu’une string équivalant à "0" ne peut pas être considérée comme vide. Toutefois, en PHP, caster une string "0" en booléen va retourner false. Eh oui… (même chose avec la fonction empty). Autrement dit, on n’entre pas dans la condition alors que la string n’est pas vide. Le test n’est pas correct.
1 2 3 4 5 | $str = '0'; if($str){ //oups, je n'entre pas dans la condition alors que ma string n'est pas vide } |
Marrant, non?
Comparaison stricte
On améliore notre test en précisant qu’on veut exclure spécifiquement les string vides.
1 2 3 | if($str !== ''){ ... } |
Ma string vaut "0"
Du coup, le "0" n’est plus considéré comme une string vide. Cette fois-ci, le test est correct.
1 2 3 4 5 | $str = '0'; if($str !== ''){ //ok, j'entre dans la condition } |
Ma string vaut null
Il existe malgré tout encore un piège. Tout va dépendre de la question suivante: voulez-vous accepter d’autres scalaires équivalents à false qui soient d’un autre type que string (false, null, 0, "")? En effet, si on teste la variable avec une comparaison stricte, on autorise d’autres types à rentrer dans la condition. Par exemple, null n’est pas strictement égale à "". Donc, on entre dans la condition. Le test n’est pas correct.
1 2 3 4 5 | $str = null; if($str !== ''){ //oups, j'entre dans la condition alors que ma valeur vaut null } |
Marrant, non?
Comparaison laxiste
Seule la comparaison laxiste permet ici d’accepter les string "0", tout en refusant les string vides ainsi que les scalaires ayant une valeur équivalente à false.
1 2 3 | if($str != ''){ ... } |
Ma string vaut null
Du coup, null ne pose plus de problème. Le test redevient correct.
1 2 3 4 5 | $str = null; if($str != ''){ //ok, je n'entre pas dans la condition } |
Ma string vaut array
Il reste malgré tout encore un piège. Si notre variable vaut un array (ou potentiellement un objet implémentant __toString), la conversion se fait du tableau vers la string. Or, caster un tableau en string retourne toujours la string « Array » (avec heureusement une notice depuis PHP 5.4, mais attention toutefois que cette notice ne se déclenche évidemment pas lors d’une comparaison laxiste). Dommage, on aurait préféré une string vide. Le test n’est donc pas correct.
1 2 3 4 5 | $str = array(); if($str != ''){ //oups,j'entre dans la condition } |
Marrant, non?
Comparaison stricte et test sur le type
La seule façon de s’assurer qu’aucun cas ne pose problème réside dans le test du type de la variable suivi par une comparaison stricte. Le test devient enfin correct.
1 2 3 | if(is_string($str) && $str !== ''){ ... } |
Au final, la solution n’est pas très intuitive et l’écriture de la condition n’est pas très claire. On se retrouve à l’exacte opposé de ce qui était préconisé dans l’article sur la simplification des conditions. Bien sûr, il s’agit d’adapter son test à chaque cas. Tous les contextes d’exécution n’ont pas forcément besoin d’autant de mesures de sécurité.
Comment tester une string non-vide?
Deuxième condition: vous voulez vous assurer d’avoir une string équivalente à "abc". L’enfance de l’art, me diriez-vous? Pas si sûr…
Il existe en effet un piège (juste un seul cette fois) : l’utilisation d’une comparaison laxiste.
1 2 3 | if($str == 'abc'){ ... } |
En effet, si notre variable vaut true, le cast se fera vers le booléen. Une string non vide équivalant à true, vous entrez dans la condition.
1 2 3 4 5 | $str = true; if($str == 'abc'){ //oups, j'entre dans la condition } |
Marrant, non?
Conclusion
Le principal problème réside dans l’inconnue du typage de la valeur testée. La précédence des types ainsi que certaines conversions particulières peuvent créer des effets non désirés. Les conversions entre types ne sont pas toujours évidentes. La documentation de PHP propose notamment un tableau de comparaison des types qu’il est intéressant de relire régulièrement. Il convient donc de rester toujours prudent si le code n’est pas correctement sécurisé.
Ah le bon vieux typage dynamique. Simple en apparence ^^
Ca me rappelle un code que j’ai débuggé cette semaine qui se comportait mal, car un string vide c’était glissé a la place d’un tableau. Et il faut savoir qu’un count() sur une string vide … Ca ne vaut pas 0 !
php > var_dump(count( »));
int(1)