Supprimer un filtre/action défini dans une fonction ou une méthode de classe dans WordPress

WordPress permet de définir des crochets hooks pour interagir sur le code. Ces crochets permettent de déclencher des filtres et/ou des actions.

Le logo WordPress, les textes hook, action, filter sur fond de Cove of Spires dans le parc national des Kenai Fjords en Alaska, États-Unis.

Les filtres

Un filtre sert à modifier une valeur, une chaîne, un tableau, un objet défini dans le code. Il retourne toujours cet élément qu’il reçoit en premier argument. Si c’est un tableau, un objet, on peut altérer, ajouter, retirer, ordonner certains items. Si c’est une valeur ou une chaîne, on peut la modifier ou l’enrichir avant de la renvoyer.

Les filtres sont définis dans le code par la fonction apply_filters( string $hook, mixed $value ). $hook est une chaîne de caractère unique qui permettra d’appeler le filtre. De nombreuses extensions peuvent appeler le même filtre, c’est pour cela qu’on appelle un filtre avec une certaine priorité. Plus la priorité est basse, plus le filtre est appelé tôt.

Par exemple, le développeur d’un thème veut afficher un titre sur une page, il définit une variable $title qui contient le titre. Ensuite, dans une page modèle template, il affiche ce titre :

Si le développeur place un filtre sur le titre, un programmeur lambda pourra changer ce titre dans un thème ou une extension :

Le titre peut être modifié en ajoutant une fonction de rappel sur le hook my_unique_hook_title, qui prend en argument la chaîne de caractère $title :

prefix_callback est le nom de la fonction qui va récupérer le titre $title, faire des opérations dessus, et le renvoyer. Lorsque le titre sera affiché, il sera passé par la fonction prefix_callback. En exemple, on ajoute la chaîne « au pieu » au bout du titre et on renvoie ce nouveau titre modifié :

Le nom de la fonction prefix_callback doit être unique, comme toute fonction dans un code php. On ajoute souvent un préfixe identique pour toutes les fonctions d’un thème ou d’une extension.

Si les fonctions de retour callbacks sont définies dans une classe, on n’a plus le problème de nommage de la fonction de retour grâce à l’encapsulation des données. On a le choix de définir cette fonction sans mot clé, c’est à dire de façon dynamique, ou avec le mot clé static. Pour fonctionner avec les filtres WordPress, le statut doit être public. Lorsque la fonction est statique, elle n’est pas appelée avec une instance de classe (définie avec l’intruction new), mais comme membre d’une classe avec la notation deux-points ::. Dans WordPress, on passe l’instance (ou la classe) et la méthode dans un tableau. Dans une classe, la variable $this représente une instance de la classe elle-même. La constante magique __CLASS__ est le nom de la classe courante.

Les actions

Les actions font partie du même concept dans WordPress, elles permettent d’interagir sur le code, sans changer le code initial. Contairement aux filtres, les actions ne retournent rien, mais effectue une action. Cela peut être une action sur les données, ou par exemple une écriture dans la page html générée par le code php. Ces actions sont définies par le premier développeur, dans le code avec la fonction do_action( string $my_unique_hook_action, mixed $arg ). La chaîne de caractère $my_unique_hook_action doit être unique, $args sont des arguments supplémentaires et optionnels qui peuvent être passés à la fonction de retour pour apporter des informations supplémentaires au développeur utilisant l’action.

En reprenant l’exemple précédent qui affiche un titre dans un fichier modèle template, le développeur initial définit une action pour permettre d’afficher quelque chose en dessous du titre :

Le développeur lambda, s’il veut ajouter quelque chose en-dessous le titre, ajoutera une action en se référençant au crochet hook dans un thème ou un plugin :

On peut aussi définir ces actions et ces fonctions de retour dans des classes. On ne sera plus soumis à l’unicité du nom hors de la classe, ces fonctions peuvent être dynamiques ou statiques :

Les priorités

Les actions et les filtres peuvent être ajoutés par de multiples extensions ou fonctions internes de thème ou du noyau core. Ils sont ajoutés les uns après les autres à chaque fois que le code trouve une fonction add_action() ou add_filter(). Chaque action ou filtre a une priorité. Plus la priorité est élevée, plus la fonction de retour sera exécutée tard dans le code. La priorité par défaut si elle n’est pas définie dans add_action() ou add_filter() est 10. Les fonctions de retour des actions ayant le même crochet hook et la même priorité seront exécutées dans l’ordre où elles ont été définies dans le code par add_action(). Le code des plugins s’exécute avant le code du thème. Le code d’un thème enfant dans functions.php s’exécute avant celui du thème parent. Un thème parent bien codé possède des fonctions dîtes pluggable. Si la fonction existe alors la fonction ne sera pas redéfinie :

Pour reprendre l’exemple précédent, on définit des priorités dans des actions avec le même hook. Il est bien entendu plus raisonnable de faire cela dans la même fonction de retour, mais c’est pour illustrer la notion :

