Tutoriel PHP POO

PHP

Découvez la programmation orienté objet en de PHP.

HTML HTML5 CSS Dreamweaver Bootstrap PHP

Méthodes magiques : __set() et __get()

Av

Rappels concernant la visibilité des propriétés et des méthodes

Accès public des propriétés

La programmation orientée objet permet de spécifier la visibilité des méthodes et propriétés d'une classe. Dans le cas des propriétés, il est très peu recommandable de leur donner une visiblité publique car de cette manière il devient possible de les modifier sans qu'aucun contrôle ne soit effectué sur les valeurs.

<?php
class Kid {
/**
* Age du kid
*
* @var int
* @access public
*/
public $age;
/**
* Retourne l'âge du Kid sous forme d'une chaîne
*
* @param void
* @return string
*/
public function showAge() {
return 'Son âge est : '. $this->age;
}
}
$billy = new Kid();
$billy->age = 'rouge';
echo $billy->showAge();
?>

Cette portion de code vous retournera donc « Son âge est rouge » ce qui, vous en conviendrez, ne veut strictement rien dire.

Accès privé et protégé des propriétés

Il est donc préférable de leur accorder une visiblité limitée : private ou protected.

<?php
class Kid {
/**
* Age du kid
*
* @var int
* @access private
*/
private $age;
}
$billy = new Kid();
$billy->age = "encore plus rouge qu'avant";
?>

Ce bout de code vous retournera une erreur fatale :

Fatal error: Cannot access private property Kid::$age in /path/to/Apprendre-php/magic_methods.php on line 6.

Une solution serait de créer un accesseur public setAge() qui permettrait de spécifier l'age en s'assurant que vous lui avez bien passé un chiffre, et getAge() pour pouvoir l'afficher.

Cette méthode est tout à fait envisageable, à condition de ne pas avoir un grand nombre de propriétés, le nombre d'accesseurs devenant bien trop important : vous pouvez utiliser implicitement les méthodes magiques __set() et __get().

De plus, la syntaxe suivante est tout à fait valable avec PHP :

<?php
class Kid {
}
$billy = new Kid();
$billy->age = 14;
$billy->cheveux = 'noirs';
// etc...
echo 'Billy est agé de ', $billy->age,' ans et ses cheveux sont de couleur ', $billy->cheveux;
?>

Et oui, vous pouvez renseigner et ensuite récuperer des propriétés à un objet php, sans que celles-ci aient été déclarées dans votre classe, plus sale non ? Il est donc possible de « boucher » cette petite bévue, toujours en utilisant implicitement les méthodes magiques __set() et __get() qui seront respectivement appelées lorsque l'on renseigne la propriété et lorsqu'on essaye d'en lire la valeur.

La méthode magique __set()

La méthode magique __set() permet de faire ce que l'on appelle de la surchage de propriétés d'une classe. En effet, lorsque l'on essaie de fixer la valeur d'une propriété inexistante de la classe, PHP appelle automatiquement cette méthode de manière implicite. Voici sa structure générale.

<?php
class MyObject
{
/**
* Methode magique __set()
*
* @param string $property Nom de la propriété
* @param mixed $value Valeur à affecter à la propriété
* @return void
*/
public function __set($property, $value)
{
// Code personnalisé à exécuter
}
}
?>

En redéfinissant explicitement cette méthode dans le corps de la classe, cela permet au développeur de réaliser des contrôles d'accès et de s'assurer que seules quelques propriétés peuvent être mises à jour. C'est ce que nous introduirons dans notre cas d'application plus loin dans ce cours.

La méthode magique __get()

La méthode magique __get() permet, quant à elle, de lire la valeur d'une propriété inexistante de la classe. Au même titre que la méthode magique __set(), la méthode magique __get() doit être redéfinie dans la classe pour exécuter du code personnalisé lorsque PHP appelle implicitement cette méthode. Là encore, cela permet de réaliser un contrôle d'accès sur les propriétés dont on essaie de lire les valeurs. Le prototype de cette méthode est présenté ci-dessous et sera développé davantage dans le cas d'application pratique de ce tutoriel.

<?php
class MyObject
{
/**
* Methode magique __get()
*
* @param string $property Nom de la propriété à atteindre
* @return mixed|null
*/
public function __get($property)
{
// Code personnalisé à exécuter
}
}
?>

Cas d'application pratique

Nous allons reprendre notre exemple précédent de notre classe Kid et nous allons lui intégrer ces deux méthodes magiques _get() et __set(). L'objectif est ainsi de réaliser un contrôle d'accès lorsque l'on essaie de fixer ou de lire la valeur de la propriété privée $age.

<?php
class Kid {
/**
* Age du kid
*
* @var int
* @access private
*/
private $age;
/**
* Methode magique __get()
*
* Retourne la valeur de la propriété appelée
*
* @param string $property
* @return int $age
* @throws Exception
*/
public function __get($property) {
if('age' === $property) {
return $this->age;
} else {
throw new Exception('Propriété invalide !');
}
}
/**
* Methode magique __set()
*
* Fixe la valeur de la propriété appelée
*
* @param string $property
* @param mixed $value
* @return void
* @throws Exception
*/
public function __set($property,$value) {
if('age' === $property && ctype_digit($value)) {
$this->age = (int) $value;
} else {
throw new Exception('Propriété ou valeur invalide !');
}
}
}
?>

De cette manière, on s'assure de ne pouvoir spécifier que l'âge de Billy, et rien de plus. Vous remarquerez que l'on effectue des tests « en dur » c'est à dire que l'on ne vérifie pas simplement que la propriété $age existe, mais seulement que l'utilisateur a cherché à spécifier « $age ». De plus, on contrôle le type de la valeur au moyen de la fonction ctype_digit() qui s'assure que le format de la valeur correspond bien à un nombre entier.

Il est tout à fait possible de récupérer les propriétés d'un objet, mais pour faire cela proprement, il est préférable d'utiliser les classes d'introspection (« reflection » en anglais), un tutoriel est actuellement en cours d'écriture concernant cet sujet de POO.

Inconvénients des méthodes magiques __get() et __set()

Bien que ces deux méthodes magiques soient très pratiques à utiliser, elles posent tout de même deux désagréments non négligeables lorsque l'on développe en environnemet professionnel. En effet, l'utilisation de __get() et __set() empêche tout d'abord la génération automatique de documentation de code au moyen des APIs (PHPDocumentor par exemple) utilisant les objets d'introspection (Reflection). D'autre part, cela empêche également les IDE tels qu'Eclipse d'introspecter le code de la classe et ainsi proposer l'auto-complétion du code.