Tester correctement variables et valeurs en php

Certains avantages du php, comme sa gestion très relâchée des types de variables, ont aussi quelques inconvénients. Mais une fois que l’on sait éviter et détecter ces problèmes, il est possible d’utiliser à son profit certaines particularités du langage pour écrire des scripts très puissants…

Comment prévenir les bogues dues aux problèmes de typage ?

En php, chaque variable peut changer de type en cours de route. Ce qui a pour conséquence de rendre le résultat de certains tests d’égalité imprévisibles

Bogues de comparaison entre types différents
Voici un bout de code courant qui peut poser problème

    if ($var==0){}

Si $var contient la chaine ‘123’ au lieu du nombre 0, cette expression est considérée comme toujours vraie…

Solution : il faut donc penser à utiliser l’opérateur d’égalité === qui vérifie à la fois que les valeurs sont égales et qu’on ne compare pas des carottes et des navets

Bogues de typage des valeurs retournées par une fonction

Il arrive fréquemment que des fonctions renvoient une valeur numérique ou une chaine lorsque tout va bien, et le booléen « false » en cas d’échec : le résultat peut être ambigu…

Exemple :

    if (fonctionquelconquerenvoyantunechaine($chaine)=="false"){}

Ce code fonctionne à peu près bien en php3. Sauf quand la chaine renvoyée se trouve être « false » (bogue subtile).

En php4, si on écrit

    if (fonctionquelconquerenvoyantunechaine($chaine)== false){}

Ce code peut toujours renvoyer false !

A l’inverse, le code :

    if (fonctionquelconquerenvoyantunechaine($chaine)=== true){}

ne fonctionnera pas correctement non plus, puisque le type peut être tantôt booléen, tantôt une chaine !

Un exemple plus spectaculaire

    $var = true ;
    if( $var == "true" ) echo("C'est vrai !"); // la chaine true est vraie car non vide donc le test d'égalité renvoie vrai !
    if( $var == "false" ) echo("C'est faux !") // même chose, sauf que là, l'affichage devient plus étonnant
    if( $var == "caribou" ) echo("C'est un cariboo !"); // cela marche avec n'importe qu'elle chaîne
    if( $var == 4 ) echo("C'est le nombre 4!") ; // cela marche aussi avec un type numérique. 4 est vrai !

donne à l’écran

    C'est vrai !
    C'est faux !
    C'est un caribou !
    C'est le nombre 4 !
Solution : éviter les comparaisons inutiles entre booléens et valeurs true et false. Préférer if(!fonction($param)){$chaine=fonction($param);} à if(fonction($param)== true){$chaine=fonction($param);}.

Attention : de plus, plusieurs fonctions, qui renvoyaient une chaine « false » en cas de problème en php3, envoient un booléen false en php4, ce qui empêche certains scripts mis au point en php3 de tourner en php4

Comportement atypique des valeurs booléennes

Par ailleurs, pour des raisons obscures (au moins pour moi), le booleen « false/true » est stocké sous la forme d’un entier égal à 1 si la valeur est true, tandis que (en php3) la valeur false est stockée parfois sous la forme de l’entier 0, la valeur « null », ou la chaine  ». Dans les versions récentes de php, ces ambiguïtés disparaissent.

Dans la pratique, cela n’a pas d’incidence notable dans les calculs, compte tenu de la conversion automatique des types en php. Ce qui fait que dans la pratique, si $exp1 est true et $exp2 est false, les calculs booléens suivants donnent les résultats attendus :
$exp1 * $exp2 est évaluée à 0 (false)
$exp1 + $exp2 est évaluée à 1 (true)

Mais il est intéressant de connaître cette particularité, car dans certains cas, le transtypage donne des résultats qui peuvent sembler étonnants, où les fonctions de conversion de type renvoyer des messages difficiles à comprendre sinon.

Par ailleurs, pour les transtypages, il est interessant de savoir comment s’évaluent différentes valeurs si elles sont converties en type booléen :

false -> false (heureusement !)
0 -> false
null -> false
0.0 -> false (attention : sources de bogues subtiles)
«  » (chaîne vide) -> false
« 0 » -> false (attention : source de bogues subtiles)
tableau vide -> false
objet vide -> false
toutes les autres valeurs -> true

Donc 2 s’évalue comme true, et -1 aussi !

Solution générale : ne jamais utiliser d’égalité sans savoir à l’avance le type des variables ou des expressions comparées => utiliser les fonctions de test de typage avant

Utilisation de la valeur NULL

NULL est apparue avec le PHP4

NULL est une constante particulière. Affectée à une variable elle permet de « vider » celle-ci de sa (ses) valeurs. Elle reste déclarée, mais ne contient rien. Elle n’a plus son ancien type non plus (le type devient NULL).

Attention là aussi aux comparaisons entre valeurs dans le cas où une variable peut contenir la (non)valeur NULL

NULL == NULL est évalué true
NULL == FALSE est évalué true
NULL == TRUE est évalué false

Cette valeur NULL n’a rien à voir avec le NULL du langage SQL

donc attention aux intuitions.

Par ailleurs, il est difficile de distinguer les variables de type NULL et les variables non déclarées ou supprimées avec unset()

Il faut utiliser le bon test

    //$var est NULL
    ($var) renvoie TRUE
    isset($var) renvoie FALSE
    empty($var) renvoie TRUE
    is_null($var) renvoie TRUE

    //$inexistante est une variable inexistante ou supprimée par unset()
    ($var) renvoie FALSE
    isset($inexistante) renvoie FALSE
    empty($novar) renvoie TRUE
    is_null($novar) renvoie "Undefined variable error"

nouveau comportement de unset()

La fonction unset($var) permet de supprimer la variable de l’environnement. La variable $var n’est plus déclarée après cela. C’est utile par exemple pour supprimer d’un seul coup toutes les valeurs d’un tablo, avant de le redéfinir avec des indices différents…

unset() était une fonction en php3. Ce n’est plus le cas en php4, c’est devenu une structure du langage. Donc le test suivant ne marche pas en php4

    if (unset($var)){}
Remarque : unset() ne fonctionne pas correctement au sein de fonctions avec des variables statiques. Les autres cases mémoires ne sont pas supprimées…

Philippe YONNET


Vous avez des questions ? Des réactions ? Vous avez remarqué une erreur ? Commentez l’article sur le forum Webmaster-hub