Simuler des classes Friend en PHP
PHP, malgré la sortie de sa version 5.3 cet été, a toujours quelques lacunes. Parmi l'une d'elle, les classes Friend seraient bien utile.
Visiblement, ce n'est pas au programme, car comme il est élégamment expliqué ici, "long ago, we decided against 'friend' declarations".
Le principe de classes Friend consiste à permettre à une classe d'accéder aux attributs et fonctions privées d'une autre. Ceci est par exemple utile quand on veut implémenter une classe Data Mapper qui fait le lien entre une classe du modèle métier et le modèle de données. Pour sauvegarder un objet en base de données (ou le charger), elle a besoin de définir ses attributs, même ceux privés.
Une manière de contourner cette limitation est peu élégante mais relativement efficace : utiliser les méthodes magiques __get, __set et __call de PHP.
Ainsi, si B essaye d'accéder à un attribut privé de A, la méthode A::__get() sera appelée avec comme paramètre le nom de l'attribut demandé. Il est alors nécessaire de savoir qui demande l'accès à cet attribut : est-ce une classe "Friend" ou pas.
Pour cela, on utilise debug_backtrace() (beurk, oui), qui nous permet de connaitre la classe à l'origine de l'appel. Une petite vérification sur le nom de la classe et on renvoie la valeur de l'attribut demandé.
Voici un exemple sur __get et __set :
public function __get($attribut)
{
// Accès aux variables privées autorisé pour les Data Mapper
// Note : une variable privée commence par '_'
if ($attribut[0] == '_') {
$trace = debug_backtrace();
if (isset($trace[1]['class'])) {
$classname = $trace[1]['class'];
if (strpos($classname, 'Mapper_')) {
return $this->$attribut;
}
}
}
// Accès à un attribut non autorisé
throw new Exception('Accès à un attribut non autorisé ou inexistant : '
. get_class() . '::' . $attribut);
}
public function __set($attribut, $valeur)
{
// Accès aux variables privées autorisé pour les Data Mapper
// Note : une variable privée commence par '_'
if ($attribut[0] == '_') {
$trace = debug_backtrace();
if (isset($trace[1]['class'])) {
$classname = $trace[1]['class'];
if (strpos($classname, 'Mapper_')) {
return $this->$attribut = $valeur;
}
}
}
// Accès à un attribut non autorisé
throw new Exception('Modification d\un attribut non autorisé ou inexistant : '
. get_class() . '::' . $attribut);
}
Cet exemple autorise l'accès aux variables privées et protégées de cette classe à n'importe quelle classe contenant "Mapper_" dans son nom (i.e. Data Mapper). Par contre ce code suppose que vous avez nommé vos variables privées en respectant les conventions de nommage PEAR.
Alors, à quand les classes Friend dans PHP ? Le polymorphisme ? Et l'héritage multiple :-) ?
Ce post vous a plu ? Ajoutez le dans vos favoris pour ne pas perdre de temps à le retrouver le jour où vous en aurez besoin :