Hook de fonctions

ViPHP
xTG
ViPHP | 7331 Messages

17 oct. 2010, 17:16

Cela reprend ce que j'avais compris de la méthode de cette technologie donc. Je sais au moins de par ton explication (qui ne s'est pas perdu en route malgré tes avertissements :) ) que j'étais sur la bonne voie.

Cependant je m'attache à une condition supplémentaire, c'est de là que j'étais parti sur mon héritage de la classe hook.
Il ne faut pas que l'utilisateur est à placer les lignes EventManager::getInstance()->launch('EmailManager.send.pre_hook'); pour que cela fonctionne.
En gros ce que je souhaite développer (si un tant soit peu on puisse le faire de façon propre, car ma classe actuelle le fait - sauf que j'ai rien vérifié en E_STRICT -) c'est de pouvoir faire n'importe quelle classe puis de la faire hériter de ma classe hook.
Juste de par cela j'obtiens la possibilité de placer des hooks où je le désire et non des hooks testés en dur dans ma classe.

ViPHP
ViPHP | 5462 Messages

17 oct. 2010, 19:03

t'as regardé du coté de runkit ?

ViPHP
xTG
ViPHP | 7331 Messages

17 oct. 2010, 20:21

t'as regardé du coté de runkit ?
Je ne connaissais pas, j'ai donc jeté un oeil à la documentation.
Ce n'est pas exactement ce que je recherches, ce serait un peu lourd je trouve.
Le but avec runkit serait de dupliquer la fonction pour ensuite son traitement. Un peu lourd pour juste faire un handler je trouve.
De plus je suis souvent amené à travailler sur des serveurs ne me permettant pas de retravailler le noyau PHP. Or runkit n'est pas fourni de base, doncà moins de tomber sur un hébergeur qui l'ai installé ce n'est pas une option facile.

devlop78
Invité n'ayant pas de compte PHPfrance

18 oct. 2010, 13:35

Php souffre de sérieuses lacunes dans ces domaines. Les évenements, il connait pas ;) A côté, il permet de faire de sérieuses gymastiques, comme l'appel dynamique de variable $$var ou de fontion ou méthode $$objet->$methode(); Mais alors qu'il pourrait implémenter la possibilité d'obliger un typage, il ne le fait guère.
Bonjour à toutes et à tous,

je cherche à mettre en place une classe gérant les hooks sur les fonctions filles (des classes étendant ma class hook).
Or je me retrouve confronté à un soucis. Comment réussir à déclencher un évènement lors de l'appel d'une fonction ?

J'ai passé un bon moment à parcourir php.net pour trouver solution à mon problème.
Je suis tombé donc sur la fonction magique __call qui permettent de gérer des hooks mais sur des éléments n'existant pas ou ne pouvant être retournés.

J'ai donc une solution temporaire mais qui ne me plaît guère, à savoir tout passer en protected ce qui aura comme conséquence de ne pouvoir être appelé depuis l'extérieur sans déclencher __call.
<?php
class hook
{
    private $tab_hook_nom = array('debut' => true, 'fin' => true);
    private $tab_hook_fonction = array();
    protected $nom_extends = "";
    
    public function __construct($nom)
    {
        $this->nom_extends = $nom;
    }
    
    public function __call($name, $arguments)
    {
        echo "Appel de la méthode '$name' " . implode(', ', $arguments). "<br />";
        if( method_exists($this,$name) )
        {
            echo "hook debut<br />";
            call_user_func_array(array($this->nom_extends, $name), $arguments);
            echo "hook fin<br />";
        }
    }
    
    protected function fonction2($nb1,$nb2,$nb3)
    {
        echo "fonction2 : $nb1, $nb2, $nb3<br/>";
    }
        
    protected function ajouter_hook($nom, $fonction)
    {
        if( !empty($this->tab_hook_nom[$nom]) )
        {
            $this->tab_hook_fonction[$nom][$fonction] = $fonction;
            return true;
        }
        else
            return false;
    }
    
    protected function supprimer_hook($nom, $fonction)
    {
        if( !empty($this->tab_hook_nom[$nom]) )
        {
            if( !empty($this->tab_hook_fonction[$nom][$fonction]) )
            {
                unset($this->tab_hook_fonction[$nom][$fonction]);
            }
        }
    }
}

class test extends hook
{
    protected $name = "test";
    
    public function __construct()
    {
        parent::__construct($this->name);
        echo "instanciation<br />";
    }
    
    protected function fonction1()
    {
        echo "fonction1<br />";
    }
}

$test = new test();
$test->fonction1(1,2,3);
$test->fonction2();

?>
Déroulement :
instanciation
Appel de la méthode 'fonction1' 1, 2, 3
hook debut
fonction1
hook fin
Appel de la méthode 'fonction2'
hook debut

Warning: Missing argument 1 for hook::fonction2() in C:\wamp\www\test.php on line 24

Warning: Missing argument 2 for hook::fonction2() in C:\wamp\www\test.php on line 24

Warning: Missing argument 3 for hook::fonction2() in C:\wamp\www\test.php on line 24

Notice: Undefined variable: nb1 in C:\wamp\www\test.php on line 26

Notice: Undefined variable: nb2 in C:\wamp\www\test.php on line 26

