Modifier et étendre WordPress avec les hooks

Le système de hook a été imaginé par WordPress pour permettre la modification ou l’ajout de fonctionnalités.

Cet article s’adresse aux personnes avec une connaissance du dev où qui n’ont pas peur de mettre les mains dans le code. J’avoue ne pas avoir touché à WordPress depuis un bout de temps, il existe peut-être maintenant d’autres hooks plus appropriés

Dans cet article je vais donner beaucoup d’exemples et citer des cas d’utilisation concrets que j’ai déjà pu rencontrer dans des situations professionnelles.

Pour commencer, c’est quoi un hook WP ?

WordPress est un CMS et il est fortement déconseillé de modifier les fichiers de base de celui-ci sous peine de voir vos modifications écrasées à la prochaine mise à jour.

Pour laisser la possibilité de personnaliser votre site, un système de hooks a été mis en place par WordPress qui va permettre aux développeurs d’exécuter du code à certains moments précis afin de modifier le rendu ou le comportement d’une partie du code : ce qu’on appelle les filtres.
Il permet aussi d’ajouter des fonctionnalités ou d’effectuer des traitements particuliers lors d’évènements : ce qu’on appelle les actions.

Les hooks sont tous ces filtres et toutes ces actions disponibles sur WordPress.

Les utilisateurs n’ont pas forcément connaissance de ces hooks, mais, en simplifiant à l’extrême, tous les thèmes et extensions fonctionnent grâce à ceux-ci.

Lorsqu’un visiteur accède à une page de votre site, WordPress va traiter la demande, exécuter le code PHP et afficher le rendu de la page. C’est le fonctionnement classique d’un site internet.

Quand il exécute le code PHP, à divers endroits WordPress va trouver des hooks (des fonctions prédéfinies) et va regarder si une fonction y est accrochée : d’où le terme hook. S’il trouve une fonction, il l’exécute aussi. S’il trouve plusieurs fonctions ajoutées au même hook, il les exécute toutes à la suite avec un ordre de priorité définissable.

On trouve ces hooks dans le cœur de WordPress mais aussi dans les thèmes et plugins. C’est une bonne pratique en tant que développeur d’ajouter des hooks à nos extensions et thèmes WordPress. Cela permettra à d’autres développeurs de modifier le comportement de notre extension (ou thème) pour l’ajuster à leur besoin ou celui de leurs clients.

Comme on retrouve ces hooks partout, il est difficile de bien les documenter. Généralement je regarde le code concerné pour voir s’il y a un hook que je peux utiliser. Je détaillerai ma façon de procéder plus bas.

Le site Hookr.io qui n’est plus maintenu, listait les filtres et actions disponibles dans le cœur de WordPress ainsi que dans les plugins et thèmes populaires. Pour vous donner un ordre d’idée, Hookr.io recensait 1674 filtres et 864 actions seulement pour le cœur de WordPress en version 4.7.4 (la dernière qui était recensée sur ce site).

Attention : Dans les exemples de code, les chaînes de caractères seront écrites en dur, sans prendre en compte les problématiques d’i18n. Ce n’est pas une bonne pratique à suivre mais ce n’est pas le sujet de l’article.

Comment utiliser les filtres WordPress ?

Les filtres permettent de modifier la valeur d’une variable.

Le développeur doit le prévoir en utilisant la fonction apply_filters() (source) et pour s’accrocher au hook avec mon code personnalisé, j’utilise la fonction add_filter() (source) :

$title         = "Titre de mon article";
$title_modifie = apply_filters( 'nom_du_hook', $title );

echo $title_modifie;

Ici avec apply_filters() j’indique à WordPress : « Lorsque tu arriveras à cette ligne, regarde s’il y a une fonction accrochée au filtre nom_du_hook et exécute la si c’est le cas ».

Par défaut, la variable $title_modifie vaudra $title, soit « Titre de mon article » si aucune fonction n’est accrochée à mon hook nom_du_hook.

