PROJET AUTOBLOG


Idleman

source: Idleman

⇐ retour index

Mise à jour

Mise à jour de la base de données, veuillez patienter...

Snippet #24 ~ PHP: Allégez vos classes avec des get/set automatiques

mardi 19 novembre 2013 à 17:17

Lorsque l’on programme en orienté objet sous php, il est courant d’utiliser des objets comme simple “conteneur” d’information, ce qu’en java on appel les POJO (Plain Old Java Object) mais que je me refuse à appeler POPO en PHP pour des raisons plus qu’évidentes :D !

Bref, ces objets ont donc une tripotée d’attributs (souvent privés) auxquels nous avons besoin d’accéder
via des méthodes comme getAttribut1(), setAttribut1(), getAttribut2…

Créer ces méthodes peut être très contraignants et alourdir le code de la classe pour pas grand chose.

Prenons pour exemple une classe utilisateur avec une tripotée d’attribut :

Class User{

private $name,$firstname,$town,$phone,$sex,$hobbies,$avatar,$login,$password,$rank,$state,$fax,$cellphone,$mail;

}

 

Afin d’accéder à ces attributs privés, il vas falloir créer les méthodes get et set pour chacune d’elles soit 2 méthodes par attribut
d’environ 3 lignes pour 14 attributs vous vous retrouvez déjà avec 84 lignes dans la vue simplement pour accéder à vos attributs.

Aussi voiçi un petit snippet permettant de générer dynamiquement ces méthodes (souvent appelées “accesseurs” ou “getter et setter“)
avec une seule méthode de 3 lignes utilisant la fonction magique de php : __call()

Class User{

private $name,$firstname,$town,$phone,$sex,$hobbies,$avatar,$login,$password,$rank,$state,$fax,$cellphone,$mail;

    function __call($m,$p) {
            $v = strtolower(substr($m,3));
            if (!strncasecmp($m,'get',3))return $this->$v;
            if (!strncasecmp($m,'set',3)) $this->$v = $p[0];
    }
}

Vous pourrez alors appeler et définir vos attributs comme si vous aviez tapé les 84 lignes d’accesseurs :

$user = new User();
$user->setName('CARRUESCO'); // Définis  $name = CARRUESCO
echo $user->getName(); // Affiche $name donc affiche CARRUESCO

L’avantage étant que ce snippet ne s’active que si la méthode n’existe pas déjà, aussi
si vous souhaitez créer une méthode getName dans votre classe qui effectue des opérations sur le nom
avant de le retourner :

public function getName(){
    return strtoupper($this->name);
}

Cette fonction sera prise en priorité par la classe.

Le revers de la médaille

Et oui, le petit soucis avec ces snippet, c’est que vos attributs ne sont plus vraiment privés,
puisqu’il sont automatiquement accessibles depuis le snippet, dans certains cas
ça peut être chiant, aussi je vous conseille d’ajouter une petite clause pour définir
les attributs qui ne pourront pas être appelés de cette façon:

Class User{

private $name,$firstname,$town,$phone,$sex,$hobbies,$avatar,$login,$password,$rank,$state,$fax,$cellphone,$mail;
private $private = array('private','rank','avatar');

    function __call($m,$p) {
            $v = strtolower(substr($m,3));
            if (!strncasecmp($m,'get',3) && !in_array($v,$this->private))return $this->$v;
            if (!strncasecmp($m,'set',3) && !in_array($v,$this->private)) $this->$v = $p[0];
    }
}

Dans ce code, tous les attributs seront accessibles en get/set, exceptés $private,$rank et $avatar

Si vous aussi vous avez des ptites techniques pour réduire la taille des classes, lâchez vous dans les commentaires :)

 
Edit: Je profite des débats lancés dans les commentaires pour donner mon point de vue :) (c’est qu’ils
ont l’esprit critiques mes idlenautes :p)

Pourquoi ne pas utiliser les fonction __get et __set ?

Pourquoi utiliser deux fonctions d’accession de 4 lignes chacune quand une seule fonction __call suffit ? :)

Mais sans même tenir compte de la taille, __get et __set ont, selon mon opinion, un gros inconvénients : il n’est pas possible
de définir la forme d’appel. En effet __get et __set appelleront les attributs uniquement sous la forme

$object->attribut = 'toto'

 

Quand la fonction __call permettra (entre autres) de créer les appels sous forme de méthode :

$object->setAttribut('toto');

 

Outre le fait que certains développeurs (dont je fait partie) puissent trouver ça plus harmonieux, c’est aussi plus utile,
l’appel au format méthode permet :

De dissocier un appel d’attribut potentiellement traité d’un appel d’attribut “brut”
De ne pas croire que l’attribut est public (la forme $object->attribut = ‘toto’ pouvant porter à confusion).
Et plus important : de changer l’accessibilité ou le mode de traitement de l’attribut du jour au lendemain sans voir à faire de modifications sur le reste du code

Pourquoi ne pas tous simplement placer tous les attributs en public ?

Cela revient à demander “quel est l’intérêt de mettre les attributs en privés ?”, effectivement il est tout à fait possible de mettre
les attributs en public, cela peut même paraître plus judicieux de prime abord, mais peut causer de gros problèmes lors de la maintenance et
de l’évolutivité du code.

Prenons comme exemple notre classe utilisateur, à l’origine l’attribut
$ville est un string, on effectue aucun traitement dessus, donc on décide de le mettre en public pour y accéder de la manière suivante:

echo $object->ville; //Affiche 'Gradignan';

 

Cela fonctionne parfaitement sans avoir besoin d’accesseur jusqu’au jour ou l’on décide que $ville sera plutot un id faisant référence à une table de ville.

echo $object->ville; //Affiche 8;

 

On souhaite donc effectuer un traitement sur la donnée $ville avant affichage pour obtenir son nom et non son id ce qui permettrait de ne pas avoir à modifier
tous les appels sous la forme $object->ville de l’application, et zut ! Ce n’est plus possible ! Car l’attribut est appelé
directement, aucun traitement n’est possible. On est donc obligé de modifier toutes les formes d’appel de l’application pour passer par une méthode, ce qui revient à taper tout les
accesseurs, ou à utiliser des fonctions magiques comme __call, __get et __set.

En résumé, l’utilisation d’accesseurs en temps que “wrapper” sur des attributs privés devrait à mon sens TOUJOURS être utilisée même dans
le cas ou ce wrapper ne fait à l’origine du projet que retourner l’attribut ou le setter sans traitement préalable, wrapper les attributs
permet une meilleure maintenance évolutive sur ces derniers car il est possible d’effectuer un traitement sur la donnée (ce qui n’est pas
le cas sur un appel d’attribut public) sans avoir à modifier l’ensemble du script, d’ou l’utilité des accesseurs en lieu et place des accès direct d’attributs.