Notice: Undefined variable: nb3 in C:\wamp\www\test.php on line 26
fonction2 : , ,
hook fin
J'ai pour le moment toujours le soucis de passer l'array des paramètres à call_user_func_array(), la variable ne semble pas être reconnue comme un type array... Enfin c'est annexe pour le moment, je trouverai bien un moyen.

Que pensez-vous de cette solution ? Existe-t-il mieux ? Plus propre ?
Bien évidemment il aurait été plus simple de gérer les hooks directement dans les fonctions... Mais c'est une solution que je ne veux pas car je ne peux être sûr que l'utilisateur qui utilisera ma classe pensera à rajouter le code pour les hooks de sa fonction.

ViPHP
xTG
ViPHP | 7331 Messages

19 oct. 2010, 17:55

Strict Standards: call_user_func_array() expects parameter 1 to be a valid callback, non-static method test::fonction1() should not be called statically, assuming $this from compatible context test in /var/www/tmp/hook.php on line 19
Je me suis remis sur ma classe et tentais de voir comment régler ce soucis.
Sauf que soit les standards ne sont pas les mêmes pour PHP sur Unix et Windows soit j'ai failli à la mise en place de l'affichage des erreurs STRICT...
error_reporting(E_ALL & E_STRICT);
Cette ligne ne suffirait-elle pas ?
Car je n'obtient aucune erreur de mon côté.

ViPHP
ViPHP | 5462 Messages

19 oct. 2010, 18:05

si les standards sont les mêmes (si t'as la même version)
Modifié en dernier par stealth35 le 19 oct. 2010, 18:46, modifié 1 fois.

Mammouth du PHP | 19672 Messages

19 oct. 2010, 18:45

C'est un | et non un & entre les deux :

Code : Tout sélectionner

error_reporting E_ALL | E_STRICT
Codez en pensant que celui qui maintiendra votre code est un psychopathe qui connait votre adresse :axe:

ViPHP
xTG
ViPHP | 7331 Messages

19 oct. 2010, 19:36

J'avais aussi testé ne sachant comment c'était géré.
Mais pas plus d'erreurs.

Je vais réessayer d'ici ce weekend à titre de test complémentaire puis je posterai ma source à jour.

Mammouth du PHP | 19672 Messages

19 oct. 2010, 21:08

Ben ma configuration ressemble à ceci :
Configuration du Serveur LAMP

Version de Apache : Apache/2.2.14 (Ubuntu)
Version de PHP : 5.3.1
Version de MySQL : 5.1.41-3ubuntu12.6
Et extrait de mon php.ini :

Code : Tout sélectionner

error_reporting=E_ALL | E_STRICT
Et avec ça j'ai les erreurs E_STRICT.
Codez en pensant que celui qui maintiendra votre code est un psychopathe qui connait votre adresse :axe:

ViPHP
xTG
ViPHP | 7331 Messages

20 oct. 2010, 00:01

Pour ma part je suis sous Wamp.
Apache : 2.2.11
PHP : 5.2.9-2 (pas à jour je sais mais c'est par soucis de compatibilité avec certains scripts)
MySQL : 5.0.51b (pareil mais on s'en fiche on final)

Mon php.ini est de la sorte : error_reporting=E_ALL & ~E_NOTICE
Et la première ligne de mon script php : error_reporting(E_ALL | E_STRICT);

Aurais-tu un exemple d'erreur E_STRICT tout bête et qui marche à tous les coups ?

Mammouth du PHP | 19672 Messages

20 oct. 2010, 02:28

ben justement l'appel statique de méthodes non statiques, il y a aussi les passages de variables par référence.

Mais il y a une note importante dans la Doc :
Avertissement

La plupart des erreurs E_STRICT sont évaluées au moment de la compilation, comme les erreurs qui ne sont pas reportées dans le fichier lorsque error_reporting est défini pour inclure les erreurs E_STRICT (et vice-versa).
En d'autres termes, essaye en modifiant directement ton php.ini parce qu'il est possible que le error_reporting ne fonctionne pas précisément pour les erreurs E_STRICT.
Codez en pensant que celui qui maintiendra votre code est un psychopathe qui connait votre adresse :axe:

ViPHP
xTG
ViPHP | 7331 Messages

20 oct. 2010, 09:08

Le passage d'un objet par référence donc devrait déclencher cette erreur, j'essayerai avec l'objet de la fonction donc.
Mais il me semble que j'avais essayé lorsque je tentai de passer $this dans le père pour éviter l'appel statique, mais j'avais un deprecated et non un E_STRICT.

ViPHP
xTG
ViPHP | 7331 Messages

20 oct. 2010, 20:33

J'ai testé certaines erreurs E_STRICT, elles apparaissent bien.
Donc ma classe est bien sans erreur dans sa version finale.

Je vais la mettre dans la catégorie des contributions dans sa dernière version. :)

Eléphant du PHP | 185 Messages

10 nov. 2010, 00:54

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

ViPHP
xTG
ViPHP | 7331 Messages

10 nov. 2010, 11:29

Intéressant cet autre vision de la chose. :D
Content de trouver une autre méthode, surtout qui est plus claire vu qu'on créé une class aspect pour chaque n-couples d'appel.
En gros un modèle de fonctionnement des hooks pré-enregistré.

Un petit peu de lecture pour les ennuyantes nuits. :3