Si, sur mon site, je veux modifier $title_modifie pour qu’il affiche « Le super titre » je dois ajouter un peu de PHP, dans le fichier functions.php de mon thème, dans un plugin, n’importe où en fait. Lisez l’article qui explique comment ajouter du code WordPress si vous avez un doute.

add_filter( 'nom_du_hook', 'modification_titre_article' );

function modification_titre_article( $titre ) {
    return "Le super titre";
}

Lorsque WordPress exécutera le code PHP pour afficher la page, il ira vérifier si j’ai accroché une fonction au filtre ‘nom_du_hook’. C’est le cas puisque j’ai accroché la fonction modification_titre_article(). Il va donc l’exécuter et assignera à $title_modifie la valeur que j’ai retourné. Dorénavant mon titre modifié sera « Le super titre ».

Lorsqu’un développeur insère un filtre il peut fournir des paramètres supplémentaires s’il juge cela utile pour ajouter du contexte. Dans mon exemple plus haut, je peux modifier le titre mais je ne sais pas si c’est un article, une page, un menu, un custom post type. Admettons que c’est un article de blog, je ne sais pas non plus de quel article il s’agit.

En partant du même exemple pour modifier le titre de l’article, cela peut être pratique de passer l’identifiant unique du post actuel.

$title_modifie = apply_filters( 'nom_du_hook', $post->post_title, $post->ID );

Maintenant dans mon fichier functions.php je peux modifier le titre en ajoutant des conditions. Ici j’ajoute la date de publication devant, seulement si c’est un article de blog.

add_filter( 'nom_du_hook', 'modification_titre_article', 10, 2 );

function modification_titre_article( $titre, $post_id = null ) {
    if ( 'post' === get_post_type($post_id) ) {
        $titre = "[" . get_the_date( 'Y', $post_id ) . "] " . $titre;
    }

    return $titre;
}

Je dois toujours retourner une valeur, même si ce n’est pas un cas qui me concerne. Sinon pour tous les cas où je ne veux rien toucher je vais vider la valeur initiale.

Cette fonction (que j’avoue n’avoir pas testé, je croise les doigts pour qu’elle fonctionne) ajoute l’année de publication, entre crochet, devant le titre de l’article avec seulement 7 lignes de PHP. Si ce n’est pas un article, elle ne touche pas au titre et le renvoie en l’état.

Incroyable ! WordPress prévoit justement un hook pour cela, qui s’appelle the_title (source).

Dans mon code d’exemple au dessus, lors de mon add_filter(), plutôt que d’accrocher ma fonction à un hook imaginaire qui s’appelle nom_du_hook, si je l’accroche au hook officiel de WordPress qui s’appelle the_title cela fonctionne sur mon site immédiatement.

Ce hook the_title est utilisé par les fonctions de base de WordPress qui sont the_title() et get_the_title(). Si j’affiche comme un gros dégueulasse le titre de mon article avec $post->post_title, le titre affiché ne passe pas par les hooks de WordPress et devient alors impossible à modifier par une extension. Voilà pourquoi c’est important de se servir des fonctions que propose WordPress.

Cas concret d’utilisation des filtres du cœur de WordPress

Voici des cas pratiques que j’ai déjà utilisés, je suis un peu rouillé alors certains ne sont peut être plus adaptés :

  • Le filtre post_gallery permet de retourner le code HTML que l’on souhaite afficher lorsqu’on utilise le shortcode gallery natif de WordPress. On retrouve le filtre dans le fichier wp-includes/media.php avec des commentaires juste au dessus.
  • Le filtre the_content est utile pour modifier le contenu d’un post juste avant de l’afficher. Par exemple : Si on est sur une page article, j’ajoute automatiquement un lien vers https://google.fr lorsque je trouve l’occurrence Google dans le contenu de l’article. Ce filtre est visible dans le fichier wp-includes/post-template.php.
  • Le filtre the_title que j’ai cité plus haut, pour modifier le titre d’un post, est présent dans le même fichier à la ligne 171.
  • Les SEO connaissent ce souci, les titres des widgets en sidebar sont par défaut des <h2> c’est pas beau ! Vous pouvez utiliser le filtre register_sidebar_defaults (source) pour modifier les paramètres before_title et after_title et remplacer l’ouverture et la fermeture du h2 par un paragraphe. Ce filtre modifie seulement les paramètres par défaut, s’ils ne sont pas précisés dans le thème lorsque celui-ci déclare une nouvelle sidebar.