Le résultat dans le fichier de modèle sera le suivant :

Supprimer un filtre/action défini précédemment

Les filtres et les actions définies dans des extensions peuvent être supprimés avec les fonctions remove_filter() et remove_action(). Ces fonctions prennent en arguments le $hook crochet, le nom de la fonction de retour et la priorité. Si la priorité est omise, alors ce sera la valeur par défaut 10.

Pourquoi vouloir supprimer des actions ou des filtres alors que l’on peut simplement effacer le code qui les a ajoutés ? Simplement car à chaque mise à jour de plugin, il faudra à nouveau supprimer l’instruction dans le code mis à jour. Bien entendu, on ne s’amuse pas à ajouter un filtre et à le supprimer ensuite dans la même extension ou le même thème que l’on développe soi-même.

Si la fonction de retour est définie hors d’une classe, ces fonctions fonctionnent parfaitement. remove_action() est juste un alias de la fonction remove_filter(). Les actions et les filtres sont enregistrées dans une variable globale $wp_filter. Une fois que toutes les actions, filtres ont été enregistrés, leurs fonctions de retour sont exécutées.

Par exemple, si l’on veut supprimer l’action suivante definie dans un plugin ou un thème sur le hook my_unique_hook_action avec la fonction de retour prefix_callback_action :

On écrit alors :

Il faut bien choisir l’endroit où l’on écrira cette instruction. Si cette instruction est écrite avant que l’action soit ajoutée avec add_action(), elle n’aura aucun effet. Si cette instruction est écrite dans une fonction de retour, on peut utiliser le hook init ou admin_init côté administration, exécuté avant les autres fonctions de retour des filtres et actions :

Cette fonction remove_action() doit être exécutée après que l’action ait été ajoutée, mais avant qu’elle ne soit exécutée. Pour illustrer cela, on peut supprimer une action dans une fonction de retour avec le même hook que celui qui a ajouté la première fonction à supprimer. Mais attention aux priorités.

Prenons un plugin qui définit un hook my_action_hook et une fonction de retour my_callback_action avec la priorité 20 :

Il est plus simple d’utiliser le hook init, mais l’on peut très bien utiliser le hook my_action_hook avec une priorité inférieure pour que la fonction puisse supprimer l’entrée dans la variable $wp_filter avant qu’elle ne soit exécutée :

Ainsi la fonction my_callback_remove_action() sera exécutée avant la fonction my_callback_action(), qui ne sera pas exécutée puisque l’action aura été supprimée juste avant. La priorité 20 dans remove_action() est indispensable, si elle est omise, elle prend par défaut la valeur 10.

Suppression d’une méthode statique de rappel, définie sur un hook

Lorsqu’un filtre/action est ajouté sur hook, et est défini dans une classe d’un plugin, et que la fonction de rappel est une méthode de type static, il faut indiquer le nom de la classe dans le tableau donné en deuxième argument de la fonction remove_filter()remove_action() n’est qu’un alias de la fonction remove_filter(). L’action est définie de la façcon suivante :

On supprime l’action de la façon suivante :

ou dans le hook init :

Suppression d’une méthode dynamique de rappel, définie sur un hook

Et là, cela se complique vraiment car la variable globale $wp_filter, qui contient le tableau de tous les filtres et fonctions de rappel possède une clé avec un identifiant devant le nom de la fonction de rappel de ce filtre. Prenons un exemple. Un plugin ajoute une action sur le hook wp_loaded dans son constructeur __construct() :

La méthode définie par le hook wp_loaded est exécutée après le chargement des plugins qui se fait sur le hook plugins_loaded. Donc on peut définir dans le constructeur de notre classe la suppression de l’action. Le problème réside dans le fait que la classe est instanciée avec l’instruction new et possède un identifiant unique qu’il est impossible de prédire. Il faut donc boucler sur l’objet WP_Hook $wp_filter['wp_loaded']. Cet objet est de la forme :

$wp_filter est de cette forme depuis la version 4.7 de WordPress. La clé 10 du tableau de la propriété callback est la priorité. On va donc boucler dessus, et tester le type de la sous-clé function. Si c’est une chaîne, c’est une fonction définie hors d’une classe, si c’est un tableau, c’est une méthode définie dans une classe. La valeur function[0] est soit un tableau pour une méthode statique, soit un objet pour une méthode dynamique lancée sur une instance de classe. On teste le nom de la classe sur l’objet function[0] que l’on obtient avec la fonction php get_class(). On supprime alors la valeur $wp_filter[$hook]->callbacks[$priority][$key], $key étant la clé dans une boucle foreach sur le tableau $wp_filter[$hook]->callbacks[$priority] et $priority est la priorité.

J’ai écrit une fonction qui permet de faire cela, elle permet en outre de supprimer tout type de filtre. Il faut juste donner en argument un tableau définissant le hook, la classe class et la méthode method ou bien la fonction function, et la priorité priority.

https://gist.github.com/xavier-bs/434770c66269b74de3ef862642e9255c

