Quelques étrangetés de PHP

Et oui, c'est un cas avéré, PHP est bien mal conçu sur certains points. Voici quelques uns des effets indésirables qui peuvent engendrer de jolis bugs si l'on y fait pas attention. Si vous avez d'autres jolis exemples de ce genre, n'hésitez pas à me les faire parvenir.

L'opérateur == ne test pas l'égalité

Contrairement à ce que l'on peut penser, la relation testée par l'opérateur == n'est pas une relation d'égalité. En effet, la relation d'égalité se doit d'être transitive, c'est à dire que si a = b et b = c alors a = c. Cet exemple nous prouve que ce n'est pas toujours le cas en PHP :

<?php
var_dump('0' == 0);
var_dump(0 == '');
var_dump('' == '0');
?>

Le résultat est sans appel :

bool(true)
bool(true)
bool(false)

Ce problème est causé par le transtypage. En effet, dans les deux premiers cas la chaîne de caractères est convertie en entier avant de réaliser le test d'égalité, ainsi '0' et la chaîne vide '' sont transformés en l'entier 0, donc la comparaison est évaluée à vrai. Pour ce qui est de la dernière comparaison, la comparaison se fait entre deux chaînes de caractères et donc aucune conversion n'est effectuée, d'où l'évaluation à faux vu qu'elles sont différentes.

Les opérateurs or et || sont-ils bien les mêmes ?

Cet exemple est beaucoup moins connu et m'a fait exploser de rire quand je m'en suis aperçu.

<?php
var_dump(true or true ? false : true);
var_dump(true || true ? false : true);
?>

Le résultat est vraiment délirant :

bool(true)
bool(false)

Pourtant, d'après la documentation de PHP, or et || sont bien tous les deux l'opérateur du ou logique. Le secret de ce résultat vient du fait que ces deux opérateurs n'ont pas la même priorité, l'un étant prioritaire sur l'opérateur ternaire et l'autre non, entraînant un changement de l'ordre d'évaluation.

Conversions aléatoires

Reprenons un exemple avec l'opérateur == et un peu de transtypage :

<?php
var_dump((int)'42');
var_dump((int)'0x2a');
var_dump('42' == '0x2a');
?>

Le résultat est particulièrement choquant :

int(42)
int(0)
bool(true)

La raison de ce résultat est que PHP ne convertit pas de la même manière les chaînes de caractères. Dans le cas d'un cast explicite, il agit exactement de la même manière que lors de l'appel de la fonction intval() et considère donc que la chaîne est en base 10. Le caractère x étant en dehors de la base 10, la conversion s'arrête lorsqu'il le rencontre, évaluant ainsi '0x42' à 0.
Dans le cas de l'utilisation de l'opérateur ==, PHP détecte que les deux chaînes de caractères représentent des nombres et les convertis donc en entiers avant de les comparer. La différence avec le cast explicite est ici qu'il interprète bien la chaîne '0x2a' comme étant de l'hexadécimal et la convertis donc en l'entier 42, tout comme la chaîne '42'.

Tags