Exemples de filtres

Interpréter les shortcodes dans les widgets texte

Dans ce cas, tout existe déjà nativement dans WordPress il faut juste forcer l’exécution de la fonction do_shortcode() sur les widgets texte.

add_filter( 'widget_text', 'do_shortcode' );

Ajouter du texte dans le contenu d’un article

On ajoute une condition pour ajouter ce texte seulement sur les articles de blog.

add_filter( 'the_content', 'ajout_date_debut_contenu' );
add_filter( 'the_content', 'ajout_fin_contenu' );

function ajout_date_debut_contenu( $content ) {
    // On s'assure d'être bien sur un article en étant sur la boucle principale
    if ( is_singular('post') && in_the_loop() && is_main_query() ) {
        $content = "<p>Article rédigé le " . get_the_date() . "</p>n";
    }
	
    return $content;
}

function ajout_fin_contenu( $content ) {
    if ( is_singular('post') && in_the_loop() && is_main_query() ) {
        $content .= "n<p>Cet article a été rédigé avec amour.</p>";
    }

    return $content;
}

Vous pouvez aussi n’utiliser qu’une seule fonction pour ne pas répéter vous répéter, tout dépend de ce que vous souhaitez faire.

add_filter( 'the_content', 'ajout_contenu_avant_apres' );

function ajout_contenu_avant_apres( $content ) {
    if ( is_singular('post') && in_the_loop() && is_main_query() ) {
        $content = "<p>Article rédigé le " . get_the_date() . "</p>n$contentn<p>Cet article a été rédigé avec amour.</p>";
    }

    return $content;
}

Si le code que vous écrivez servira uniquement pour votre site, je ne vois aucun inconvénient à utiliser une seule fonction. Dans le cas contraire, je pense qu’il est préférable de séparer en deux fonctions distinctes. De cette façon, un utilisateur pourra enlever le contenu ajouté en début d’article en écrivant remove_filter( 'the_content', 'ajout_date_debut_contenu' ); dans son fichier functions.php sans toucher au contenu ajouté automatiquement en fin d’article.

C’est le hook utilisé par l’extension YARPP pour afficher des articles similaires à la fin des posts.

Modifier les balises des titres des widgets

Encore l’exemple d’un cas cité plus haut pour remplacer les h2 des widgets par des p seulement si le thème précisé du HTML particulier.

add_filter( 'register_sidebar_defaults', 'remplacer_tag_title' );

function remplacer_tag_title( $args ) {
    $args["before_title"] = "<p class="widgettitle">";
    $args["after_title"] = "</p>n";
    
    return $args;
}

Lors de mon test sur un site de démo, le thème utilisé avait défini une balise HTML précise et ne donnait aucun moyen de la modifier via un filtre.

Il est alors possible, quand même, de modifier la balise au moment de l’affichage des widgets avec le code suivant. Je fais bien attention à reprendre le code HTML que le thème utilise ainsi que les classes CSS.

add_filter( 'dynamic_sidebar_params', 'remplacer_tag_title_a_laffichage' );

function remplacer_tag_title_a_laffichage( $params ) {
    $params[0]["before_title"] = "<p class="widget-title widget-title-1"><span class="header-after">";
    $params[0]["after_title"] = "</span></p>n";

    return $params;
}

Se servir des actions WordPress

Les actions sont très proches des filtres, WordPress les traite de la même manière. D’ailleurs la fonction add_action() de WordPress ne fait qu’appeler add_filter(). Les actions ne servent pas à changer une valeur mais à ajouter des traitements à exécuter à certains moments.

Concrètement, dans les filtres on devait retourner une valeur alors qu’une action ne retourne rien, elle fait quelque chose.

Côté développeur, pour déclarer une nouvelle action à WordPress on utilise la fonction PHP suivante :