On intègre la fonction dans le fichier php qui définit notre plugin, ou dans le fichier functions.php de notre thème.

Ordre d’exécution des hooks

Les hooks sont d’abord classés par ordre de définition, c’est-à-dire la première fois qu’ils ont été définis. Ensuite, un même hook peut avoir plusieurs fonctions de rappel callback, celles-ci sont exécutées en fonction de leur priorité. Par exemple, une fonction de rappel ayant une priorité 10 sur un hook est exécutée avant une fonction de rappel sur le même hook avec priorité 20. Si la priorité est la même, alors c’est la fonction de rappel définie en premier qui aura sera exécutée avant. Le noyau core de WordPress définit et exécute aussi des hooks et des fonctions de rappel dans lesquels peuvent être définis d’autres hooks du core. Ces hooks permettent d’exécuter d’autres fonctions de rappel, définis dans une extension ou un thème. Il est donc bien difficile d’établir une hiérarchie. Quoiqu’il en soit, sur un même hook du core tel init, ce sont les fonctions de rappel des hooks définies dans les extensions, ensuite le thème, thème enfant s’il est défini, et le thème parent si les priorités sont identiques.

Ces hooks peuvent être listés en regardant la variable globale $wp_filter mais cela risque de largement dépasser les capacités mémoire autorisées par le serveur web http php Apache, nGinx ou autre. Sur un site commun avec l’extension wooCommerce et un certain nombre d’autres plugins, je liste 922 entrées de hooks sur la page /wp-admin sur le backend et 2075 sur la page d’accueil du frontend, c’est absolument énorme.

L’ordre d’exécution de quelques filtres/actions sur le frontend est le suivant :

hooktypeaction
initactionGénéralement utilisé par les plugins pour s’initialiser. L’utilisateur est déjà authentifié.
the_contentfiltreFiltre le contenu d’une publication.
wp_headactionÉcrit des scripts, des styles, des données dans la balise head du frontend.
template_redirectactionAvant de spécifier le template à charger.
wp_footeractionEcit des scripts et des données avant la balise de fermeture </body> sur le frontend.
plugins_loadedactionAprès que les plugins aient été chargés.
admin_initactionInitialise le backend et les fonctions ajax.
setup_themeactionAvant que le thème soit chargé.
wp_loadedactionAprès que WordPress soit entièrement chargé.
wp_enqueue_scriptsactionPlace les scripts et les styles en file d’attente.
after_setup_themeactionS’exécute pendant l’initialisation des thèmes et est généralement utilisé pour effectuer des actions de configuration, d’enregistrement et d’initialisation pour un thème.
wpactionS’exécute après que la requête a été analysée et que les posts ont été chargés, mais avant toute exécution de template, dans la fonction principale de WordPress wp(). Utile si vous devez avoir accès aux données de publication mais que vous ne pouvez pas utiliser de templates pour la sortie. Argument de la fonction de rappel : l’objet WP $wp par référence. On peut en particulier définir des cookies à cet endroit.

admin_init est aussi présent sur le frontend car il est lancé pour les requêtes ajax lorsque l’utilisateur est connecté wp_ajax_$action, init est lancé en premier car il est aussi utilisé dans des plugins, sinon il est déclenché après after_setup_theme, de même pour le filtre the_content. Quoiqu’il en soit, il faut vraiment tester les actions et les filtres pour savoir lequel se déclenche avant un autre, sinon on joue sur la priorité d’un même hook.

Et hop, on peut supprimer des hooks et des fonctions de rappel définis de manière dynamique dans des classes de plugins.

1 commentaire

  1. Bonjour,
    J’ai installé un filtre en short code pour afficher les 3 derniers posts citations d’une catégorie dans une section d’élémentor sur la page Home.
    Ce que je souhaite c’est si je clique sur n’importe quelle citation de la home , je parvienne sur une page où sont réunies toutes les citations sous forme de blog… et que les articles individuels ne soient pas accessibles…
    En fait si je clique sur le lien du texte ou du titre d’une citation sur la home, j’atterrit sur la page de l’article ce qui à peu d’intérêt… J’ai réussi à mettre un lien sur le texte de la citation qui donne sur une page « blog » de cette catégorie mais le lien du titre renvoie toujours sur l’article…J’ai beaucoup cherché et essayé beaucoup de chose avec des liens et des ancres… Et j’ai regardé votre article sur les filtres mais je n’ai pas compris grand chose…  J’ai essayé de supprimer « get-permalink de cette ligne mais cela ne fonctionne pas:
    ‘<a class= »titre » href= »‘ . get_permalink() . ‘ »> ‘. get_the_title() .get_the_content(). ‘</a>’ . ‘</li>’;endwhile;
    Je suis sur océanWp et j’utilise Gutemberg… et si vous avez une idée cela m’aiderais beaucoup…
    mon site n’est encore en ligne je suis en local
    Je vous remercie

Soumettre un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Ce site utilise Akismet pour réduire les indésirables.