Intermediaire 2 min de lecture · 362 mots

WordPress Hooks : Actions et Filtres expliqués avec exemples

Estimated reading time: 1 minute

Introduction

Le système de hooks (crochets) de WordPress est l’épine dorsale de son architecture extensible. C’est ce qui permet à des milliers de plugins et thèmes de coexister et d’étendre WordPress sans modifier son code source. Comprendre les hooks est essentiel pour tout développeur WordPress intermédiaire.

Dans ce guide approfondi, nous explorerons la différence entre actions et filtres, leur fonctionnement interne, et comment les utiliser efficacement dans vos projets professionnels.

Table des matières

  • Architecture du système de hooks
  • Actions vs Filtres : comprendre la différence
  • Utiliser les actions existantes
  • Utiliser les filtres existants
  • Créer vos propres hooks personnalisés
  • Priorités et ordre d’exécution
  • Hooks conditionnels et contextuels
  • Performance et optimisation
  • Debugging des hooks
  • Cas d’usage avancés et patterns
  • 1. Architecture du système de hooks

    Le pattern Observer

    WordPress implémente le pattern de conception Observer (aussi appelé Publish-Subscribe) :

┌─────────────┐        Fire Event        ┌──────────────┐
│   WordPress │ ──────────────────────> │  Hook System │
│    Core     │                          │   (wp-hooks) │
└─────────────┘                          └──────────────┘
                                                │
                                                │ Notify
                                                ▼
                           ┌────────────────────────────────┐
                           │   Registered Callbacks         │
                           │  (Plugins, Themes, Functions)  │
                           └────────────────────────────────┘

Fonctionnement interne


  Simplifié : structure interne du système de hooks
 /

// Tableau global stockant tous les hooks
global $wpfilter;

/
Structure de $wpfilter:
$wpfilter = array(
    'hookname' => WPHook Object {
        callbacks = array(
            10 => array( // Priorité
                'uniqueid1' => array(
                    'function' => 'callbackfunction',
                    'acceptedargs' => 1
                ),
                'uniqueid2' => array(
                    'function' => array($object, 'method'),
                    'acceptedargs' => 2
                )
            ),
            20 => array( ... )
        )
    }
)
/

Cycle de vie d’un hook