do_action( 'mon_action' );

Côté utilisateur, sur mon site, je peux ajouter un traitement sur cette action avec le code qui suit :

add_action( 'mon_action', 'envoyer_un_email' )

function envoyer_un_email() {
    wp_mail( "monadresse@example.com", "Sujet : L'action mon_action est déclenchée", "Message : On vient de déclencher l'action mon_action." );
}

Je dis à WordPress : « Lorsque tu arrives à l’action mon_action je veux que tu envoies un email. »

L’exemple concret le plus simple, c’est l’ajout du fichier style.css dans le thème. C’est le code par défaut à écrire dès la création d’un thème :

add_action( 'wp_enqueue_scripts', 'add_theme_scripts' );

function add_theme_scripts() {
  wp_enqueue_style( 'style', get_stylesheet_uri() );
}

Je demande à WordPress de lancer la fonction add_theme_scripts() qui ajoute le fichier style.css du thème lorsqu’il arrive à l’action wp_enqueue_scripts. On retrouvera ensuite le fichier style.css du thème à l’intérieur de la balise <head> des pages du site.

Tout comme les filtres, WordPress permet aux développeurs d’ajouter des paramètres pour contextualiser : do_action( 'save_post', $post_ID, $post, $update ); (source).

Attention : Il existe aussi des actions avec des variables : save_post_{$post->post_type} (source). Cela signifie que je peux directement exécuter une action sur un type de post particulier. Si je veux lancer mon action lorsque c’est une page qui est sauvegardée (et non pas un article de blog) je peux utiliser le hook save_post_page. Si je veux cibler les custom post types ‘product’ je peux appeler save_post_product, etc.

Cette action est déclenchée par WordPress automatiquement juste après la création ou la mise à jour d’un post (article, page, custom post type), lorsqu’il est sauvegardé avec wp_insert_post() par exemple.

L’action fournit 3 paramètres qui sont l’ID du post, l’objet WP_Post et un booléen qui vaut true si c’est une mise à jour et false si c’est un nouveau post.

Encore une fois, sauf quelques exceptions, c’est à l’aide de ces actions qu’on développe un plugin WordPress.

Voici une exception : Le fil d’ariane de Yoast est ajouté avec la fonction yoast_breadcrumb() que je dois placer dans mon thème à l’endroit où je veux l’afficher. Je ne peux pas utiliser un hook car il n’existe pas forcément d’action disponible à l’endroit exact où je le veux, chaque thème étant dépendant du ou des développeurs qui l’ont codé.

Cas concret d’utilisation des actions du cœur de WordPress

Je sais qu’il est difficile de visualiser l’intérêt des hooks alors je vais vous donner quelques exemples d’usages du hook save_post dont je parlais plus haut :

  • J’ai développé un plugin de cache qui enregistre toutes les pages en HTML pour accélérer le chargement. A la mise à jour ou la création d’un post, mon cache est périmé puisqu’un article à changé. J’utilise cette action pour supprimer le cache et le régénérer. Et je peux aller plus loin avec les paramètres. Par exemple pour ne supprimer le cache que des pages concernées ainsi que les archives des catégories associées.
  • Je propose à mes visiteurs de s’abonner à une catégorie de mon blog pour recevoir les nouveaux articles par email. J’écoute donc le déclenchement de l’action save_post, si $update est à true je ne m’y intéresse pas car je veux seulement les nouveaux articles. Il me suffit ensuite de regarder si c’est bien un article et si la catégorie correspond. Si elle correspond, j’envoie un email.
  • Sur mon site j’ai une liste de films. Lorsque je crée un nouveau film, je veux faire une requête API pour récupérer les informations liées au film et remplir des champs custom que je souhaite afficher sur mon site (producteurs, acteurs, image du film, etc).

