Pourquoi et comment utiliser unassert en php5 OO
1 Généralités sur les asserts
Le assert est quelquechose qui sert énormément dans un programme,en général en C. On s'en sert souvent pour vérifier lors d'undebug, si nos variables sont bien cohérentes. L'utilisation d'unassert :
assert(chose_qui_doit_etre_vraie);
On peut trouver de nombreux exemples sur les assert sur cette page :http://www.google.com/codesearch?as_q=assert\((.*)\)voici un exemple marquant :
assert( min <= max );
en C, ce n'est pas une fonction mais une macro, elle peut-etredéfinie comme ceci :
#ifdef DEBUG
#define assert(expr) {if (!(expr)) \ FatalError("Assertion failed file %s, line %d: expr\n", \ __FILE__, __LINE__); }
#else
#define assert(expr)
#endif
http://www.google.com/codesearch?hl=en&q=+assert\((.*)\)+show:fInDJnJnt8I:bDg3EMzJKx4:KLZFRWkNVQk&sa=N&cd=10&ct=rc&cs_p=http://releases.mozilla.org/pub/mozilla.org/firefox/releases/2.0.0.2/source/firefox-2.0.0.2-source.tar.bz2&cs_f=mozilla/gfx/cairo/libpixman/src/pixregion.c Source de mozilla.
Le fait que ça soit une macro ne ralentit en rien l'exécution enproduction (en debug, on s'en moque). C'est un aventage compareaux assert de d'autres langages (php par exemple.)
Quand on débute, on peut s'imaginer que ça ne sert à rien devérifier si un min est infèrieur à un max, mais parfois, il arriveque des bugs nous ammènnent à cette situation, on doit donc pouvoirêtre prévenu en cas de bug, trouver le bug, et le corriger.L'assert ne nous permet pas de le corriger, mais il nous permet desavoir qu'il existe, et en gros de situer ou se trouve le bug.
En gros, vérifiez les évidences et vous debugerez moins longtemps.
2 Pourquoi utiliser un assert en php5 ?
En php, trop peu de gens se servent d'un error_reporting strict, ontrouve trop de gens qui codent sans afficher les warnings et notices.Si ce n'est deja fait : la page :http://fr2.php.net/manual/fr/function.error-reporting.phpvous offre un apperçu intéressant pour reconfigurer votre php.ini.Voici la fonction assert prédéfinie dans php :http://fr2.php.net/manual/fr/function.assert.phptrop peu s'en servent. Tout les programmeurs C ont deja vu desassert, j'en ai rarement vu en php. en php :http://www.google.com/codesearch?as_q=assert\((.*)\)&btnG=Search+Code&as_lang=php8000 codes trouvés seulement (contre 1 570 000 pour tout langagesconfondus)
En php, on peut passer des éléments du mauvais type, voir, laisserpasser des choses par le client (en GET ou POST), qu'il ne devraitpas passer.
Parfois, vérifier tout ça, avec des assert, serait utile, ouvérifier qu'une date de naissance à une valeur infèrieure à ladate du jours, des choses du genre. Ca peut toujours être utile devérifier ça, pour interdire les objets "mauvais", cesobjets entrainnent un code "fou", il est important debloquer le flux d'execution quand on arrive là.
Quand on utilise le modèle objet, il est simple de vérifier danschaque "seter" de propriétée privée ou protegée, si lavariable à une valeur "corrompue" ou non.
En php, on a pas de properties, on fait soit des seters, soit uneméthode magique :
public function __set($clef, $valeur){ $this->$clef = $valeur ;
}
3 Comment utiliser un assert en php5
On peut commencer par definir une Exception d'assertion.
class AssertException extends Exception {} Pour la suite, on vérifiera si un humain a un âge négatif ou non.
En général, quand on fait de l'objet en java ou php5, on fait untruc du genre :
class Humain extends assertable{ public function __construct($age){ parent::__construct();
echo 'Un humain nait<br/>';
$this->setAge($age);
}
protected function setAge($age){ if ($age < 0){ throw new AssertException('age invalide'); }
$this->age=$age;
}
}
En pascal ou C#, on peut utiliser les properties, ca revient à peuprès au même, mais c'est transparent.
En php, la méthode magique __set permet de faire une fonctionappellée pour gérer les affectations de variables membres par ducode qui n'est pas contenu dans la classe.
$truc->clef=val;
cependant, ça à plusieurs inconvéniants : exemple : l'utilisationne sera pas transparente, c'est seulement ce qui est hors de laclasse qui appelle __set. Voici un exemple de la porté de __set.
//exemple pour voir les apples de __set
class A{ public function __construct($a){ $this->a=$a; // ceci ne l'appelle pas.
}
public function __set($k, $v){ echo 'appel a __set';
$this->$k=$v;
}
private $a;
}
$a=new A('ok'); // ceci ne l'appelle pas.
$a->a='B'; // ceci l'appelle
Pour l'avoir de façon transparente, à l'intérieur de laclasse, on doit faire un héritage :
abstract class assertable{ public function __construct(){ } public function __set($key, $val){ $this->$key = $val;
$this->__assert();
}
abstract protected function __assert();
}
Voici une classe qui en hérite :
class Humain extends assertable{
public function __construct($age){ parent::__construct();
echo 'Un humain nait<br/>';
$this->age=$age;
}
protected function __assert(){ if ($this->age > 130){ throw new AssertException('age invalide'); }
}
}
4 Inconvéniants
L'inconvéniant principal, c'est la porté de la variable.
Humain::age n'existe pas, en réalite, c'est assertable::age, cequi fait que age est défini comme étant public...
Ce qui est intéressant ici, c'est que le coté "est-ce publicou pas" n'intervient pas trop, puisqu'on reste dans la boitenoirre, avec ce seter magique qui vérifie les mauvaises valeurs (enrésum é, on simule des sortes de properties du C# ou freepascal).
Cependent, ça peut-etre embétant pour des choses comme ceci :
[assertable] <--- [Humain] <--- [Enfant]
Sur un UML comme ceci, on aurait des choses bizares : on auraittoutes les variables membres de Enfant stoquées en réalite dansassertable.
Avec une déclaration correcte des variables, si on avait vouludéfinir dans Humain une variable private ($age par exemple), et leredéfinir dans Enfant, sans le hack, ca nous aurait fait deuxinstances différentes (à condition de les déclarer avant). Avec lehack, ca ne fonctionne plus, nos deux Enfant::age, et Humain::agesont les mêmes variables.
5 Conclusion
Utiliser les assert vous fera gagner du temps lors du debug, utiliserles __set vous permet d'appler vos assert quand vos variables sontmodifiées depuis d'autres classes, et utiliser __set couple à del'héritage vous permet de faire un assert puissant, qui vérifiechaque modification interne à la classe, bien que vous y perdiez enrigueur.