Bonsoir,
Je m'incruste un peu désolé. ^^
Ce que tu veux faire, je crois bien que ça se rapproche de la programmation orientée aspect (oui, ça fait joli comme nom ^^ ). Certains langages ont ça nativement, mais par contre c'est émulable facilement je pense. Voici ce que j'avais obtenu il y a un petit temps pour réaliser ce que tu veux. Ça n'utilise pas l'héritage mais la composition.
<?php
class DB {
public function query($sql) {
printf("Executing %s.<br />", $sql);
return 'Result';
}
public function execWithReturn() {
printf("Executing execReturn.<br />");
}
public function execWithException() {
printf("Executing execException.<br />");
throw new Exception('My Exception');
}
}
class Aspect
{
private $object;
private $before;
private $afterReturn;
private $afterException;
public function __construct($object)
{
$this->object = $object;
$r = new ReflectionClass($object);
//print_r($r->getMethods());
$this->around = array();
$this->before = array();
$this->afterReturn = array();
$this->afterException = array();
}
public function _aopExecuteAround($method, $advice, $args = array())
{
$this->around[$method] = array($advice, $args);
}
public function _aopExecuteBefore($method, $advice, $args = array())
{
$this->before[$method] = array($advice, $args);
}
public function _aopExecuteAfterException($method, $advice, $args = array())
{
$this->afterException[$method] = array($advice, $args);
}
public function _aopExecuteAfterReturn($method, $advice, $args = array())
{
$this->afterReturn[$method] = array($advice, $args);
}
public function __call($method, $args)
{
if (method_exists($this->object, $method) || method_exists($this->object, '__call'))
{
if (isset($this->around[$method]))
{
$i =& $this->around[$method];
return call_user_func_array($i[0], array($i[1], &$args, array($this->object, $method)));
}
if (isset($this->before[$method]))
{
$i =& $this->before[$method];
call_user_func_array($i[0], array($i[1], &$args));
}
try
{
$return = call_user_func_array(array($this->object, $method), $args);
if (isset($this->afterReturn[$method])) {
$i =& $this->afterReturn[$method];
call_user_func_array($i[0], array($i[1], &$return));
}
return $return;
}
catch (Exception $e)
{
if (isset($this->afterException[$method])) {
$i =& $this->afterException[$method];
call_user_func_array($i[0], array($i[1], &$e));
}
}
return;
}
trigger_error("Unknown method $method in class ".get_class($this->object), E_USER_ERROR);
}
}
class DB_Aspect extends Aspect
{
public function __construct($db)
{
parent::__construct($db);
$this->_aopExecuteBefore('query', array($this, 'before'));
$this->_aopExecuteAfterReturn('execReturn', array($this, 'afterReturn'));
$this->_aopExecuteAfterException('execException', array($this, 'afterException'));
$this->_aopExecuteAround('query', array($this, 'around'));
}
protected function before($argsAdvice, &$argsMethod)
{
printf("Before query.<br />");
}
protected function afterReturn($argsAdvice, &$returnValue)
{
printf("After return.<br />");
}
protected function afterException($argsAdvice, &$exception)
{
printf("After exception.<br />");
throw $exception;
}
protected function around($argsAdvice, &$argsMethod, $func)
{
printf("Method before!<br />");
printf("Returned: %s.<br />", call_user_func_array($func, $argsMethod));
printf("Method after!<br />");
}
}
$db = new DB();
$db = new DB_Aspect($db);
$db->query('sql');
echo '-----<br />';
$db->execWithReturn();
echo '-----<br />';
$db->execWithException();
La classe DB, c'est celle où on veut ajouter des "hooks" (avant ou après l'éxécution d'une méthode). Elle n'a pas besoin de connaître les hooks ou de savoir si certains sont présents. Les hooks sont rajoutés dynamiquement sans modification de la classe de base.
La classe Apect, c'est la classe magique, t'y touches pas tu l'inclues quelque part.
La classe DB_Aspect contient les hooks que tu veux appliquer à la classe DB. Ces derniers sont configurés dans le constructeur.
Il est possible de mettre des hooks avant l'éxécution (et lire la liste des paramètres entrants). Pour en mettre après, il y a une distinction entre retour classique ou retour à cause d'une exception. Enfin, avec le around() à toi de rappeler la méthode voulue manuellement avec call_user_func_array().
L'exemple d'utilisation est ensuite présenté. Il faut d'abord créer la classe que tu veux (exemple : DB), puis la passer en paramètre à l'aspect voulue. Tu peux "empiler" plusieurs aspects si nécessaires.
Voilà voilà ! ^^
[edit] J'allais oublier : un jouli n'article en bonus :
http://www.dotnetguru.org/articles/doss ... /AOP15.htm