Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

  1. #1
    Rédacteur

    POO PHP5 : Design Pattern observateur aidé de la Standard PHP Library (SPL)
    Le design pattern observateur est un classique du GOF, il participe au découplage et à la réduction des dépendances.
    En général, 2 interfaces sont utilisées, on peut aussi manipuler des classes abstraites. Nous allons ici montrer un exemple complet de son utilisation et nous allons nous aider de la puissante librairie objet interne de PHP5 : la SPL.
    POO PHP5 : Design Pattern observateur aidé de la Standard PHP Library (SPL)


  2. #2
    Membre à l'essai
    Petite coquille
    Très bon article, avec un exemple concret

    Je pense qu'il y a une coquille dans la fabrique par réflexion :
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
     
    public function factory($listener, array $args = array())
        {
            $reflect = new ReflectionClass($class); //devrait être $listener
            return $reflect->newInstanceArgs($args);
        }


    La variable $class n'est pas définie, ca devrait être $listener, nan ?

  3. #3
    Membre régulier
    Avis
    Bonjour.
    Dans un premier temps, grand merci à Julien Pauli pour ses nombreux tutoriaux.
    Ceci étant dit, une relecture avant publication ne serait pas inutile. Ce n'est pas la première fois que les codes sont inutilisables et qu'il est fait référence en 3 mots à une notion qui nécessiterait bien 10 phrases (si on lit un tuto sur un design pattern, les chances sont grandes qu'on ne maitrise pas les autres sur le bout des doigts)
    Quelques erreurs:
    Chaque fois qu'apparait la méthode notifyObservers, ce devrait être notify.
    Dans le même registre, pour une raison incompréhensible, les instances de ErrorHandler utilisent la méthode add() (non définie) quand elles devraient utiliser attach().
    Enfin, pour être franc, à partir du II-D, je ne comprends plus rien (oui, je sais, je ne suis pas doué, mais c'est aussi pour ça que je lis des tutos, pour progresser)

  4. #4
    Membre confirmé
    tant qu'on y est...
    Excellent tutoriel, je lis toujours avec un grand plaisir les publications de Julien Pauli, car c'est une véritable mine d'or pour le développeur avide de code clair, propre et maintenable.

    petite suggestion
    Plutôt que de faire une méthode getError() qui retourne une chaine, ne peut-on pas pousser encore plus loin l'utilisation de l'API standard PHP au moyen de la méthode magique __toString() qui remplirait exactement le même rôle ?

  5. #5
    Membre habitué
    Bon, je vais surtout parler de gestionnaire d'erreur car j'ai bien un pattern Observer, mais sans la SPL. Voici un gestionnaire d'erreur sympa à utiliser également, le plus sympa étant de pouvoir filtrer les types d'erreurs directement dans le gestionnaire !

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    <?php
     
    class MultipleErrorHandler {
     
    	// List every observer for each $errno
    	protected static $observers   = array();
    	// Reference each callback function
    	protected static $callbacks   = array();
    	// List all the catchable errors
    	protected static $catchable_errors = array();
     
    	// Attach an callback function as error handler
    	// The index return is needed to Detach the callback handler
    	public static function Attach($callback, $errno = E_ALL) {
    		static $count = 1;
    		self::$callbacks[$count] = $callback;
    		foreach(self::get_catchable_errors() as $catchable) {
    			if (($errno & $catchable) == $catchable) {
    				array_unshift(self::$observers[$catchable], $count);
    			}
    		}
    		return $count++;
    	}
     
    	// Detach a callback function from error handling
    	// Needs the index returned by Attach()
    	public static function Detach($index, $errno = E_ALL) {
    		foreach(self::get_catchable_errors() as $catchable) {
    			if (($errno & $catchable) == $catchable) {
    				$key = array_search($index, self::$observers[$catchable]);
    				if (false !== $key) {
    					unset(self::$observers[$catchable][$key]);
    				}
    			}
    		}
    	}
     
    	// Runs the MultipleErrorHandler
    	public static function Run($old_error_level = E_ALL) {
    		$old_error_handler = set_error_handler(array(__CLASS__, 'Notify'));
    		// If an old handler was attached, keep it in the error handling process
    		if ($old_error_handler !== null && $old_error_level) {
    			self::Attach($old_error_handler, $old_error_level);
    		}
    		return $old_error_handler;
    	}
     
    	// Custom error handler used by the class
    	public static function Notify($errno, $errstr, $errfile, $errline, $errcontext) {
    		$stop_propagation = false;
    		foreach(self::get_catchable_errors() as $catchable) {
    			if (($errno & $catchable) == $errno) {
    				foreach(self::$observers[$errno] as $callback) {
    					$stop_propagation = (true === call_user_func(self::$callbacks[$callback], $errno, $errstr, $errfile, $errline, $errcontext)) || $stop_propagation;
    				}
    			}
    		}
    		return $stop_propagation;
    	}
     
    	// Returns all catchable errors as an array
    	public static function get_catchable_errors() {
    		if (empty(self::$catchable_errors)) {
    			self::$catchable_errors = array(E_WARNING,E_PARSE,E_NOTICE,E_USER_ERROR,E_USER_WARNING,E_USER_NOTICE);
    			foreach(array('E_STRICT','E_RECOVERABLE_ERROR','E_DEPRECATED','E_USER_DEPRECATED') as $constant) {
    				defined($constant) && self::$catchable_errors[] = constant($constant);
    			}
    			foreach(self::$catchable_errors as $type) {
    				self::$observers[$type] = array();
    			}
    		}
    		return self::$catchable_errors;
    	}
    }
     
    function test_handler($errno, $errstr, $errfile, $errline, $errcontext) {
    	echo 'test ok';
    	return false;
    }
     
    function test_handler_2($errno, $errstr, $errfile, $errline, $errcontext) {
    	echo 'test autre';
    	return false;
    }
     
    MultipleErrorHandler::Run(); // Don't reattach the old error handler
    $index = MultipleErrorHandler::Attach('test_handler', E_WARNING);
    $index = MultipleErrorHandler::Attach('test_handler_2', E_WARNING);
    2/0;

  6. #6
    Expert éminent
    Bonjour et merci pour cet article.

    J'aurais juste une remarque d'ordre esthétique à apporter à la discussion : tant qu'a avoir un factory qui utilise les classes de reflexion, on pourrait en profiter pour protéger le programmeur de plus haut niveau de ses erreurs en restreignant les classes utilisables aux seuls SplObservers :

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    public static function factory ($class, $args)
    {
        if (!is_array($args)) $args = array($args); // permet l'usage class::factory('FileHandler', 'fichier.txt');
        $reflect = new ReflectionClass($class);
        if ($reflect->implementsInterface('SplObserver'))
            return $reflect->newInstanceArgs($args);
        else
            // Erreur|Exception|null
    }


    De cette manière le code est bien plus sécurisé : le factory n'est capable de créer que ce qui peut être attaché à l'objet en question.

###raw>template_hook.ano_emploi###