Voici quelques autres actions utiles en dehors de save_post :

  • Un classique : pre_get_posts permet de modifier la requête avant que WordPress aille chercher les posts à afficher en base de données. Par exemple, si vous souhaitez afficher uniquement les articles à la une en page d’accueil, plutôt que de créer dans le thème une nouvelle requête, vous pouvez en quelques lignes modifier la requête principale.
  • wp_enqueue_scripts évidemment pour ajouter des scripts JS ou CSS dans le header ou footer de votre site.
  • On retrouve aussi wp_head pour ajouter des éléments dans le header du site, un tag Analytics par exemple.

Exemples d’actions

Voici quelques bouts de code pratiques à mettre dans le fichier functions.php ou dans un plugin ou mu-plugin.

Rechercher uniquement dans les articles de blog

Comme je vous le disais, modifier la requête principale (on l’appelle la loop) est très pratique. Ce code permet de ne chercher par défaut que dans les articles de blog lorsqu’on effectue une recherche sur le site.

add_action( 'pre_get_posts', 'search_posts_only' );

function search_posts_only( $query ) {
    // Si on est sur la loop et sur une page de recherche
    if ( $query->is_main_query() && is_search() ) {
	$query->set( 'post_type', 'post' );
    }
}

Afficher seulement les sticky posts

Je veux afficher uniquement les articles à la une (sticky posts) sur la page d’accueil, toujours avec l’action pre_get_posts :

add_action( 'pre_get_posts', 'sticky_home' );

function sticky_home( $query ) {
    // Si on est sur la page d'accueil "blog" et que c'est la loop
    if ( ! is_admin() && $query->is_main_query() && is_home() ) {
        $sticky_posts = get_option( 'sticky_posts', [] );
        $query->set( 'post__in', $sticky_posts );
    }
}

Ajouter une balise meta dans le header

Cette meta me garantira la première position pour toutes mes pages, j’en suis sûr c’est Djibou qui me l’a dit.

// Priorité de 0 pour l'avoir le plus haut possible dans mon header
add_action( 'wp_head', 'add_custom_meta', 0 );

function add_custom_meta() {
    echo '<meta name="position" content="1" />';
}

Forcer l’utilisation d’un SMTP pour les mails

Ici je force le SMTP de mailtrap pour envoyer les mails WordPress, que je peux appliquer uniquement si la constante WP_DEBUG est à true par exemple.

add_action( 'phpmailer_init', 'mailtrap' );

function mailtrap( $phpmailer ) {
    if ( true !== WP_DEBUG ) {
        return;
    }

    $phpmailer->isSMTP();
    $phpmailer->Host = 'smtp.mailtrap.io';
    $phpmailer->SMTPAuth = true;
    $phpmailer->Port = 2525;
    $phpmailer->Username = '***********'; // A remplacer par ce que donne mailtraip
    $phpmailer->Password = '***********'; // A remplacer par ce que donne mailtraip
}

Ajouter une colonne dans l’admin de la liste des posts

On va avoir besoin d’un filtre et d’une action pour ajouter une colonne sur la liste des articles dans l’admin, avec le nombre de mots du contenu (je le fais le plus basique possible et je ne sais pas comment ça réagit avec Gutenberg). Le filtre ajouter une entête à la liste des entêtes des colonnes du tableau, l’action affiche la valeur dans chaque ligne.

/**
 * Je déclare une nouvelle colonne au tableau 
 */
add_filter( 'manage_post_posts_columns', 'add_column_words_count' );

function add_column_words_count( $columns ) {
    return array_merge( $columns, array( 'count_words' => 'Mots' ) );
}

/**
 * J'ajoute le contenu pour chaque article 
 */
add_action( 'manage_post_posts_custom_column', 'set_words_count_value', 10, 2 );

function set_words_count_value( $column_key, $post_id ) {
    if ($column_key == 'count_words') {
        $post = get_post($post_id);
        $content_without_html = wp_filter_nohtml_kses($post->post_content);
        echo str_word_count($content_without_html);
    }
}

Les hooks des thèmes et extensions

Comme je le disais, il est fortement conseillé de mettre des filtres et des actions lorsqu’on développe sur WordPress. C’est quelque chose que je regarde pour choisir une extension, car sans hooks il je ne peux pas modifier le comportement ou étendre l’extension.

