Des attributs en lecture seule en PHP

S’il y a bien une chose que je trouve limitante dans le modèle objet de PHP (et certainement d’autres langages) c’est bien le fait de ne pas pouvoir déclarer un attribut en lecture seule. On peut définir l’accès à un attribut, via public, protected et private, mais un accès public va autoriser un accès en lecture qu’en écriture. Soit on autorise les deux, soit on n’autorise rien du tout.

Comment dès lors autoriser l’accès en lecture tout en interdisant l’écriture ? PHP n’offre pas de mécanisme nous permettant de le faire simplement. Il va donc falloir ruser, mais c’est possible.

La solution tient dans la surcharge et donc dans la visibilité des attributs. On croit généralement que PHP va exécuter la méthode magique __get() lorsqu’un attribut est indéfini, mais en fait elle est invoquée dès qu’un attribut n’est pas visible. La méthode magique est appelée lorsque l’attribut n’existe pas, mais c’est une conséquence, pas un fait. À noter que ceci est tout aussi valable pour __set() et certainement les autres méthodes magiques.

Donc, si je déclare un attribut comme étant privé (ou protégé) et que j’essaye d’y accéder directement depuis l’extérieur de l’objet, PHP va exécuter la méthode magique __get(), ce qui va nous permettre de pouvoir réagir en fonction.

Exemple :

class Object
{
  protected $variable = "toto";
  
  function __get($attribute)
  {
    return $this->$attribute;
  }
}

$obj = new Object();
echo $obj->variable;
// echoes "toto"

$obj->variable = 'tata';
// throws an error: 'variable' is a protected attribute.

Cet exemple est basique mais il montre que cela marche. On peut bien entendu faire beaucoup mieux et créer un objet générique qui permettrait de facilement déclarer des variables en lecture-seule, avec une limitation cependant : cela se limite aux attributs protégés et aux attributs privés de la classe déclarant __get().

Voici par exemple une classe générique qui permet de déclarer certains attributs comme accessibles en lecture ou en écriture seulement. La limitation étant que les attributs doivent être protégés (et pas privés) et qu’il n’est pas possible pour plusieurs classes héritants l’une de l’autre de définir chacune ses propres attributs (à moins de trouver une autre astuce ?).

class Object
{
  protected $attr_reader = array();
  protected $attr_writer = array();
  
  function __get($attribute)
  {
    if (in_array($attribute, $this->attr_reader)) {
      return $this->$attribute;
    }
    trigger_error("Can't read undefined or protected attribute '$attribute'.", E_USER_WARNING);
  }
  
  function __set($attribute, $value)
  {
    if (in_array($attribute, $this->attr_writer)) {
      return $this->$attribute = $value;
    }
    trigger_error("Can't write undefined or protected attribute '$attribute'.", E_USER_WARNING);
  }
}

Laisser un commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion / Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion / Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion / Changer )

Photo Google+

Vous commentez à l'aide de votre compte Google+. Déconnexion / Changer )

Connexion à %s


%d blogueurs aiment cette page :