Le PHP Big Bang provoqué par un isset sur l’attribut d’un objet

L’utilisation de PHP5, enfin l’utilisation de la programmation objet dans ce langage peut parfois s’avérer déstabilisant. Un développeur PHP ne doit jamais oublier que la notion objet est en réalité un patch appliqué au langage et qui n’était donc pas prévu dans les premières versions.

J’ai été récemment confronté à un problème qui m’a laissé relativement dubitatif. Il s’agissait d’élucider le mystère de l’aberrant résultat issu de mes lignes de code PHP, qui dans une certaine situation, me retournait un objet vide mais ayant tout de même une valeur.
Dit de cette manière, je sais que ce n’est pas très clair. Je vais donc, plutôt illustrer mon problème avec une synthèse de ces quelques lignes :

$objA = new ObjA() ;
$objA->attr = "42" ;
$var = $objA->attr ;
var_dump($objA->attr) ;               
var_dump(!empty($objA->attr)) ; 
var_dump(isset($objA->attr)) ; 
var_dump(isset($var)) ; 
 
// retourne
// string '42' (length=2)
// boolean false
// boolean false
// boolean true

Donc, voilà mon problème comment l’attribut attr de l’objet objA peut-il être vide, et en même temps retourner la valeur 42.

Ca ne parait pas logique, mais bon, après tout, ça ne serait pas la première réalité qui m’échapperait. Si le temps et l’espace est à l’origine une même chose, alors pourquoi le néant et le concret ne seraient-ils pas aussi confondus ?

Au lieu de chercher ma réponse à l’origne des temps (ou de l’espace je ne sais plus trop), essayons de remonter à la racine de mon code source.

La classe ObjA est la suivante :

class ObjA {
    private $attr;
 
    public function __get($att){
        return $this->$att;
    }
 
    public function __set($att,$val){
        $this->$att = $val;
        return $this->$att;
    }
}

La seule spécificité de cette classe est qu’elle possède des magic getters et setters qui permettent d’accéder à ses tous ses attributs comme s’ils étaient publics de la manière suivante :

$objA = new ObjA();
$objA->attr = "42" ; // appel de __set
echo $objA->attr ; // appel de __get

Si on change l’attribut de la classe ObjA en public et que l’on relance le test, on obtient le surprenant résultat suivant :

$objA = new ObjA() ;
$objA->attr = "42" ;
$var = $objA->attr ;
var_dump($objA->attr) ;               
var_dump(!empty($objA->attr)) ; 
var_dump(isset($objA->attr)) ; 
var_dump(isset($var)) ; 
 
// retourne
// string '42' (length=2)
// boolean true
// boolean true
// boolean true

L’attribut $objA->attr possède toujours la valeur «42» et ne semble cette fois ne plus être vide. Le PHP Big Bang aurait-il enfin eu lieu ? Le néant et le concret se seraient-ils séparés ?

Rien n’est encore sur, mais on peut affirmé que le problème survient lorsque l’on essaie d’accéder à un attribut à partir d’une classe dont nous n’avons pas la visibilité (sur un attribut private ou protected).

Essayons de décortiquer ce qu’il s’est passé :

isset($objA->attr) ;

$objA->attr est catché par le magic getter de ObjA qui est censé retourner la valeur de l’attribut $this->attr .

Ce qu’il fait car echo $objA->attr retourne bien la valeur.

En toute logique la fonction isset devrait analyser ce que nous lui fournissons en paramètre, soit la valeur retourné par le getter, mais il n’en est rien, pour une raison inconnue elle semble introspecter l’objet $objA.

Un petit coup d’œil dans la documentation de la fonction isset de php permet d’y voir un peu plus clair.

Note:

When using isset() on inaccessible object properties, the __isset overloading method will be called, if declared.

Pour contrer le Big Bang, il suffirait de surcharger le comportement de la magic method __isset d’un object lors de l’appel de la méthode isset sur son attribut.

En simple, la classe ObjA doit ressemblé à ça :

class ObjA {
    private $attr;
 
    public function __get($att){
        return $this->$att;
    }
 
    public function __set($att,$val){
        $this->$att = $val;
        return $this->$att;
    }
 
    public function __isset($key) {
        return isset($this->$key);
    }

Encore plus incroyable, après avoir tester, le Big Bang ne se produirait pas sur Windows. En tout cas pas avec la version 5.2.5 de PHP.

Articles similaires :

Mots-clefs : , , ,

Un commentaire sur “Le PHP Big Bang provoqué par un isset sur l’attribut d’un objet”

  1. Geoffray dit :

    En fait, la doc indique bien que la méthode magique __isset() est invoquée par isset() mais aussi par empty().

    http://www.php.net/manual/fr/language.oop5.overloading.php#language.oop5.overloading.members

    Je suis d’accord pour dire que c’est assez étrange puisque dans la logique de PHP ce sont 2 états bien distincts.

    Donc donc dans ton cas, la propriété a bien une valeur mais vu qu’elle est privée les fonctions isset() et empty() ne peuvent en connaître l’existence si la méthode magique __isset() ne les aide pas.
    Car en réalité, et c’est aussi quelque chose de déroutant, elles ne vont même pas tenter d’invoquer __get() !

Laisser une réponse