1. addaction() ou addfilter()
   ↓
  • Stockage dans $wpfilter global
  • doaction() ou applyfilters()
  • Tri par priorité
  • Exécution séquentielle des callbacks
  • Retour du résultat (filtres) ou fin (actions)
  • 2. Actions vs Filtres : comprendre la différence

    Actions : Faire quelque chose à un moment précis

    Les actions permettent d’exécuter du code à des moments spécifiques sans modifier de données.

    
      Exemple conceptuel d'une action
     /
    
    // WordPress fait ceci internalement:
    function doactionexample() {
        // Code WordPress...
    
        doaction('monactionpersonnalisee', $arg1, $arg2);
    
        // Suite du code WordPress...
    }
    
    // Vous vous "accrochez" avec:
    addaction('monactionpersonnalisee', 'mafonctioncallback', 10, 2);
    
    function mafonctioncallback($arg1, $arg2) {
        // Votre code s'exécute ici
        // Pas besoin de retourner quoi que ce soit
        echo "Action déclenchée !";
    }
    

    Filtres : Modifier des données

    Les filtres permettent de modifier des données avant qu’elles soient utilisées ou affichées.

    
      Exemple conceptuel d'un filtre
     /
    
    // WordPress fait ceci internalement:
    function applyfiltersexample() {
        $valeur = "Texte original";
    
        // Permet aux plugins de modifier $valeur
        $valeur = applyfilters('monfiltrepersonnalise', $valeur, $arg2);
    
        // Utilise $valeur (potentiellement modifiée)
        echo $valeur;
    }
    
    // Vous vous "accrochez" avec:
    addfilter('monfiltrepersonnalise', 'moncallbackfiltre', 10, 2);
    
    function moncallbackfiltre($valeur, $arg2) {
        // Modifier la valeur
        $valeur = strtoupper($valeur);
    
        // TOUJOURS retourner la valeur
        return $valeur;
    }
    

    Tableau comparatif

    Caractéristique Actions Filtres
    But Exécuter du code Modifier des données
    Valeur de retour Aucune (void) Obligatoire
    Fonction d’ajout addaction() addfilter()
    Fonction de déclenchement doaction() applyfilters()
    Exemple d’usage Envoyer un email, enregistrer un log Modifier un titre, ajouter du CSS

    3. Utiliser les actions existantes

    Actions du cycle de vie WordPress

    
      Hook au démarrage de WordPress
     /
    addaction('init', 'yawcinitialization');
    
    function yawcinitialization() {
        // Enregistrer des post types personnalisés
        registerposttype('livre', array(
            'labels' => array(
                'name' => 'Livres',
                'singularname' => 'Livre'
            ),
            'public' => true,
            'hasarchive' => true,
            'supports' => array('title', 'editor', 'thumbnail'),
            'rewrite' => array('slug' => 'livres'),
        ));
    
        // Enregistrer des taxonomies
        registertaxonomy('genre', 'livre', array(
            'labels' => array(
                'name' => 'Genres',
                'singularname' => 'Genre'
            ),
            'hierarchical' => true,
            'rewrite' => array('slug' => 'genre'),
        ));
    }
    
    /
      Hook lors de l'activation d'un thème
     /
    addaction('aftersetuptheme', 'yawcthemesetup');
    
    function yawcthemesetup() {
        // Support des fonctionnalités du thème
        addthemesupport('post-thumbnails');
        addthemesupport('custom-logo');
        addthemesupport('html5', array(
            'search-form',
            'comment-form',
            'comment-list',
            'gallery',
            'caption',
        ));
    
        // Enregistrer les menus
        registernavmenus(array(
            'primary' => ('Menu Principal', 'yawc'),
            'footer'  => ('Menu Pied de page', 'yawc'),
        ));
    
        // Tailles d'images personnalisées
        addimagesize('portrait', 400, 600, true);
        addimagesize('paysage', 800, 400, true);
    }
    
    /
      Hook lors du chargement des scripts
     /
    addaction('wpenqueuescripts', 'yawcenqueueassets');
    
    function yawcenqueueassets() {
        // Charger les styles
        wpenqueuestyle(
            'yawc-main-style',
            gettemplatedirectoryuri() . '/assets/css/main.css',
            array(),
            '1.0.0'
        );
    
        // Charger les scripts
        wpenqueuescript(
            'yawc-main-script',
            gettemplatedirectoryuri() . '/assets/js/main.js',
            array('jquery'),
            '1.0.0',
            true
        );
    
        // Passer des données PHP au JavaScript
        wplocalizescript('yawc-main-script', 'yawcData', array(
            'ajaxUrl' => adminurl('admin-ajax.php'),
            'nonce'   => wpcreatenonce('yawc-nonce'),
            'siteUrl' => homeurl(),
        ));
    }
    

    Actions de contenu et d’affichage

    
      Ajouter du contenu avant et après les articles
     /
    addaction('thecontent', 'yawcaddcontentbefore', 1);
    addaction('thecontent', 'yawcaddcontentafter', 999);
    
    function yawcaddcontentbefore($content) {
        if (issingle() && !isadmin()) {
            $before = '';
    
            return $before . $content;
        }
        return $content;
    }
    
    function yawcaddcontentafter($content) {
        if (issingle() && !isadmin()) {
            $after = '
    '; $after .= '#### Partager cet article'; $after .= yawcgetsharebuttons(); $after .= '
    '; return $content . $after; } return $content; } function yawcgetreadingtime() { $content = getpostfield('postcontent', gettheID()); $wordcount = strwordcount(striptags($content)); $readingtime = ceil($wordcount / 200); // 200 mots par minute return $readingtime; } / Modifier le footer / addaction('wpfooter', 'yawccustomfootercontent'); function yawccustomfootercontent() { ?>

    Actions d'administration

    
      Ajouter une page d'administration personnalisée
     /
    addaction('adminmenu', 'yawcaddadminmenu');
    
    function yawcaddadminmenu() {
        addmenupage(
            'Configuration YAWC',           // Titre de la page
            'YAWC Settings',                // Titre du menu
            'manageoptions',               // Capacité requise
            'yawc-settings',                // Slug
            'yawcsettingspagecallback',  // Callback
            'dashicons-admin-generic',      // Icône
            30                              // Position
        );
    
        addsubmenupage(
            'yawc-settings',
            'Statistiques',
            'Stats',
            'manageoptions',
            'yawc-stats',
            'yawcstatspagecallback'
        );
    }
    
    function yawcsettingspagecallback() {
        ?>
        
    # html(getadminpagetitle()); ?>
    fields('yawcsettingsgroup'); dosettingssections('yawc-settings'); submitbutton(); ?>
    Enregistrer les paramètres / addaction('admininit', 'yawcregistersettings'); function yawcregistersettings() { registersetting('yawcsettingsgroup', 'yawcapikey', array( 'type' => 'string', 'sanitizecallback' => 'sanitizetextfield', 'default' => '', )); registersetting('yawcsettingsgroup', 'yawcenablecache', array( 'type' => 'boolean', 'sanitizecallback' => 'restsanitizeboolean', 'default' => false, )); addsettingssection( 'yawcmainsection', 'Paramètres principaux', 'yawcsectioncallback', 'yawc-settings' ); addsettingsfield( 'yawcapikey', 'Clé API', 'yawcapikeycallback', 'yawc-settings', 'yawcmainsection' ); addsettingsfield( 'yawcenablecache', 'Activer le cache', 'yawcenablecachecallback', 'yawc-settings', 'yawcmainsection' ); } function yawcsectioncallback() { echo '

    Configurez les paramètres de YAWC.

    '; } function yawcapikeycallback() { $value = getoption('yawcapikey', ''); printf( '', escattr($value) ); } function yawcenablecachecallback() { $value = getoption('yawcenablecache', false); printf( '', checked(1, $value, false) ); }

    Actions de base de données

    
      Actions lors de la sauvegarde d'un post
     /
    addaction('savepost', 'yawcsavepostmeta', 10, 3);
    
    function yawcsavepostmeta($postid, $post, $update) {
        // Vérifications de sécurité
        if (defined('DOINGAUTOSAVE') && DOINGAUTOSAVE) {
            return;
        }
    
        if (!currentusercan('editpost', $postid)) {
            return;
        }
    
        if (wpispostrevision($postid)) {
            return;
        }
    
        // Sauvegarder des métadonnées personnalisées
        if (isset($POST['yawccustomfield'])) {
            updatepostmeta(
                $postid,
                'yawccustomfield',
                sanitizetextfield($POST['yawccustomfield'])
            );
        }
    
        // Générer automatiquement un slug SEO-friendly
        if ($post->posttype === 'livre') {
            $seotitle = sanitizetitle($post->posttitle);
            updatepostmeta($postid, 'yawcseoslug', $seotitle);
        }
    
        // Logger l'activité
        yawclogpostupdate($postid, $post, $update);
    }
    
    /
      Action lors de la suppression d'un post
     /
    addaction('beforedeletepost', 'yawccleanuppostdata');
    
    function yawccleanuppostdata($postid) {
        // Nettoyer les données associées
        global $wpdb;
    
        // Supprimer les métadonnées personnalisées
        $wpdb->delete(
            $wpdb->postmeta,
            array('postid' => $postid, 'metakey' => 'yawccustomdata'),
            array('%d', '%s')
        );
    
        // Supprimer les fichiers uploadés associés
        $attachments = getposts(array(
            'posttype'   => 'attachment',
            'postparent' => $postid,
            'numberposts' => -1,
        ));
    
        foreach ($attachments as $attachment) {
            wpdeleteattachment($attachment->ID, true);
        }
    
        // Logger la suppression
        errorlog(sprintf('Post %d deleted at %s', $postid, currenttime('mysql')));
    }
    

    4. Utiliser les filtres existants

    Filtres de contenu

    
      Modifier le titre des articles
     /
    addfilter('thetitle', 'yawcmodifytitle', 10, 2);
    
    function yawcmodifytitle($title, $postid) {
        // Ajouter un indicateur "Nouveau" aux articles récents
        if (ismainquery() && !isadmin()) {
            $post = getpost($postid);
    
            if ($post && $post->posttype === 'post') {
                $daysold = (time() - strtotime($post->postdate)) / DAYINSECONDS;
    
                if ($daysold <= 7) {
                    $title .= ' Nouveau';
                }
            }
        }
    
        return $title;
    }
    
    /
      Modifier l'extrait
     /
    addfilter('theexcerpt', 'yawccustomexcerpt');
    
    function yawccustomexcerpt($excerpt) {
        // Ajouter un lien "Lire la suite" personnalisé
        if (hasexcerpt()) {
            $excerpt = rtrim($excerpt);
            $excerpt .= sprintf(
                ' Continuer la lecture →',
                getpermalink()
            );
        }
    
        return $excerpt;
    }
    
    /
      Modifier la longueur de l'extrait
     /
    addfilter('excerptlength', 'yawcexcerptlength');
    
    function yawcexcerptlength($length) {
        return isfrontpage() ? 20 : 55;
    }
    
    /
      Personnaliser le texte "Lire la suite"
     /
    addfilter('excerptmore', 'yawcexcerptmore');
    
    function yawcexcerptmore($more) {
        return '...';
    }
    

    Filtres de requête

    
      Modifier la requête principale
     /
    addfilter('pregetposts', 'yawcmodifymainquery');
    
    function yawcmodifymainquery($query) {
        // Ne modifier que la requête principale sur le front-end
        if (!isadmin() && $query->ismainquery()) {
    
            // Exclure une catégorie de la page d'accueil
            if ($query->ishome()) {
                $query->set('cat', '-5'); // Exclure la catégorie ID 5
            }
    
            // Changer le nombre d'articles par page pour les archives
            if ($query->isarchive()) {
                $query->set('postsperpage', 12);
            }
    
            // Trier par titre sur les pages de catégorie
            if ($query->iscategory()) {
                $query->set('orderby', 'title');
                $query->set('order', 'ASC');
            }
    
            // Recherche personnalisée
            if ($query->issearch()) {
                // Rechercher dans plusieurs post types
                $query->set('posttype', array('post', 'page', 'livre'));
    
                // Exclure les posts privés des résultats de recherche
                $query->set('poststatus', 'publish');
            }
        }
    
        return $query;
    }
    
    /
      Modifier les arguments de WPQuery
     /
    addfilter('postswhere', 'yawcfilterpostswhere', 10, 2);
    
    function yawcfilterpostswhere($where, $query) {
        global $wpdb;
    
        // Ajouter un filtre de date personnalisé
        if ($customdate = $query->get('yawccustomdate')) {
            $where .= $wpdb->prepare(
                " AND $wpdb->posts.postdate >= %s",
                date('Y-m-d', strtotime($customdate))
            );
        }
    
        return $where;
    }
    
    // Utilisation:
    // $query = new WPQuery(array('yawccustomdate' => '-30 days'));
    

    Filtres d'authentification et de permissions

    
      Modifier les capacités utilisateur
     /
    addfilter('userhascap', 'yawcmodifyusercapabilities', 10, 4);
    
    function yawcmodifyusercapabilities($allcaps, $caps, $args, $user) {
        // Permettre aux éditeurs de gérer les options
        if (isset($allcaps['editor']) && $allcaps['editor']) {
            $allcaps['manageoptions'] = true;
        }
    
        // Restriction basée sur l'heure
        $currenthour = (int) currenttime('H');
        if ($currenthour >= 18 || $currenthour < 8) {
            // Retirer certaines capacités hors des heures de bureau
            unset($allcaps['deletepublishedposts']);
        }
    
        return $allcaps;
    }
    
    /
      Personnaliser le message de connexion
     /
    addfilter('loginmessage', 'yawccustomloginmessage');
    
    function yawccustomloginmessage($message) {
        if (empty($message)) {
            return '

    Bienvenue ! Connectez-vous pour accéder à votre espace.

    '; } return $message; } / Modifier l'URL de redirection après connexion / add
    filter('loginredirect', 'yawcloginredirect', 10, 3); function yawcloginredirect($redirectto, $request, $user) { if (!isset($user->roles) || !isarray($user->roles)) { return $redirectto; } // Rediriger les admins vers le dashboard if (inarray('administrator', $user->roles)) { return adminurl(); } // Rediriger les autres utilisateurs vers leur profil return adminurl('profile.php'); }

    Filtres d'administration

    
      Personnaliser les colonnes de la liste des posts
     /
    addfilter('managepostscolumns', 'yawcaddcustomcolumns');
    addfilter('managepostscustomcolumn', 'yawccustomcolumncontent', 10, 2);
    
    function yawcaddcustomcolumns($columns) {
        // Réorganiser les colonnes
        $newcolumns = array();
    
        foreach ($columns as $key => $value) {
            $newcolumns[$key] = $value;
    
            // Ajouter après le titre
            if ($key === 'title') {
                $newcolumns['wordcount'] = 'Nombre de mots';
                $newcolumns['readingtime'] = 'Temps de lecture';
            }
        }
    
        return $newcolumns;
    }
    
    function yawccustomcolumncontent($column, $postid) {
        switch ($column) {
            case 'wordcount':
                $content = getpostfield('postcontent', $postid);
                $wordcount = strwordcount(striptags($content));
                echo numberformati18n($wordcount);
                break;
    
            case 'readingtime':
                $content = getpostfield('postcontent', $postid);
                $wordcount = strwordcount(striptags($content));
                $minutes = ceil($wordcount / 200);
                printf('%d min', $minutes);
                break;
        }
    }
    
    /
      Rendre les colonnes personnalisées triables
     /
    addfilter('manageedit-postsortablecolumns', 'yawcsortablecolumns');
    
    function yawcsortablecolumns($columns) {
        $columns['wordcount'] = 'wordcount';
        return $columns;
    }
    
    /
      Personnaliser le footer de l'admin
     /
    addfilter('adminfootertext', 'yawccustomadminfooter');
    
    function yawccustomadminfooter($text) {
        return sprintf(
            'Développé avec ❤️ par Votre Entreprise | WordPress %s',
            'https://votresite.com',
            getbloginfo('version')
        );
    }
    

    5. Créer vos propres hooks personnalisés

    Créer des actions personnalisées

    
      Plugin principal avec hooks personnalisés
     /
    class YAWCNewsletter {
    
        public function construct() {
            addaction('adminpostnewslettersubscribe', array($this, 'handlesubscription'));
        }
    
        public function handlesubscription() {
            // Vérifications de sécurité
            if (!isset($POST['newsletternonce']) ||
                !wpverifynonce($POST['newsletternonce'], 'newslettersubscribe')) {
                wpdie('Erreur de sécurité');
            }
    
            $email = sanitizeemail($POST['email']);
    
            if (!isemail($email)) {
                wpdie('Email invalide');
            }
    
            // Hook AVANT l'inscription
            doaction('yawcbeforenewslettersubscribe', $email);
    
            // Logique d'inscription
            $result = $this->addsubscriber($email);
    
            if ($result) {
                // Hook APRÈS l'inscription réussie
                doaction('yawcafternewslettersubscribe', $email, $result);
    
                wpredirect(addqueryarg('subscribed', '1', wpgetreferer()));
            } else {
                // Hook en cas d'erreur
                doaction('yawcnewslettersubscribefailed', $email);
    
                wpredirect(addqueryarg('subscribeerror', '1', wpgetreferer()));
            }
    
            exit;
        }
    
        private function addsubscriber($email) {
            global $wpdb;
    
            $tablename = $wpdb->prefix . 'newslettersubscribers';
    
            $result = $wpdb->insert(
                $tablename,
                array(
                    'email' => $email,
                    'subscribedate' => currenttime('mysql'),
                    'status' => 'active',
                ),
                array('%s', '%s', '%s')
            );
    
            return $result !== false;
        }
    }
    
    new YAWCNewsletter();
    
    /
      Utilisation des hooks personnalisés par d'autres plugins/thèmes
     /
    
    // Envoyer un email de bienvenue
    addaction('yawcafternewslettersubscribe', 'yawcsendwelcomeemail');
    
    function yawcsendwelcomeemail($email, $subscriberid) {
        $subject = 'Bienvenue dans notre newsletter !';
    
        $message = sprintf(
            "Bonjour,nnMerci de vous être inscrit à notre newsletter.nnVous recevrez désormais nos dernières actualités.nnCordialement,nL'équipe"
        );
    
        wpmail($email, $subject, $message);
    }
    
    // Logger les tentatives échouées
    addaction('yawcnewslettersubscribefailed', 'yawclogfailedsubscription');
    
    function yawclogfailedsubscription($email) {
        errorlog(sprintf(
            '[Newsletter] Failed subscription attempt for email: %s at %s',
            $email,
            currenttime('mysql')
        ));
    }
    
    // Ajouter à une liste MailChimp
    addaction('yawcafternewslettersubscribe', 'yawcsynctomailchimp', 20);
    
    function yawcsynctomailchimp($email) {
        // Intégration avec l'API MailChimp
        $apikey = getoption('yawcmailchimpapikey');
        $listid = getoption('yawcmailchimplistid');
    
        if (empty($apikey) || empty($listid)) {
            return;
        }
    
        // Appel API MailChimp...
    }
    

    Créer des filtres personnalisés

    
      Système de templating avec filtres personnalisés
     /
    class YAWCTemplateEngine {
    
        public static function render($templatename, $data = array()) {
            // Permettre la modification des données avant le rendu
            $data = applyfilters('yawctemplatedata', $data, $templatename);
            $data = applyfilters("yawctemplatedata{$templatename}", $data);
    
            // Obtenir le chemin du template
            $templatepath = self::gettemplatepath($templatename);
    
            // Permettre la modification du chemin
            $templatepath = applyfilters('yawctemplatepath', $templatepath, $templatename);
    
            if (!fileexists($templatepath)) {
                return '';
            }
    
            // Extraire les variables
            extract($data);
    
            // Commencer le buffer
            obstart();
    
            // Hook avant le rendu
            doaction('yawcbeforetemplaterender', $templatename, $data);
    
            // Inclure le template
            include $templatepath;
    
            // Hook après le rendu
            doaction('yawcaftertemplaterender', $templatename, $data);
    
            // Récupérer le contenu
            $output = obgetclean();
    
            // Permettre la modification du output final
            $output = applyfilters('yawctemplateoutput', $output, $templatename, $data);
    
            return $output;
        }
    
        private static function gettemplatepath($templatename) {
            return gettemplatedirectory() . '/templates/' . $templatename . '.php';
        }
    }
    
    /
      Utilisation des filtres personnalisés
     /
    
    // Ajouter des données globales à tous les templates
    addfilter('yawctemplatedata', 'yawcaddglobaltemplatedata');
    
    function yawcaddglobaltemplatedata($data) {
        $data['sitename'] = getbloginfo('name');
        $data['currentuser'] = wpgetcurrentuser();
        $data['isloggedin'] = isuserloggedin();
    
        return $data;
    }
    
    // Modifier les données pour un template spécifique
    addfilter('yawctemplatedataproduct-card', 'yawcenrichproductdata');
    
    function yawcenrichproductdata($data) {
        if (isset($data['productid'])) {
            $data['productreviews'] = yawcgetproductreviews($data['productid']);
            $data['relatedproducts'] = yawcgetrelatedproducts($data['productid']);
        }
    
        return $data;
    }
    
    // Minifier le HTML en sortie
    addfilter('yawctemplateoutput', 'yawcminifyhtml', 999);
    
    function yawcminifyhtml($output) {
        if (!isadmin() && !currentusercan('editposts')) {
            $output = pregreplace('/s+/', ' ', $output);
            $output = pregreplace('/>s+', '><', $output);
        }
    
        return $output;
    }
    

    Plugin avec architecture complète de hooks

    
      Plugin: YAWC Events Manager
      Démonstration d'une architecture complète avec hooks
     /
    class YAWCEventsManager {
    
        private static $instance = null;
    
        public static function getinstance() {
            if (null === self::$instance) {
                self::$instance = new self();
            }
            return self::$instance;
        }
    
        private function construct() {
            $this->inithooks();
        }
    
        private function inithooks() {
            addaction('init', array($this, 'registerposttype'));
            addaction('savepostevent', array($this, 'saveeventmeta'), 10, 2);
        }
    
        public function registerposttype() {
            $labels = applyfilters('yawceventposttypelabels', array(
                'name'          => 'Événements',
                'singularname' => 'Événement',
                'addnew'       => 'Ajouter un événement',
                'addnewitem'  => 'Ajouter un nouvel événement',
                'edititem'     => 'Modifier l'événement',
            ));
    
            $args = array(
                'labels'       => $labels,
                'public'       => true,
                'hasarchive'  => true,
                'supports'     => array('title', 'editor', 'thumbnail'),
                'rewrite'      => array('slug' => 'evenements'),
            );
    
            $args = applyfilters('yawceventposttypeargs', $args);
    
            registerposttype('event', $args);
        }
    
        public function saveeventmeta($postid, $post) {
            // Hook avant la sauvegarde
            doaction('yawcbeforesaveeventmeta', $postid, $post);
    
            $metafields = array('eventdate', 'eventlocation', 'eventprice');
    
            foreach ($metafields as $field) {
                if (isset($POST[$field])) {
                    $value = $POST[$field];
    
                    // Permettre le filtrage de chaque valeur
                    $value = applyfilters("yawceventmeta{$field}", $value, $postid);
                    $value = applyfilters('yawceventmetavalue', $value, $field, $postid);
    
                    updatepostmeta($postid, '' . $field, $value);
                }
            }
    
            // Hook après la sauvegarde
            doaction('yawcaftersaveeventmeta', $postid, $post);
        }
    
        /
          Obtenir les événements à venir
         /
        public function getupcomingevents($limit = 10) {
            $args = array(
                'posttype'      => 'event',
                'postsperpage' => $limit,
                'metakey'       => 'eventdate',
                'orderby'        => 'metavalue',
                'order'          => 'ASC',
                'metaquery'     => array(
                    array(
                        'key'     => 'eventdate',
                        'value'   => date('Y-m-d'),
                        'compare' => '>=',
                        'type'    => 'DATE',
                    ),
                ),
            );
    
            // Permettre la modification de la requête
            $args = applyfilters('yawcupcomingeventsqueryargs', $args);
    
            $query = new WPQuery($args);
    
            // Permettre la modification des résultats
            $events = applyfilters('yawcupcomingevents', $query->posts, $query);
    
            return $events;
        }
    }
    
    // Initialiser le plugin
    YAWCEventsManager::getinstance();
    
    /
      Exemples d'utilisation des hooks par des extensions
     /
    
    // Extension 1: Ajouter un champ personnalisé
    addfilter('yawceventposttypeargs', 'yawcaddeventcustomfields');
    
    function yawcaddeventcustomfields($args) {
        $args['supports'][] = 'custom-fields';
        return $args;
    }
    
    // Extension 2: Valider le prix
    addfilter('yawceventmetaeventprice', 'yawcvalidateeventprice', 10, 2);
    
    function yawcvalidateeventprice($value, $postid) {
        $value = floatval($value);
        return max(0, $value); // S'assurer que le prix n'est pas négatif
    }
    
    // Extension 3: Envoyer une notification
    addaction('yawcaftersaveeventmeta', 'yawcnotifynewevent', 10, 2);
    
    function yawcnotifynewevent($postid, $post) {
        if ($post->poststatus === 'publish' && !getpostmeta($postid, 'notificationsent', true)) {
            // Envoyer des notifications aux abonnés
            yawcsendeventnotifications($postid);
    
            // Marquer comme envoyé
            updatepostmeta($postid, 'notificationsent', true);
        }
    }
    

    6. Priorités et ordre d'exécution

    Comprendre les priorités

    
      La priorité détermine l'ordre d'exécution
      Priorité par défaut: 10
      Plus le nombre est bas, plus tôt la fonction s'exécute
     /
    
    // Ces fonctions s'exécuteront dans cet ordre:
    addaction('init', 'fonctionpriorite5', 5);    // 1er
    addaction('init', 'fonctionpriorite10', 10);  // 2ème (défaut)
    addaction('init', 'fonctionpriorite10bis', 10); // 3ème (même priorité, ordre d'ajout)
    addaction('init', 'fonctionpriorite15', 15);  // 4ème
    addaction('init', 'fonctionpriorite100', 100); // 5ème (dernier)
    
    function fonctionpriorite5() {
        echo "1. Priorité 5n";
    }
    
    function fonctionpriorite10() {
        echo "2. Priorité 10 (première)n";
    }
    
    function fonctionpriorite10bis() {
        echo "3. Priorité 10 (deuxième)n";
    }
    
    function fonctionpriorite15() {
        echo "4. Priorité 15n";
    }
    
    function fonctionpriorite100() {
        echo "5. Priorité 100n";
    }
    

    Cas pratiques de gestion des priorités

    
      Exemple: Chaîne de filtres avec priorités
     /
    
    // Filtre de base
    addfilter('thetitle', 'yawcuppercasetitle', 10);
    
    function yawcuppercasetitle($title) {
        return strtoupper($title);
    }
    
    // Ce filtre s'exécute AVANT (priorité plus basse)
    addfilter('thetitle', 'yawcaddprefix', 5);
    
    function yawcaddprefix($title) {
        return '[ARTICLE] ' . $title;
    }
    
    // Ce filtre s'exécute APRÈS (priorité plus haute)
    addfilter('thetitle', 'yawcaddsuffix', 20);
    
    function yawcaddsuffix($title) {
        return $title . ' ✓';
    }
    
    // Résultat final: "[ARTICLE] MON TITRE ✓"
    // Ordre d'exécution:
    // 1. yawcaddprefix (5):   "[ARTICLE] Mon titre"
    // 2. yawcuppercasetitle (10): "[ARTICLE] MON TITRE"
    // 3. yawcaddsuffix (20):  "[ARTICLE] MON TITRE ✓"
    
    /
      Surcharger un hook existant
     /
    
    // Hook d'un plugin tiers (priorité 10)
    addfilter('contentfilter', 'otherpluginfunction', 10);
    
    // Pour modifier APRÈS le plugin tiers
    addfilter('contentfilter', 'yawcmodifyafter', 20);
    
    // Pour s'exécuter AVANT le plugin tiers
    addfilter('contentfilter', 'yawcmodifybefore', 5);
    
    // Pour désactiver complètement le hook du plugin tiers
    removefilter('contentfilter', 'otherpluginfunction', 10);
    

    Gestion avancée avec nombre d'arguments

    
      Le 4ème paramètre définit le nombre d'arguments acceptés
     /
    
    // Hook WordPress avec plusieurs arguments
    doaction('exampleaction', $arg1, $arg2, $arg3);
    
    // Recevoir 1 seul argument (défaut)
    addaction('exampleaction', 'fonction1arg', 10, 1);
    
    function fonction1arg($arg1) {
        // Accès uniquement à $arg1
    }
    
    // Recevoir 2 arguments
    addaction('exampleaction', 'fonction2args', 10, 2);
    
    function fonction2args($arg1, $arg2) {
        // Accès à $arg1 et $arg2
    }
    
    // Recevoir tous les arguments
    addaction('exampleaction', 'fonctionallargs', 10, 3);
    
    function fonctionallargs($arg1, $arg2, $arg3) {
        // Accès à tous les arguments
    }
    
    /
      Exemple concret: Modification de requête
     /
    addaction('pregetposts', 'yawcmodifysearch', 10, 1);
    
    function yawcmodifysearch($query) {
        // $query est le seul argument nécessaire
        if ($query->issearch() && !isadmin()) {
            $query->set('postsperpage', 20);
        }
    }
    
    /
      Exemple: Callback de sauvegarde avec arguments multiples
     /
    addaction('savepost', 'yawcsavehandler', 10, 3);
    
    function yawcsavehandler($postid, $post, $update) {
        // 3 arguments sont disponibles
        if ($update) {
            // C'est une mise à jour, pas une nouvelle publication
            yawclogupdate($postid, $post);
        }
    }
    

    7. Hooks conditionnels et contextuels

    Exécution conditionnelle

    
      N'exécuter des hooks que dans certaines conditions
     /
    
    // Seulement sur la page d'accueil
    addaction('wpfooter', 'yawchomepagefooter');
    
    function yawchomepagefooter() {
        if (!isfrontpage()) {
            return;
        }
    
        echo '
    Découvrez nos services !
    '; } // Seulement pour les articles d'une certaine catégorie add
    filter('thecontent', 'yawcaddcategorydisclaimer'); function yawcaddcategorydisclaimer($content) { if (!issingle() || !incategory('tutoriels')) { return $content; } $disclaimer = '
    '; $disclaimer .= 'Note: Ce tutoriel nécessite des connaissances de base en PHP.'; $disclaimer .= '
    '; return $disclaimer . $content; } // Seulement pour certains rôles utilisateur add
    action('adminnotices', 'yawcadminnoticeforeditors'); function yawcadminnoticeforeditors() { $user = wpgetcurrentuser(); if (!inarray('editor', $user->roles)) { return; } ?>

    Rappel: N'oubliez pas de vérifier l'orthographe avant de publier.

    action('wpfooter', 'yawcmobiledownloadapp'); function yawcmobiledownloadapp() { if (!wpismobile()) { return; } ?>

    Téléchargez notre application mobile pour une meilleure expérience !

    Hooks dynamiques

    
      Créer des hooks dynamiques basés sur le contexte
     /
    class YAWCDynamicHooks {
    
        public static function init() {
            // Hook générique
            addaction('yawcprocessform', array(CLASS, 'process'), 10, 2);
        }
    
        public static function process($formid, $data) {
            // Hook spécifique au formulaire
            doaction("yawcprocessform{$formid}", $data);
    
            // Hook basé sur le type de données
            if (isset($data['type'])) {
                doaction("yawcprocessformtype{$data['type']}", $formid, $data);
            }
    
            // Hook basé sur l'utilisateur
            if (isuserloggedin()) {
                $user = wpgetcurrentuser();
                doaction("yawcprocessformuser{$user->ID}", $formid, $data);
    
                foreach ($user->roles as $role) {
                    doaction("yawcprocessformrole{$role}", $formid, $data);
                }
            }
        }
    }
    
    YAWCDynamicHooks::init();
    
    /
      Utilisation des hooks dynamiques
     /
    
    // Hook pour un formulaire spécifique
    addaction('yawcprocessformcontact', 'yawchandlecontactform');
    
    function yawchandlecontactform($data) {
        // Logique spécifique au formulaire de contact
        wpmail(getoption('adminemail'), 'Nouveau contact', printr($data, true));
    }
    
    // Hook pour un type de données
    addaction('yawcprocessformtypenewsletter', 'yawchandlenewslettersignup');
    
    function yawchandlenewslettersignup($formid, $data) {
        // Ajouter à la liste de newsletter
        yawcaddtonewsletter($data['email']);
    }
    
    // Hook pour un rôle spécifique
    addaction('yawcprocessformrolesubscriber', 'yawcsubscriberformbonus');
    
    function yawcsubscriberformbonus($formid, $data) {
        // Bonus pour les abonnés
        yawcaddloyaltypoints(getcurrentuserid(), 10);
    }
    

    Hooks avec taxonomies et post types

    
      Hooks spécifiques aux post types personnalisés
     /
    
    // WordPress fournit des hooks dynamiques pour chaque post type
    addaction('savepostlivre', 'yawcsavebook', 10, 3);
    addaction('savepostfilm', 'yawcsavemovie', 10, 3);
    
    function yawcsavebook($postid, $post, $update) {
        // Logique spécifique aux livres
        if (!$update) {
            // Nouveau livre
            yawcnotifybookeditors($postid);
        }
    }
    
    function yawcsavemovie($postid, $post, $update) {
        // Logique spécifique aux films
        yawcupdateimdbrating($postid);
    }
    
    /
      Filtres sur les requêtes de taxonomie
     /
    addaction('creategenre', 'yawcnewgenrecreated', 10, 2);
    
    function yawcnewgenrecreated($termid, $ttid) {
        // Un nouveau genre a été créé
        yawcrebuildgenrecache();
    }
    
    /
      Hooks sur les métadonnées
     /
    addaction('addedpostmeta', 'yawcmetaadded', 10, 4);
    
    function yawcmetaadded($metaid, $postid, $metakey, $metavalue) {
        // Réagir à l'ajout de métadonnées spécifiques
        if ($metakey === 'featuredbook') {
            yawcupdatefeaturedbookslist();
        }
    }
    

    8. Performance et optimisation

    Éviter les hooks coûteux

    
      MAUVAIS: Hook exécuté à chaque fois
     /
    addaction('thepost', 'yawcbadexpensiveoperation');
    
    function yawcbadexpensiveoperation($post) {
        // Cette requête s'exécute pour CHAQUE post dans une boucle !
        $relatedposts = getposts(array(
            'categoryin' => wpgetpostcategories($post->ID),
            'postnotin' => array($post->ID),
            'postsperpage' => 5,
        ));
    
        // Traitement coûteux...
    }
    
    /
      BON: Hook avec mise en cache
     /
    addaction('thepost', 'yawcgoodexpensiveoperation');
    
    function yawcgoodexpensiveoperation($post) {
        // Vérifier le cache d'abord
        $cachekey = 'relatedposts' . $post->ID;
        $relatedposts = wpcacheget($cachekey);
    
        if ($relatedposts === false) {
            // Seulement si pas en cache
            $relatedposts = getposts(array(
                'categoryin' => wpgetpostcategories($post->ID),
                'postnotin' => array($post->ID),
                'postsperpage' => 5,
            ));
    
            // Mettre en cache pour 1 heure
            wpcacheset($cachekey, $relatedposts, '', 3600);
        }
    
        // Utiliser $relatedposts...
    }
    
    /
      MEILLEUR: Hook conditionnel
     /
    addaction('thepost', 'yawcbestexpensiveoperation');
    
    function yawcbestexpensiveoperation($post) {
        // N'exécuter que si nécessaire
        if (!issingle() || isadmin()) {
            return;
        }
    
        // Utiliser un transient pour un cache persistant
        $cachekey = 'relatedposts' . $post->ID;
        $relatedposts = gettransient($cachekey);
    
        if ($relatedposts === false) {
            $relatedposts = getposts(array(
                'categoryin' => wpgetpostcategories($post->ID),
                'postnotin' => array($post->ID),
                'postsperpage' => 5,
                'fields' => 'ids', // Récupérer seulement les IDs
                'nofoundrows' => true, // Pas besoin de pagination
            ));
    
            settransient($cachekey, $relatedposts, HOURINSECONDS);
        }
    }
    
    /
      Nettoyer le cache quand nécessaire
     /
    addaction('savepost', 'yawcclearrelatedpostscache');
    
    function yawcclearrelatedpostscache($postid) {
        $cachekey = 'relatedposts' . $postid;
        deletetransient($cachekey);
    
        // Nettoyer aussi les caches des posts de la même catégorie
        $categories = wpgetpostcategories($postid);
        foreach ($categories as $catid) {
            deletetransient('categoryposts' . $catid);
        }
    }
    

    Limiter l'utilisation des hooks

    
      Retirer des hooks quand ils ne sont plus nécessaires
     /
    class YAWCConditionalHook {
    
        private static $hookadded = false;
    
        public static function maybeaddhook() {
            // Ajouter le hook seulement une fois et seulement si nécessaire
            if (!self::$hookadded && self::shouldrun()) {
                addfilter('thecontent', array(CLASS, 'modifycontent'));
                self::$hookadded = true;
            }
        }
    
        private static function shouldrun() {
            return issingle() && incategory('premium');
        }
    
        public static function modifycontent($content) {
            // Après la première exécution, retirer le hook
            removefilter('thecontent', array(CLASS, 'modifycontent'));
    
            return self::addpremiumcontent($content);
        }
    
        private static function addpremiumcontent($content) {
            return '
    Contenu Premium
    ' . $content; } } // Initialiser conditionnellement if (is
    single()) { YAWCConditionalHook::maybeaddhook(); }

    Débouncing et throttling

    
      Limiter la fréquence d'exécution d'un hook coûteux
     /
    class YAWCHookThrottle {
    
        private static $lastrun = array();
    
        /
          N'exécuter qu'une fois par période définie
         /
        public static function throttle($hookname, $callback, $waitseconds = 60) {
            $currenttime = time();
    
            if (!isset(self::$lastrun[$hookname]) ||
                ($currenttime - self::$lastrun[$hookname]) >= $waitseconds) {
    
                calluserfunc($callback);
                self::$lastrun[$hookname] = $currenttime;
    
                return true;
            }
    
            return false;
        }
    }
    
    /
      Utilisation
     /
    addaction('savepost', 'yawcrebuildcachethrottled');
    
    function yawcrebuildcachethrottled($postid) {
        // Ne rebuild le cache que toutes les 5 minutes maximum
        YAWCHookThrottle::throttle('rebuildcache', function() {
            yawcrebuildentirecache();
        }, 300);
    }
    
    /
      Debouncing: Attendre que l'activité se calme
     /
    class YAWCHookDebounce {
    
        private static $timers = array();
    
        public static function debounce($hookname, $callback, $delayseconds = 5) {
            // Annuler le timer précédent s'il existe
            if (isset(self::$timers[$hookname])) {
                wpclearscheduledhook('yawcdebounced' . $hookname);
            }
    
            // Programmer une nouvelle exécution
            wpschedulesingleevent(
                time() + $delayseconds,
                'yawcdebounced' . $hookname
            );
    
            // Enregistrer le callback
            if (!hasaction('yawcdebounced' . $hookname)) {
                addaction('yawcdebounced' . $hookname, $callback);
            }
    
            self::$timers[$hookname] = time();
        }
    }
    
    /
      Utilisation: N'exécuter qu'après 10 secondes d'inactivité
     /
    addaction('wpajaxautosave', 'yawcautosavedebounced');
    
    function yawcautosavedebounced() {
        YAWCHookDebounce::debounce('autosave', function() {
            // Cette fonction ne s'exécutera que 10 secondes après
            // la dernière tentative de sauvegarde automatique
            yawcperformheavyautosaveoperations();
        }, 10);
    }
    

    9. Debugging des hooks

    Lister tous les hooks actifs

    
      Afficher tous les hooks enregistrés pour un tag donné
     /
    function yawclisthooks($tag = '') {
        global $wpfilter;
    
        if (empty($tag)) {
            echo '## Tous les hooks WordPress';
            foreach ($wpfilter as $hookname => $hook) {
                yawcdisplayhookdetails($hookname, $hook);
            }
        } else {
            if (isset($wpfilter[$tag])) {
                yawcdisplayhookdetails($tag, $wpfilter[$tag]);
            } else {
                echo "

    Aucun hook trouvé pour: $tag

    "; } } } function yawcdisplayhookdetails($hookname, $hook) { echo "### $hookname"; echo ''; echo ''; foreach ($hook->callbacks as $priority => $callbacks) { foreach ($callbacks as $callback) { $functionname = yawcgetcallbackname($callback['function']); echo ''; echo ""; echo ""; echo ""; echo ''; } } echo '
    PrioritéFonctionArguments acceptés
    $priority$functionname{$callback['acceptedargs']}

    '; } function yawc
    getcallbackname($callback) { if (isstring($callback)) { return $callback; } elseif (isarray($callback)) { if (isobject($callback[0])) { return getclass($callback[0]) . '->' . $callback[1]; } else { return $callback[0] . '::' . $callback[1]; } } elseif ($callback instanceof Closure) { return 'Closure'; } else { return 'Callback inconnu'; } } // Utilisation en mode debug if (WPDEBUG && currentusercan('manageoptions')) { addaction('wpfooter', function() { echo '
    '; yawclisthooks('wpenqueuescripts'); echo '
    '; }); }

    Logger l'exécution des hooks

    
      Logger toutes les exécutions de hooks
     /
    class YAWCHookLogger {
    
        private static $logs = array();
        private static $enabled = false;
    
        public static function enable() {
            if (self::$enabled) {
                return;
            }
    
            self::$enabled = true;
    
            // Intercepter tous les hooks
            addfilter('all', array(CLASS, 'loghook'), 1);
        }
    
        public static function loghook($tag) {
            if (!self::$enabled) {
                return $tag;
            }
    
            $backtrace = debugbacktrace(DEBUGBACKTRACEIGNOREARGS, 4);
    
            // Déterminer si c'est une action ou un filtre
            $type = 'unknown';
            foreach ($backtrace as $trace) {
                if (isset($trace['function'])) {
                    if (inarray($trace['function'], array('doaction', 'doactionrefarray'))) {
                        $type = 'action';
                        break;
                    } elseif (inarray($trace['function'], array('applyfilters', 'applyfiltersrefarray'))) {
                        $type = 'filter';
                        break;
                    }
                }
            }
    
            self::$logs[] = array(
                'tag'  => $tag,
                'type' => $type,
                'time' => microtime(true),
                'memory' => memorygetusage(),
            );
    
            return $tag;
        }
    
        public static function getlogs() {
            return self::$logs;
        }
    
        public static function displaylogs() {
            if (empty(self::$logs)) {
                echo '

    Aucun log disponible.

    '; return; } $start
    time = self::$logs[0]['time']; $startmemory = self::$logs[0]['memory']; echo ''; echo ''; foreach (self::$logs as $index => $log) { $timediff = ($log['time'] - $starttime) 1000; $memorydiff = ($log['memory'] - $startmemory) / 1024; printf( '', $index + 1, eschtml($log['tag']), eschtml($log['type']), $timediff, $memorydiff ); } echo '
    #TagTypeTemps (ms)Mémoire (KB)
    %d%s%s%.2f%.2f
    '; } public static function save
    tofile($filename = 'hook-logs.json') { $uploaddir = wpuploaddir(); $filepath = $uploaddir['basedir'] . '/' . $filename; fileputcontents($filepath, jsonencode(self::$logs, JSONPRETTYPRINT)); return $filepath; } } // Activer en mode debug if (WPDEBUG && isset($GET['loghooks'])) { YAWCHookLogger::enable(); addaction('shutdown', function() { $file = YAWCHookLogger::savetofile(); errorlog('Hooks logged to: ' . $file); }); }

    Debug d'un hook spécifique

    
      Tracer l'exécution d'un hook particulier
     /
    function yawctracehook($tag) {
        addaction($tag, function() use ($tag) {
            $args = funcgetargs();
    
            echo '
    '; echo "#### Hook déclenché: $tag"; if (!empty($args)) { echo '
    ';
                printr($args);
                echo '

    ';
    }

    // Backtrace pour voir d'où vient l'appel
    echo '

    Backtrace
    ';
            debugprintbacktrace(DEBUGBACKTRACEIGNOREARGS);
            echo '

    ';

    echo '

    ';
    }, 1, 99); // Priorité 1 pour s'exécuter en premier, 99 args max
    }

    // Utilisation
    if (current

    usercan('manageoptions') && isset($GET['trace'])) {
    yawc
    tracehook('savepost');
    yawctracehook('wpenqueuescripts');
    yawctracehook('thecontent');
    }

    ## 10. Cas d'usage avancés et patterns
    ### Pattern: Hook Proxy

    
      Créer un système de proxy pour intercepter et modifier les hooks
     /
    class YAWCHookProxy {
    
        private $originaltag;
        private $proxytag;
        private $middleware = array();
    
        public function construct($originaltag, $proxytag = null) {
            $this->originaltag = $originaltag;
            $this->proxytag = $proxytag ?: $originaltag . 'proxied';
    
            $this->setup();
        }
    
        private function setup() {
            // Intercepter le hook original
            addfilter($this->originaltag, array($this, 'intercept'), 1, 99);
        }
    
        public function intercept() {
            $args = funcgetargs();
    
            // Exécuter les middlewares
            foreach ($this->middleware as $middleware) {
                $args = calluserfuncarray($middleware, $args);
                if ($args === false) {
                    // Un middleware a arrêté l'exécution
                    return $args[0];
                }
            }
    
            // Exécuter le hook proxifié
            arrayunshift($args, $this->proxytag);
            return calluserfuncarray('applyfilters', $args);
        }
    
        public function addMiddleware($callback) {
            $this->middleware[] = $callback;
            return $this;
        }
    }
    
    /
      Utilisation du proxy
     /
    $contentproxy = new YAWCHookProxy('thecontent');
    
    // Ajouter un middleware de logging
    $contentproxy->addMiddleware(function($content) {
        errorlog('Content length before filters: ' . strlen($content));
        return funcgetargs();
    });
    
    // Ajouter un middleware de sécurité
    $contentproxy->addMiddleware(function($content) {
        // Bloquer si le contenu contient certains mots
        if (strpos($content, 'BLOCKEDWORD') !== false) {
            errorlog('Content blocked');
            return false; // Arrête l'exécution
        }
        return funcgetargs();
    });
    
    // Utiliser le hook proxifié
    addfilter('thecontentproxied', function($content) {
        return $content . '

    Contenu ajouté via proxy

    '; });

    Pattern: Event Dispatcher

    
      Système d'événements avancé basé sur les hooks
     /
    class YAWCEventDispatcher {
    
        private static $instance = null;
        private $listeners = array();
    
        public static function instance() {
            if (null === self::$instance) {
                self::$instance = new self();
            }
            return self::$instance;
        }
    
        /
          Enregistrer un écouteur d'événement
         /
        public function listen($event, $callback, $priority = 10) {
            if (!isset($this->listeners[$event])) {
                $this->listeners[$event] = array();
            }
    
            $this->listeners[$event][] = array(
                'callback' => $callback,
                'priority' => $priority,
            );
    
            // Trier par priorité
            usort($this->listeners[$event], function($a, $b) {
                return $a['priority'] - $b['priority'];
            });
    
            return $this;
        }
    
        /
          Déclencher un événement
         /
        public function dispatch($event, $payload = array()) {
            // Hook WordPress standard
            doaction("yawcevent{$event}", $payload);
    
            // Système d'événements personnalisé
            if (!isset($this->listeners[$event])) {
                return null;
            }
    
            $result = null;
    
            foreach ($this->listeners[$event] as $listener) {
                $result = calluserfunc($listener['callback'], $payload, $result);
    
                // Permettre l'arrêt de la propagation
                if ($result === false) {
                    break;
                }
            }
    
            return $result;
        }
    
        /
          Écouter un événement une seule fois
         /
        public function once($event, $callback, $priority = 10) {
            $wrapper = function($payload) use ($event, $callback, &$wrapper) {
                // Retirer l'écouteur après exécution
                $this->forget($event, $wrapper);
    
                return calluserfunc($callback, $payload);
            };
    
            return $this->listen($event, $wrapper, $priority);
        }
    
        /
          Retirer un écouteur
         /
        public function forget($event, $callback = null) {
            if ($callback === null) {
                unset($this->listeners[$event]);
                return $this;
            }
    
            if (isset($this->listeners[$event])) {
                $this->listeners[$event] = arrayfilter(
                    $this->listeners[$event],
                    function($listener) use ($callback) {
                        return $listener['callback'] !== $callback;
                    }
                );
            }
    
            return $this;
        }
    }
    
    /
      Utilisation du Event Dispatcher
     /
    $events = YAWCEventDispatcher::instance();
    
    // Écouter un événement personnalisé
    $events->listen('user.registered', function($payload) {
        $user = $payload['user'];
    
        // Envoyer un email de bienvenue
        wpmail($user->useremail, 'Bienvenue !', 'Merci de vous être inscrit.');
    });
    
    $events->listen('user.registered', function($payload) {
        // Ajouter des points de fidélité
        yawcaddloyaltypoints($payload['user']->ID, 100);
    }, 20);
    
    // Écouter une seule fois
    $events->once('first.purchase', function($payload) {
        // Bonus uniquement pour le premier achat
        yawcsendfirstpurchasebonus($payload['userid']);
    });
    
    // Déclencher l'événement
    function yawcregisteruser($userdata) {
        $user = wpinsertuser($userdata);
    
        if (!iswperror($user)) {
            YAWCEventDispatcher::instance()->dispatch('user.registered', array(
                'user' => getuserdata($user),
                'timestamp' => currenttime('timestamp'),
            ));
        }
    
        return $user;
    }
    

    Pattern: Hook Chain

    
      Chaîner plusieurs transformations avec validation
     /
    class YAWCFilterChain {
    
        private $value;
        private $filters = array();
    
        public function construct($initialvalue) {
            $this->value = $initialvalue;
        }
    
        /
          Ajouter un filtre à la chaîne
         /
        public function pipe($callback) {
            $this->filters[] = $callback;
            return $this;
        }
    
        /
          Ajouter un filtre conditionnel
         /
        public function when($condition, $callback) {
            if (iscallable($condition)) {
                $shouldapply = calluserfunc($condition, $this->value);
            } else {
                $shouldapply = (bool) $condition;
            }
    
            if ($shouldapply) {
                $this->filters[] = $callback;
            }
    
            return $this;
        }
    
        /
          Valider la valeur
         /
        public function validate($callback, $errormessage = 'Validation failed') {
            $this->filters[] = function($value) use ($callback, $errormessage) {
                if (!calluserfunc($callback, $value)) {
                    throw new Exception($errormessage);
                }
                return $value;
            };
    
            return $this;
        }
    
        /
          Exécuter la chaîne et retourner le résultat
         /
        public function get() {
            $value = $this->value;
    
            try {
                foreach ($this->filters as $filter) {
                    $value = calluserfunc($filter, $value);
                }
            } catch (Exception $e) {
                errorlog('Filter chain error: ' . $e->getMessage());
                return $this->value; // Retourner la valeur originale en cas d'erreur
            }
    
            return $value;
        }
    }
    
    /
      Utilisation de la chaîne de filtres
     /
    $processedcontent = (new YAWCFilterChain($rawcontent))
        ->pipe('trim')
        ->pipe('striptags')
        ->validate(function($value) {
            return strlen($value) > 10;
        }, 'Content too short')
        ->pipe(function($content) {
            return wpautop($content);
        })
        ->when(isuserloggedin(), function($content) {
            return $content . '

    Contenu premium pour membres

    '; }) ->pipe(function($content) { return applyfilters('mycustomcontentfilter', $content); }) ->get();

    Conclusion

    Le système de hooks WordPress est l'un des patterns architecturaux les plus puissants et élégants du CMS. Maîtriser actions et filtres vous permet de:

  • Étendre WordPress sans modifier le core
  • Créer des plugins modulaires et maintenables
  • Intégrer facilement des fonctionnalités tierces
  • Construire des architectures event-driven sophistiquées
  • Optimiser les performances avec un contrôle fin de l'exécution
  • Récapitulatif des bonnes pratiques

  • Nommer clairement vos hooks personnalisés avec un préfixe unique
  • Toujours échapper et valider les données dans vos callbacks
  • Utiliser les priorités judicieusement pour contrôler l'ordre d'exécution
  • Optimiser avec des conditions pour éviter l'exécution inutile
  • Documenter vos hooks pour faciliter leur utilisation
  • Nettoyer après vous en retirant les hooks quand nécessaire
  • Utiliser la mise en cache pour les opérations coûteuses
  • Tester l'impact performance de vos hooks
  • Ressources complémentaires

  • Plugin API Reference - WordPress Codex
  • Adam Brown's Hook Database
  • Query Monitor plugin pour debugging
  • Developer Tools dans le WordPress Dashboard
  • Le système de hooks est au cœur de l'écosystème WordPress. En les maîtrisant, vous rejoignez la communauté de développeurs qui construit collaborativement l'un des CMS les plus extensibles au monde.

    Une remarque, un retour ?

    Cet article est vivant — corrections, contre-arguments et retours de production sont les bienvenus. Trois canaux, choisissez celui qui vous convient.