Propriétés en PHP

En C#, il y une fonctionnalité que j'apprécie particuliérement. C'est la possibilité de définir des propriétés :

class MyClass
{
    protected string _myProperty;
 
    public string MyProperty
    {
        get
        {
            return _myProperty.ToUpper();
        }
        set
        {
            _myProperty = value;
        }
    }
}

Bien sur c'est utile uniquement si on a un traitement à effectuer lors de l'affectation ou de la lecture (dans cet exemple je passe la chaine de caractère en majuscules) ou si on veut un accès en lecture seule (dans ce cas, on n'implémente pas la méthode set).

Une fois que la propriété a été définie, on peut y accéder de la façon suivante :

MyClass o = new MyClass();
o.MyProperty = "Ma valeur";
Console.WriteLine(o.MyProperty); // Affiche MA VALEUR

Je trouve ça bien plus lisible que l'emploi des getter/setter. En PHP, on devrait faire :

<?php
$o 
= new MyClass();
$o->setMyProperty("Ma valeur");
echo 
$o->getMyProperty();
?>

Heureusement, on dispose des méthodes magiques __get et __set qui nous permettent d'écrire une classe qui émule cette fonctionnalité :

<?php
class Wiip_Object_Exception extends Zend_Exception
{
}

class 
Wiip_Object 
{
    public function 
__get($name
    {
        
$method 'get' ucfirst($name);
        if (
method_exists($this$method)) {
            return 
$this->$method();
        } else {
            
$this->throwNoPropertyException($name);
        }
    }

    public function  
__isset($name
    {
        
$method 'get' ucfirst($name);
        if (
method_exists($this$method)) {
            return 
$this->$method() != NULL;
        } else {
            
$this->throwNoPropertyException($name);
        }
    }

    public function 
__set($name$value
    {
        
$method 'set' ucfirst($name);
        if (
method_exists($this$method)) {
            
$this->$method($value);
        } else {
            
$this->throwNoPropertyException($name);
        }
    }

    private function 
throwNoPropertyException($name)
    {
        throw new 
Wiip_Object_Exception('No property with name "' $name '"');
    }

    public function  
__unset($name
    {
        
$method 'set' ucfirst($name);
        if (
method_exists($this$method)) {
            
$this->$method(NULL);
        } else {
            
$this->throwNoPropertyException($name);
        }
    }
}
?>

J'ai implémenté également les méthodes __isset et __unset pour pouvoir utiliser les fonctions isset et unset.

Si maintenant on crée une nouvelle classe dérivée de Wiip_Object, on peut lui ajouter des propriétés qu'on pourra écrire ou lire sans passer par les méthodes get* et set* :

<?php
/**
 * @property string $firstName
 * @property string $lastName
 */
class Person extends Wiip_Object
{
    protected 
$_firstName;

    public function 
getFirstName()
    {
        return 
$this->_firstName;
    }

    public function 
setFirstName($value)
    {
        
$this->_firstName $value;
    }

    public function 
getLastName()
    {
        return 
'Dupont';
    }
}

$p = new Person();
$p->firstName 'Pierre';
echo 
$p->lastName;
?>

Les annotations @property permettent à NetBeans de gérer l'autocomplétion.

On pourrait également utiliser un switch, mais sur un objet avec beaucoup de propriétés, on risquerait de se retrouver avec une méthode de plusieurs dizaines de lignes qui ne serait pas très lisible. En déportant chaque propriété dans un couple de méthode get/set, on rend l'implémentation de la classe bien plus claire. Cela permet également dans le cas d'une boucle par exemple où l'on souhaiterai une performance maximale, d'appeler directement les méthodes get/set au lieu de passer par la propriété (puisque l'appel de __set ou de __get et de method_exists entraine une surcharge et donc certainement une moindre performance).

Commentaires

"si on veut un accès en lecture seule (dans ce cas, on n'implémente pas la méthode get)."
Je pense que tu voulais dire "set" et non "get", .puisque le "get" permet la lecture.

Oui effectivement il y a une coquille. C'est corrigé, merci de l'avoir signalé.

Je n'étais pas certain de mon fait lors de mon premier commentaire, mais je viens de voir qu'il y a bien une RFC pour que PHP supporte nativement ce mécanisme, active, en plus.

http://wiki.php.net/rfc/propertygetsetsyntax

Ajouter un commentaire