14- L'introspection et la réflexion

L'introspection et la réflexion sont une capacité du langage à obtenir des informations sur les classes, les méthodes etc... pendant l'exécution du programme.

Par exemple, si un objet a été instancié et que vous voulez savoir de quelle classe il s'agit, et quels sont ses parents pendant l'exécution, PHP vous permet d'obtenir ces données à l'intérieur et à l'extérieur de la classe. Ceci est très puissant pour connaître les types des objets et obtenir des informations sur ces-derniers alors que l'application est en cours d'exécution.

Depuis PHP, et le typage scalaire, nous pouvons préciser les typs des paramètres et des retours et il est un peu moins nécessaire de faire de l'introspection. Néanmoins, pour connaître les types des valeurs d'une collection, par exemple, cela est encore très utile.

PHP nous fournit une API de reflexion très utile (Reflexion), mais il est souvent possible de faire de l'introspection avec certaines fonctions simples et natives.

  • Il est possible de connaître la classe d'un objet en utilisant get_class($objet). Si la classe fait partie d'un espace de nom, get_class() nous reverra \espace\de\nom\Class. Pour retourner simplement le nom de la classe sans l'espace de nom, il faut utiliser (new ReflectionClass($objet))->getShortName();
  • il est possible de tester la classe d'un objet avec l'opérateur de comparaison instanceof, par exemple `if($voiture instanceof Voiture)
  • Pour connaitre les attributs d'un objet, il est possible de le transformer en array avec get_object_vars($object).
  • Il est tout à fait possible d'utiliser ces méthodes dans la classe avec $this, par exemple pour redéfinir jsonSerialize() qui redéfinira json_encode() sur un objet, on peut utiliser :

Supposons que nous voulons sérialiser des données en JSON. Pour faire cela, nous utilisons la méthode native de PHP json_encode(). Néanmoins, d'une part, notre objet a des attributs privés, et de plus, nous ne voulons pas que json_encode()affiche toutes les propriétés de l'objet mais nous voulons en cacher certaines. Nous allons donc trier les données à afficher dans la classe et modifier l'output de json_encode()

// dans la classe
class Voiture implements JsonSerializable {
    private $nom;
    private $marque;

    public function __construct(string $nom, string $marque){
        $this->nom = $nom;
        $this->marque = $marque;
        var_dump(get_object_vars($this));
    }

    public function jsonSerialize(){
        $ret = get_object_vars($this);
        unset($ret["marque"]);
        return $ret;
    }
}

// instanciation
$voiture = new Voiture('Twingo', 'Renault'); // on peut accéder aux données privées puisqu'on est dans la classe
//array(2) {
//["nom"]=>
//  string(6) "Twingo"
//["marque"]=>
//  string(7) "Renault"
//}
var_dump(get_object_vars($voiture)); // retourne un tableau vide puisqu'on est hors de la classe et qu'on ne peut pas voir les données privées
var_dump(json_encode($voiture)); // retourne string(16) "{"nom":"Twingo"}"

Nous observons ici que nous pouvons faire de l'introspection sur des données privées à l'intérieur mais pas à l'extérieur de la classe. L'utilisation de l'introspection nous permet ici de trier les données que nous voulons encoder.

  • On peut interroger un objet sur son héritage avec get_parent_class($objet)et tester le nom de sa classe parente avec is_subclass_of('NomDeClasse').
  • On peut savoir si une classe a été définie avec class_exists('NomDeClasse') et une interface avec interface_exists("Interface"). Pour une liste exhaustive de toutes les classes, il faut utiliser get_declared_classes().
  • On peut connaître les méthodes d'une classe avec get_class_methods($objet);.
  • Pour savoir si une classe possède une certaine méthode, on peut utiliser method_exists($objet, "nomDeMethode")

Les possibilités sont extrêmement variées, et cela est très utile pour créer des ORM par exemple ou débugger le code. Pour connaître toutes les possibilités qu'offre la classe ReflectionClass :

  • http://php.net/manual/en/book.reflection.php