Takayuki Miyoshi, le développeur de l’extension Contact Form 7 propose aussi le plugin Flamingo pour permettre l’enregistrement en base de données de tous les formulaires CF7 renseignés. Sans hooks dans Contact Form 7, il n’aurait jamais pu proposer Flamingo.

Exemples de hooks d’extensions WordPress

Voici quelques exemples de hooks que j’ai pu utiliser. Attention, encore une fois, je n’ai pas développé activement sur WordPress depuis plus de deux ans ! Peut-être que certaines actions ne sont plus adaptées.

Modifier le destinataire d’un mail avec CF7

add_action( 'wpcf7_mail_components', 'update_recipient', 0, 2 );

function update_recipient( $components, $form ) {
    // Seulement sur le formulaire ID 1840
    if ( 1840 === $form->id() ) {
        $sender = WPCF7_Submission::get_instance()->get_posted_data('your-email');

        if ( "etienne@spam.fr" === $sender ) {
            $components["recipient"] = "spam@dev-wp.fr";
        }
    }

    return $components;
}

Cet exemple est parfait pour répondre à la question de Raph. Ce le tweet qui m’a fait me souvenir que j’avais l’article à publier.

Ce code m’a pris environ 20 minutes à écrire car j’avais déjà fait quelque chose de similaire, j’ai dû retrouver le bon filtre. La première fois on peut compter plus de temps mais une fois qu’on a compris le fonctionnement de CF7 ce n’est pas très difficile.

Ici je vérifie l’ID du formulaire (qu’on trouve dans l’admin). Je récupère les informations renseignées par le visiteur. Si l’email renseigné (champ your-email, celui de base avec le formulaire par défaut de CF7) est etienne@spam.fr je change le destinataire du formulaire car il me saoule trop.

C’est un exemple basique et pas adapté. Dans ce cas précis (spam) il y a un autre filtre plus adapté : wpcf7_spam (source) qui doit retourner true si c’est un spam. Mais imaginez que vous ayez un champ select dans le formulaire pour choisir le sujet du message : « Problème technique », « Demande de devis », « Mes données personnelles ». Vous pouvez envoyer le formulaire au bon destinataire en regardant le sujet sélectionné par le visiteur. Cela demandera 3/4 lignes de code supplémentaires par rapport à mon exemple.

CF7 a aussi une action wpcf7_submit déclenchée juste après l’expédition du mail. On peut retrouver toutes les informations et savoir si le message a bien été envoyé où s’il y avait une erreur.

Je ne vais pas vous mettre de code car c’est trop variable mais c’est le hook parfait pour importer les demandes de contact dans un CRM par exemple 

Forcer le noindex nofollow avec Yoast

add_filter( 'wpseo_robots', 'force_noindex' );

function force_noindex ( $value ) {
  if (is_singular('post')) {
    $value = 'noindex, nofollow';
  }
  
  return $value;
}

Ici, quoi qu’il arrive je force le noindex nofollow sur les articles de blog (grosse astuce SEO au passage). Peu importe ce qu’on cochera dans l’édition de l’article, tous les articles seront en noindex quoi qu’il arrive, on bypass complètement la configuration dans l’admin. Cependant cela peut porter à confusion pour l’utilisateur s’il modifie un paramètre mais que cela ne se répercute pas sur le site.

Recevoir un email à la fin d’un import avec WP All Import

add_action( 'pmxi_after_xml_import', 'email_results', 10, 1 );

function email_results( $import_id ) {
    global $wpdb;
    
    $table = $wpdb->prefix . 'pmxi_imports';
    $data = $wpdb->get_row( 'SELECT * FROM `' . $table . '` WHERE `ID` = "' . $import_id . '"' );
	
    if ( $data ) {
        $msg = "Import Report for Import ID " . $import_id . "rnrn";
        $msg .= "Created: " . $data->created . "rn";
        $msg .= "Updated: " . $data->updated . "rn";
        $msg .= "Skipped: " . $data->skipped . "rn";
        $msg .= "Deleted: " . $data->deleted . "rn";
        
        wp_mail( 'youremail@domain.com', 'Import Report', $msg );
    }
}

