Utilisation de l’opérateur “or”

Recemment je me suis heurté à quelques soucis avec l’opérateur “or”, notamment lorsqu’il est utilisé en dehors d’une condition. Bien que l’ordre de précédence des opérateurs soit défini dans la documentation de PHP (et commun à beaucoup d’autres langages), il m’arrive encore de me faire surprendre. Aussi, voici une petite clarification.

Un exemple très répandu de l’utilisation de “or” en dehors d’une condition est lorsqu’il est utilisé conjointement avec die() celui-ci :

doSomething() or die();

Si doSomething() retourne “vrai”, alors l’évaluation s’arrette, parceque l’opération “or” retourne toujours “vrai” si au moins un des éléments est “vrai”, donc le moteur optimise en passant les tests inutiles.

Si doSomething() retourne faux, alors l’évaluation continue, et die() est executé (adieu petit script).

Dans une affectation

Lorsque “or” est utilisé avec l’opérateur d’affectation (=), les choses peuvent se compliquer si on ne fait pas attention.

function getValue() { return 42; }

$a = getValue() or die();
var_dump($a); // int 42

A cause de l’ordre de priorité des opérateurs, l’affectation sera évaluée d’abord. Donc die() ne sera executé que si l’opérateur d’affectation retourne une valeur assimilée à “faux”. Comme l’affectation retourne la valeur affectée, die() sera executée si getValue() retourne 0, false, null ou une chaine vide. C’est équivalent à :

($a = getValue()) or die()

A priori c’est le comportement souhaité. Mais que ce passe-t-il si on veut obtenir réellement le résultat de l’opération “or” ?

$foo = 42;
$a = is_string($foo) or is_numeric($foo);
var_dump($a); // bool false

Suprise ! Le test retourne “faux”. En fait, comme vu ci-avant, c’est équivalent à :

($a = is_string($foo)) or is_numeric($foo);

L’affectation est effectuée d’abord, elle retourne faux, puis is_numeric est executé. Mais il ne fait plus partie de l’opération d’affectation. Pour que ça fonctionne correctement, il faut écrire :

$a = (is_string($foo) or is_numeric($foo));

Dans une fonction

Maintenant que ce passe-t-il si on utilise cette syntaxe dans une fonction ?

function foo() {
    return getValue() or die();
}
$a = foo();
var_dump($a); // bool true

Surprise ! La fonction ne retourne pas le résultat de getValue(), mais un booléen, parceque maintenant que l’opérateur d’affectation a disparu, il n’y a plus de question de priorité. L’opération “or” est executée, et son résultat, un booléen, est retourné. C’est équivalent à :

return (getValue() or die());

Conclusion

Au final tous ces comportements sont cohérents et plus ou moins identiques aux autres langages, mais, quand on n’y fait pas attention, une parenthèse oubliée ou mal placée peut tout casser et ce genre de bug n’est pas toujours facile à retrouver. Moralité, comme dirait l’autre : Utilisez les parenthèses pour augmenter la lisibilité du code.