3 questions « piège » sur les hooks WordPress

(1222 mots)

Révélations ?

Disclaimer

Ah ah pas mal l’accroche.

Je sais que tu en sais des choses, peut-être même sur les hooks WordPress. Ici je souhaite évoquer des questions que j’ai eu en donnant des formations et qui ont failli me poser une colle sur le moment, tout simplement parce que je ne les avais pas encore explorées à l’époque.

Donc tu l’auras compris, je vais pas refaire un cours exhaustif sur les hooks, juste rappeler le fonctionnement général et tenter d’aller un peu plus loin.

Rappel

Je vais très vite, rassure-toi.

Le concept

Les hooks sont faits pour modifier, étendre les fonctionnnalités, les données sur un site WordPress.

Le but in fine est de pouvoir personnaliser WordPress sans intervenir dans le coeur du réacteur (les fichiers natifs) ce qui est un pêché mortel, totalement illégal, pas bien, toi pas faire.

Donc quand tu veux faire un thème, des plugins, tu as à ta disposition une flopée d’actions et de filtres (les hooks) pour personnaliser ton projet. Le fait de ne pas toucher aux fichiers natifs directement te permettra de mettre à jour WordPress tout en conservant tes modifications.

add_action() / add_filter()

Techniquement une action ou un filtre c’est presque pareil. add_action() fait un return de add_filter(). Mais afin de ne pas écrire toute une page pour cette partie rappel, disons simplement qu’une action te permet de déclencher ton code à un moment précis : un écran dans l’admin par exemple ou quand tu vas cliquer sur un bouton, ou encore à un moment précis du chargement global de WordPress. C’est possible parce que dans le coeur de WP on a des centaines de do_action() qui font office d’ancre ou de point d’entrée pour nos add_action(). L’équivalent pour les filtres c’est apply_filters().

Le filtre lui te permettra de modifier des données à la volée, par exemple au moment de l’affichage des données récupérées depuis la base ou juste avant d’envoyer des données (ex: formulaire) vers la base.

La technique

L’utilisation des hooks se veut facile, l’idée n’est pas de donner dans l’élitisme, c’est même tout à fait le contraire.

Concrètement :

function exclude_category( $query ) {
    if ( $query->is_home() && $query->is_main_query() ) {
        $query->set( 'cat', '-1' );
    }
}
add_action( 'pre_get_posts', 'exclude_category' )

source

permet d’exclure les articles de la catégorie « non classé » de l’affichage des articles sur la page qui liste les articles.

3 Questions – 3 réponses

il se passe quoi si deux fonctions sont sur un même hook ?

function _the_init_bis() {
    // exécuter du code ici
}
add_action( 'init', '_the_init_bis' );
function _the_init() {
    // exécuter du code ici
}
add_action( 'init','_the_init' );

Lequel des deux codes va s’exécuter en 1er ? La bonne réponse est _the_init_bis(). Ici ce n’est pas lié à WP mais à PHP qui va juste exécuter le code au fur et à mesure. Comme _the_init_bis() est déclarée et ajoutée avant et bien elle sera ajoutée avant. 1er arrivé, 1er servi !

La seule chose qui compte ici c’est l’ordre de :

add_action( 'init', '_the_init_bis');
add_action( 'init','_the_init' );

Maintenant WP a prévu un mécanisme de priorité sous la forme d’un 3ème paramètre à la fonction add_action(). Donc si on reprend l’exemple précédent en mettant :

add_action( 'init', '_the_init_bis', 10 );
add_action( 'init','_the_init',9 );

alors cette fois-ci c’est _the_init() qui passera en 1er. 10 est la valeur par défaut (on peut donc ne pas la mettre), et attention de 0 à 10 on aura une priorité plus élevée alors que de 10 à l’infini on aura une priorité moins élevée. Autrement dit mettre 11 retarde l’execution.

C’est la raison pour laquelle on voit parfois :

function _my_callback() {}
add_action( 'NOM_DU_HOOK', '_my_callback', 999 );

l’auteur du plugin aura souhaité « passer après tout le monde » au cas très probable où un plugin et/ou un thème utiliserait le même hook.

On peut mettre une priorité négative ?

En droite ligne de la précédente question, où on aura compris qu’on peut jouer sur le 3ème paramètre de add_action() (add_filter fonctionne pareil) pour exécuter son code avant ou après au besoin, et puisque toute priorité inférieure à 10 la fait passer avant la valeur par défaut, « pourquoi qu’on » irait pas dans le négatif ?

Imaginons que quelqu’un ait fait :

add_action( 'NOM_DU_HOOK', '_my_callback', 0 );

si moi je veux passer avant :

add_action( 'NOM_DU_HOOK', '_my_callback', -1 );

ça marche ça ? Alors ? tu sais ? Dis pas « non » d’emblée ! tu pourrais avoir des surprises… parce que oui ça marche ^^.

Alors faille du système ? la réponse est non. L’intention est bien de donner la priorité. Même si a priori le core WP ne le fait pas, j’ai pas trouvé d’exemple à l’intérieur de la bécanne native, je pense que certains dévs ont du y penser et le faire.

Mais pourquoi ça marche pas quand je me trompe sur le nombre d’arguments ?

Bon déjà c’est quoi cette histoire d’arguments ? Tu te rappelles des paramètres de add_action() qu’on vient de voir ? on en a vu 3, le nom du hook, le callback PHP et la priorité :

add_action($tag, $function_to_add, $priority = 10, $accepted_args = 1)

Source

bon bah y en a un 4ème, le paramètre $accepted_args que tu dois préciser quand tu utilises plusieurs arguments. Jusqu’à présent on a vu une utilisation avec un callback PHP, un add_action() et roulez.

Mais tu peux accéder à des données si celles-ci sont transmises dans le do_action(). Par exemple :

do_action( 'save_post', $post_ID, $post, $update );

ce qui te permet de faire :

function wpdocs_my_save_post( $post_ID, $post, $update ) {
   // exécuter du code
}
add_action( 'save_post', 'wpdocs_my_save_post', 10, 3 );

et c’est bien pratique car cela te permet d’utiliser les arguments comme autant de moyen d’affiner ta condition et de limiter l’exécution de ton code au(x) seul(s) vrai(s) cas qui t’intéresse.

Jusque là la théorie est à dérouler et c’est simple à utiliser mais que se passera-t-il si tu fais :

function wpdocs_my_save_post( $post_ID, $post, $update ) {
   if ( $update ) {
        // exécuter du code
   }
}
add_action( 'save_post', 'wpdocs_my_save_post', 10, 2 );

à ton avis ? ça marche quand même ?

Bah ça pète !

Fatal error: Uncaught ArgumentCountError

Mais pourquoi ? La réponse se situe dans le fichier wp-includes/class-wp-hook.php à la ligne 288 (à l’heure où j’écris c’est encore le cas, je suis en WordPress 4.9.5).

$value = call_user_func_array( $the_['function'], array_slice( $args, 0, (int)$the_['accepted_args'] ) );

En fait ici, y a un « double effet kiss-kool », à la fois WP te jette mais surtout PHP 7.1. Avant tu avais droit à un avertissement mais depuis c’est une bonne fatale qu’on te renvoie donc attention.

Conclusion

J’espère t’avoir renseigné sur quelques points, pas si compliqués, encore une fois là n’est pas le but, mais qui peuvent poser question quand on découvre WP et même après 😉

Les commentaires sont fermés.

Haut de page ▲