Ce code là je l’ai pas du tout testé. J’ai déjà fait des choses plus complexes avec WP All Import mais je n’ai plus le plugin sous la main et je n’ai pas envie de le réinstaller et configurer un import de démo juste pour essayer le code.

Éviter une boucle infinie lorsqu’on veut travailler avec l’action save_post

Je vous ai donné des cas concrets d’utilisation de l’action save_post plus haut. Sur la documentation du hook, WordPress nous met en garde car on peut créer une boucle infinie.

Si on update un champ du post, on va à nouveau appeler le hook save_post, qui passera dans notre fonction, qui va mettre à jour le champ, etc. Il faut donc décrocher temporairement notre fonction. Dans l’exemple suivant (donné par WordPress, je vous ai traduit seulement les explications) tous les posts associés à la catégorie par défaut doivent être en privé.

add_action( 'save_post', 'set_private_categories' );

function set_private_categories( $post_id ) {
    // Si c'est une révision on récupère le post source
    if ( $parent_id = wp_is_post_revision( $post_id ) ) {
        $post_id = $parent_id;
    }
 
    // On récupère l'ID de la catégorie par défaut
    $defaultcat = get_option( 'default_category' );

    // On effectue un traitement seulement si notre post possède la catégorie par défaut
    if ( in_category( $defaultcat, $post_id ) ) {
        // On décroche notre fonction
        remove_action( 'save_post', 'set_private_categories' );
        
        // On update notre post pour forcer le statut privé
        wp_update_post( array( 'ID' => $post_id, 'post_status' => 'private' ) );
        
        // On accroche à nouveau notre fonction
        add_action( 'save_post', 'set_private_categories' );
    }
}

De cette manière on évite une boucle infinie qui se terminera par un timeout.

Annuler une action ou un filtre

Il est parfois utile d’enlever une action ou un filtre effectué le core de WordPress, un thème ou une extension. WordPress nous donne deux fonctions dédiées : remove_action() et remove_filter().

On peut vouloir supprimer la meta generator du header. Elle est ajoutée par WordPress à l’aide de la fonction wp_generator() accrochée à l’action wp_head. Avec ce code je demande à WordPress de ne plus l’appeler.

remove_action( 'wp_head', 'wp_generator' );

Comment trouver les hooks WordPress ?

Il faut avant tout comprendre ce qu’on veut toucher ou modifier. Pour trouver les hooks à utiliser il faut savoir si le code qu’on veut modifier fait partie du cœur de WordPress, d’une extension ou d’un thème.

Voilà par exemple mon cheminement pour trouver le hook qui me permet de modifier le rendu html du shortcode de galerie WordPress :

  1. C’est le code de WordPress de base je cherche alors sur Google « wordpress shortcode gallery »
  2. Grâce à Google (merci !) j’arrive sur la documentation de la fonction gallery_shortcode() (source)
  3. Sur cette page, dans la section « Source » j’ai accès à tout le code de la fonction. Je lis le code et ligne 2046 (au moment où j’écris cet article), je trouve le filtre qui m’intéresse.

Dans le cas de mon exemple avec le plugin Contact Form 7 pour modifier le destinataire, je ne suis pas allé sur la doc du plugin, je suis passé directement dans le code. Le cheminement est plus long :

  1. J’ai cherché « do_action » sur tout le dossier du plugin, ça m’a listé 30 résultats
  2. J’ai regardé le nom des actions pour voir si j’en trouvais une qui me plaisait, j’ai trouvé wpcf7_submit.
  3. J’ai été lire le code de la fonction qui l’utilisait, j’ai créé une fonction dans mon thème qui s’accroche sur cette action. Ma fonction faisait un var_dump() puis un die()
  4. Malgré le die() qui arrete l’exécution du PHP, j’envoyais l’email, donc mon action arrivait trop tard.
  5. Je suis retourné voir du côté du code du plugin, quelques lignes avant le déclenchement de mon action j’ai vu qu’on appelait WPCF7_Submission::get_instance() je suis donc allé à la classe PHP WPCF7_Submission.
  6. Au tout début de la méthode get_instance() je vois qu’on appelle une autre méthode proceed(), submission, proceed, bon ça me parle, je vais voir.
  7. Dans la méthode proceed() je vois qu’on appelle une autre méthode mail() si elle fonctionne je mets mail_sent en status. ça doit être là que j’envoie le mail alors. Je vais voir la méthode mail() (de la classe, pas la fonction mail() de PHP)
  8. Je vois qu’elle appelle WPCF7_Mail::send() je sens que je me rapproche, je vais voir ce que cette classe.
  9. On continue, WPCF7_MAIL::send() appelle compose() alors je vais à quoi elle ressemble.
  10. Trouvé ! Dans compose() je trouve un apply_filters() avec les informations qui m’intéressent. Et comme c’est un filtre, je peux modifier les valeurs !

