Partie 7 – WordPress et Programmation Orientée Objet : Un Exemple WordPress – Implémentation : Gestion des Hooks WordPress

Publié: 2022-02-04

Jusqu'à présent, interagir avec l'API Plugin signifiait appeler add_action() et add_filters() dans le constructeur de chaque classe.

Jusqu'à présent, cette approche était assez bonne, car elle simplifiait les choses et nous permettait de nous concentrer sur l'apprentissage de la programmation orientée objet avec WordPress. Cependant, ce n'est pas idéal.

Si un objet enregistre tous ses hooks lors de sa création, des choses comme les tests unitaires deviennent délicates.

REMARQUE : Les tests unitaires doivent tester chaque « unité » de manière isolée. Même si vous n'écrivez pas de tests unitaires pour le moment, écrire du code testable vous fera gagner beaucoup de temps lors de la refactorisation ultérieure, si jamais vous décidez d'écrire des tests.

Le gestionnaire de crochets

Allons un peu plus loin et introduisons une nouvelle classe pour gérer nos hooks, nous l'appellerons Hooks_Manager . Cette classe va être responsable de l'enregistrement de tous nos crochets. Nous allons donc créer une nouvelle classe avec une méthode register() .

 class Hooks_Manager { /** * Register the hooks of the given object. * * @param object $object */ public function register( $object ) { // Register the hooks the specified object needs } }

Nous avons besoin d'une interface pour chaque classe qui doit enregistrer des crochets à implémenter.

 interface Hooks { /** * Return the actions to register. * * @return array */ public function get_actions(); }

Vous pouvez considérer une interface comme un contrat , où une classe qui implémente cette interface est "contractuellement liée" pour implémenter toutes les méthodes définies dans cette interface.

Par exemple, une classe Login_Error qui s'accroche à l'action login_head doit implémenter la méthode get_actions() de notre interface Hooks .

 class Login_Error implements Hooks { public function get_actions() { return array( 'login_head' => array( 'add_errors', 10, 1 ), ); } }

La méthode register() de Hooks_Manager accepte un objet, appelle sa méthode get_actions() et enregistre toutes ses actions.

 public function register( $object ) { $actions = $object->get_actions(); foreach ( $actions as $action_name => $action_details ) { $method = $action_details[0]; $priority = $action_details[1]; $accepted_args = $action_details[2]; add_action( $action_name, array( $object, $method ), $priority, $accepted_args ); } }

Ajoutons une méthode get_filters() à notre interface, afin que nous puissions enregistrer à la fois les actions et les filtres.

 interface Hooks { /** * Return the actions to register. * * @return array */ public function get_actions(); /** * Return the filters to register. * * @return array */ public function get_filters(); }

De retour à notre classe Login_Error , nous devons implémenter cette nouvelle méthode get_filters() .

 class Login_Error implements Hooks { public function get_actions() { return array( 'login_head' => array( 'add_errors', 10, 1 ), ); } public function get_filters() { return array( 'authenticate' => array( 'track_credentials', 10, 3 ), 'shake_error_code' => array( 'add_error_code', 10, 1 ), 'login_errors' => array( 'format_error_message', 10, 1 ), ); } }

Nous allons renommer la méthode register() de notre Hooks_Manager en register_actions() . Nous ajouterons également une méthode register_filters() . Ces deux méthodes seront chargées d'enregistrer respectivement les actions et les filtres.

 class Hooks_Manager { /** * Register the actions of the given object. * * @param object $object */ private function register_actions( $object ) { $actions = $object->get_actions(); foreach ( $actions as $action_name => $action_details ) { $method = $action_details[0]; $priority = $action_details[1]; $accepted_args = $action_details[2]; add_action( $action_name, array( $object, $method ), $priority, $accepted_args ); } } /** * Register the filters of the given object. * * @param object $object */ private function register_filters( $object ) { $filters = $object->get_filters(); foreach ( $filters as $filter_name => $filter_details ) { $method = $filter_details[0]; $priority = $filter_details[1]; $accepted_args = $filter_details[2]; add_filter( $filter_name, array( $object, $method ), $priority, $accepted_args ); } } }

Nous pouvons maintenant ajouter à nouveau une méthode register() , qui va simplement appeler à la fois register_actions() et register_filters() .

 class Hooks_Manager { /** * Register an object. * * @param object $object */ public function register( $object ) { $this->register_actions( $object ); $this->register_filters( $object ); } // ...

Que se passe-t-il si une classe n'a pas besoin d'enregistrer à la fois les actions et les filtres ? L'interface Hooks contient deux méthodes : get_actions() et get_filters() . Toutes les classes qui implémentent cette interface seront obligées d'implémenter les deux méthodes.

 class Cookie_Login implements Hooks { public function get_actions() { return array( 'auth_cookie_bad_username' => array( 'handle_bad_username', 10, 1 ), 'auth_cookie_bad_hash' => array( 'handle_bad_hash', 10, 1 ), 'auth_cookie_valid' => array( 'handle_valid', 10, 2 ), ); } public function get_filters() { return array(); } }

Par exemple, la classe Cookie_Login doit enregistrer uniquement les actions, mais elle est maintenant obligée d'implémenter la méthode get_filters() uniquement pour renvoyer un tableau vide.

Le principe de ségrégation d'interface (ISP) , le « I » de SOLID, stipule :

"Aucun client ne devrait être contraint de dépendre de méthodes qu'il n'utilise pas."

Cela signifie que ce que nous faisons maintenant est exactement ce que nous ne devrions pas faire.

Ségrégation d'interface

Nous pouvons résoudre ce problème en divisant notre interface en plus petites et plus spécifiques afin que nos classes n'aient à connaître que les méthodes qui les intéressent.

 interface Actions { /** * Return the actions to register. * * @return array */ public function get_actions(); }
 interface Filters { /** * Return the filters to register. * * @return array */ public function get_filters(); }

Nous n'avons plus besoin à la fois get_actions() et get_filters() , nous ne pouvons implémenter que l'interface Actions et nous débarrasser de get_filters()

 class Cookie_Login implements Actions { public function get_actions() { return array( 'auth_cookie_bad_username' => array( 'handle_bad_username', 10, 1 ), 'auth_cookie_bad_hash' => array( 'handle_bad_hash', 10, 1 ), 'auth_cookie_valid' => array( 'handle_valid', 10, 2 ), ); } }

D'autre part, Login_Error , qui a besoin d'actions et de filtres, n'a qu'à implémenter les deux interfaces. Les classes peuvent implémenter plus d'une interface en les séparant par une virgule.

 class Login_Error implements Actions, Filters { public function get_actions() { return array( 'login_head' => array( 'add_errors', 10, 1 ), ); } public function get_filters() { return array( 'authenticate' => array( 'track_credentials', 10, 3 ), 'shake_error_code' => array( 'add_error_code', 10, 1 ), 'login_errors' => array( 'format_error_message', 10, 1 ), ); } }

Maintenant que nous avons séparé notre interface, il nous suffit de mettre à jour la méthode register() de Hooks_Manager pour refléter nos modifications.

 class Hooks_Manager { /** * Register an object. * * @param object $object */ public function register( $object ) { if ( $object instanceof Actions ) { $this->register_actions( $object ); } if ( $object instanceof Filters ) { $this->register_filters( $object ); } } // ...

De cette façon, nous appelons conditionnellement uniquement register_actions() , uniquement register_filters() , ou les deux, en fonction de la ou des interfaces que l'objet spécifié implémente.

Pour utiliser réellement le gestionnaire de hooks :

 $hooks_manager = new Hooks_Manager(); $hooks_manager->register( $login_error ); $hooks_manager->register( $cookie_login );

C'est ça! Nous pouvons maintenant utiliser cet objet pour gérer les crochets sur l'ensemble de la base de code.

Conclusion

Bien sûr, il existe plusieurs manières de gérer vos hooks de manière orientée objet, nous venons de vous en montrer une. Vous devriez expérimenter et trouver celui qui correspond à vos besoins.

Restez avec nous pour la dernière partie de cette série, où nous verrons comment gérer les options de manière orientée objet, parler d'encapsulation, d'abstraction et comment découpler vos classes pour créer un plugin flexible et facile à étendre !

Cliquez ici pour lire la partie 8 de notre série sur la programmation orientée objet

Voir également

  • WordPress et la programmation orientée objet - Un aperçu
  • Partie 2 – WordPress et la programmation orientée objet : un exemple concret
  • Partie 3 – WordPress et la programmation orientée objet : Α Exemple WordPress – Définition de la portée
  • Partie 4 – WordPress et la programmation orientée objet : un exemple WordPress – Conception
  • Partie 5 – WordPress et la programmation orientée objet : un exemple WordPress – Implémentation : le menu Administration
  • Partie 6 – WordPress et la programmation orientée objet : un exemple WordPress – Implémentation : enregistrement des sections