[PHP] Class hook (l'art de lancer plusieurs traitements)
Posté : 20 oct. 2010, 20:45
Je souhaitai développer une class permettant de faire du hook, à savoir lancer un second (voir X traitements) en plus de la fonction appelée et ce de manière presque transparente. Voici le fil que j'avais ouvert lors du développement de cette classe : php-oriente-objet/hook-fonctions-t255592.html
Certains frameworks possèdent ce genre de traitement au travers d'une construction de leurs fonction de la sorte :
C'est donc un ajout direct dans le code de toutes les fonctions et cela ne me plaisait pas.
Je me suis donc penché sur comment faire une class dont on hériterai et qui pourrai gérer l'appel d'un autre traitement.
Le procédé final n'est pas génial (faut aussi admettre que PHP ne donne aucun atout véritable pour gérer ce type de fonctionnement...) et pourra certes être contesté. Je l'ai développé pour mes propres besoins (et ajouté un ou deux trucs qui me serviront sans doute jamais mais qui vous aideront peut être) donc je ne m'amuserai pas à tout re-modifier sans que cela ne m'apporte quelque chose à mon problème de départ. Cela dit vous êtes libre de repartir sur ces bases et de la modifier à votre convenance.

Certains frameworks possèdent ce genre de traitement au travers d'une construction de leurs fonction de la sorte :
Code : Tout sélectionner
fonction ma_fonction(paramètres)
{
appel fonction hook début si elle existe
exécution de la fonction "ma_fonction"
appel fonction hook fin si elle existe
}Je me suis donc penché sur comment faire une class dont on hériterai et qui pourrai gérer l'appel d'un autre traitement.
Le procédé final n'est pas génial (faut aussi admettre que PHP ne donne aucun atout véritable pour gérer ce type de fonctionnement...) et pourra certes être contesté. Je l'ai développé pour mes propres besoins (et ajouté un ou deux trucs qui me serviront sans doute jamais mais qui vous aideront peut être) donc je ne m'amuserai pas à tout re-modifier sans que cela ne m'apporte quelque chose à mon problème de départ. Cela dit vous êtes libre de repartir sur ces bases et de la modifier à votre convenance.
class hook
{
/*
* Liste des emplacements hook actifs
* @type array(String => Boolean)
*/
protected $tab_hook_nom = array('debut' => true, 'fin' => true);
/*
* Liste des fonctions hook
* @type array(Mixed)
*/
protected $tab_hook_fonction = array();
/*
* Instance fille
* @type Object
*/
protected $instance_fille = null;
/*
* Constructeur
* @type Object objet héritant de la classe Hook
*/
public function __construct($instance_fille)
{
$this->instance_fille = $instance_fille;
}
/*
* Appel de fonction
*/
public function __call($name, $arguments)
{
// Gestion de l'appel du plus bas niveau de l'héritage
if( !empty($this->instance_fille) )
$fonction_appel = array($this->instance_fille, $name);
else
$fonction_appel = array($this, $name);
// On vérifie que la méthode appelée existe
if( method_exists($fonction_appel[0],$name) )
{
// On prépare les arguments pour les fonctions hook
$arguments_hook[0] = $name;
$arguments_hook[1] = $arguments;
// Appel des hooks de debut
if( !empty($this->tab_hook_nom['debut']) && !empty($this->tab_hook_fonction['debut'][$name]) )
{
foreach($this->tab_hook_fonction['debut'][$name] as $fonction)
call_user_func_array($fonction, $arguments_hook);
}
// Appel de la fonction
$retour = call_user_func_array($fonction_appel, $arguments);
// Appel des hooks de fin
if( !empty($this->tab_hook_nom['fin']) && !empty($this->tab_hook_fonction['fin'][$name]) )
{
foreach($this->tab_hook_fonction['fin'][$name] as $fonction)
{
$arguments_hook[2] = $retour;
$retour_tmp = call_user_func_array($fonction, $arguments_hook);
if( isSet($retour_tmp) ) // vérification que la fonction renvoie bien une valeur
$retour = $retour_tmp;
}
}
// Retour du résultat
return $retour;
}
}
/*
* Méthode ajoutant un hook sur une fonction de la classe
* @type String emplacement du hook
* @type String fonction ciblée par le hook
* @type String/array(mixed) fonction appelée lors du hook
* string => nom de la fonction
* array(mixed) => array( Object, String) => array( objet instancié, nom de la fonction de l'objet)
* @return Boolean
*/
protected function ajouter_hook($nom, $fonction, $fonction_hook)
{
if( !empty($this->tab_hook_nom[$nom]) )
{
$this->tab_hook_fonction[$nom][$fonction][] = $fonction_hook;
return true;
}
else
return false;
}
/*
* Méthode supprimant un hook sur une fonction de la classe
* @type String emplacement du hook
* @type String fonction ciblée par le hook
* @type String fonction appelée lors du hook
*/
protected function supprimer_hook($nom, $fonction, $fonction_hook)
{
if( !empty($this->tab_hook_nom[$nom]) )
{
if( !empty($this->tab_hook_fonction[$nom][$fonction]) )
{
foreach($this->tab_hook_fonction[$nom][$fonction] as $key => $fct)
{
if( (is_array($fct) && is_array($fonction_hook)) || (is_string($fct) && is_string($fonction_hook)) )
if( $fct == $fonction_hook )
{
unset($this->tab_hook_fonction[$nom][$fonction][$key]);
break;
}
}
}
}
}
public function __toString()
{
return get_class();
}
}
Point positif du système :
- La classe qui hérite de Hook ne change en rien de code, toutes vos fonctions restent les mêmes
- On peut appeler plusieurs fonctions avant et après la fonction appelée, c'est un tableau de fonction qui est géré.
- Pour que le système s'applique il faut que vos méthodes soient protected afin que la méthode __call() de la classe Hook puisse être lancée. La classe se base sur le fait que la fonction ne puisse être appelée de par la portée pour exécuter la fonction __call() qui est une fonction PHP d'erreur.
- Il faut jouer un peu avec le constructeur si vous faites plusieurs héritages (voir le constructeur de la classe Test) afin d'enregistrer le dernier fils de l'héritage pour palier aux soucis d'appels statique.
class test extends hook
{
/*
* @type Object
*/
public function __construct($fils = null)
{
if( !empty($fils) )
parent::__construct($fils);
else
parent::__construct($this);
}
protected function fonction1()
{
echo "exécution fonction1<br />";
}
protected function fonction2($nb1,$nb2,$nb3)
{
echo "exécution fonction2 : $nb1, $nb2, $nb3<br/>";
return $nb1 + $nb2 + $nb3;
}
public function __toString()
{
return get_class();
}
}
$i = 0;
function mon_hook($fonction, $arguments)
{
global $i;
$i++;
}
function mon_hook2($fonction, $arguments, $retour)
{
global $i;
$i += 2;
echo "$retour + 2(hook)";
return $retour + 2;
}
$test = new test();
$test->ajouter_hook("debut","fonction2",array($test,"fonction1"));
$test->ajouter_hook("fin","fonction2","mon_hook");
echo $test->fonction2(1,2,3) . "<br />";
echo $i;
En espérant que cela serve à quelqu'un.exécution fonction1
exécution fonction2 : 1, 2, 3
6
1