Cet exemple est un peu plus compliqué je vous l’accorde, comme je vous le disais il faut prendre le temps de comprendre le code du plugin et comment il fonctionne. Il faut fouiller, tester.

La première fois, pour un plugin qu’on ne connaît pas, ça prend du temps. Mais ce n’est pas du temps perdu, on en gagne énormément ensuite, car la solution fonctionne pour tous les sites qui utilisent Contact Form 7, j’évite de casser du code existant et je m’assure que ça ne cassera pas lors d’une mise à jour de l’extension.

Pour l’anecdote, quand j’ai fouillé le plugin Contact Form 7, c’était pour développer un plugin à installer sur tous les sites d’un groupe (entre 10 et 20 sites) pour que les formulaires de contact soient attribués au bon service automatiquement et enregistré dans Hubspot.

L’attribution se faisait en fonction du nom du formulaire, de la page sur laquelle a été soumis le formulaire, de données renseignées par l’utilisateur et parfois de l’emplacement du formulaire dans la page. Le formulaire de contact classique, en sidebar, devait être envoyé au service technique si l’utilisateur était dans la FAQ au moment de la soumission, par exemple.

Conclusion

Les hooks WordPress sont une bénédiction et bien savoir les utiliser permet de gagner énormément de temps. Souvent, je remarque que les utilisateurs installent des plugins que je vais considérer comme inutiles (ça dépend) qui peuvent être remplacés par 10 lignes de PHP.

C’est le cas pour forcer l’utilisation d’un SMTP pour l’envoie des mails WordPress. Mais la mise en place d’un plugin peut être justifiée si on fait un site pour un client afin de lui permettre de modifier ensuite le SMTP.

Pour un site personnel sur lequel on a la main, il y a tout de suite moins d’intérêt. Et alors si on gère un réseau de sites, c’est d’après moi une grosse connerie. Il va falloir ajouter le plugin à chaque site, le configurer dans l’administration, un enfer. Si je veux utiliser le même smtp sur tous les sites, je crée un fichier smtp.php avec le code d’exemple que j’ai utilisé plus haut. Ce fichier je le place dans wp-content/mu-plugins ça l’active par défaut.

PS : Si vous ne savez pas ce que sont les mu-plugins, vous pouvez lire l’article dédié.

L’essentiel n’est pas de tout coder soit même ou de n’utiliser que des extensions, mais de comprendre ce qu’on fait, et faire le choix qui nous semble le plus approprié dans notre contexte.

Avec l’expérience on connaît certains hook par cœur et on ne réfléchit même plus pour travailler dessus. Pour ceux qui touchent un peu au code, n’hésitez pas à vous pencher sur les hooks si vous ne connaissiez pas. Pour les autres, si vous sous-traitez ou employez des dev, ne considérez pas que c’est forcément rapide à faire si la solution tient en quelques lignes. Le nombre de lignes de code n’est pas gage de qualité, et il est possible de passer 2h pour écrire 5 lignes.

Et si vous avez besoin de dev particulier sur vos sites WordPress (ou Laravel) contactez moi, je suis aussi disponible en freelance !


Publié

dans

par

Commentaires

Laisser un commentaire

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