Développer une extension WordPress sur mesure : structure, hooks et publication d’une extension légère

Sur un site indépendant, beaucoup de "petites personnalisations" n'exigent pas d'installer une extension tierce : un fichier unique de moins de 300 lignes suffit. Le critère : ne passez à une arborescence de dossiers que si le besoin couvre plus de 5 fonctionnalités distinctes ; sinon, un seul fichier php avec l'en-tête de commentaire d'extension fait l'affaire.

自研轻量插件:结构、钩子与发布Lightweight custom plugin: structure, hooks, ship 能用20行钩子解决的事,别装一个5000行的第三方插件Solve it in 20 hook lines instead of a 5000-line plugin 1 主文件1 Main file 插件头注释 + 常量定义Header comment + constants 单文件插件不超过300行Single file under 300 lines 2 钩子2 Hooks add_action / add_filteradd_action / add_filter 后台钩子别挂在前台请求上No admin hooks on frontend 3 激活卸载3 Activate / deactivate register_activation_hookregister_activation_hook 卸载钩子清理自己的表Uninstall hook cleans up 发布方式Shipping 私有插件不上wordpress.orgPrivate plugins skip wp.org 打zip上传 或git拉到mu-pluginsZip upload or mu-plugins 红线Red lines 别改主题functions.phpDo not edit theme functions.php 别在前台直接写SQL不过滤Never run raw SQL unescaped 判定:需求>5个独立功能才拆插件目录,否则单文件插件足够Decision: 5+ features justify a folder, else single file 反直觉:functions.php越写越长不是熟练,是在给下次换主题埋雷Counterintuitive: long functions.php kills the next theme swap 把自定义逻辑从主题搬进插件,主题才能放心换Move logic out of theme into plugins
Comparatif : structure de fichiers, emplacement des hooks et mode de publication d’une extension WordPress légère

Développer une extension WordPress sur mesure : structure, hooks et publication d’une extension légère

Conclusion d’abord : Sur un site indépendant, beaucoup de “petites personnalisations” n’exigent pas d’installer une extension tierce : un fichier unique de moins de 300 lignes suffit. Le critère : ne passez à une arborescence de dossiers que si le besoin couvre plus de 5 fonctionnalités distinctes ; sinon, un seul fichier php avec l’en-tête de commentaire d’extension fait l’affaire. Un constat contre-intuitif : entasser le code dans le functions.php du thème semble plus simple, mais au prochain changement de thème, toute cette logique disparaît.

1. Quand écrire sa propre extension

Dans WordPress, tout comportement personnalisé doit être accroché à un hook. Du simple changement d’adresse d’expéditeur à l’ajout d’un champ personnalisé, quelques dizaines de lignes de PHP suffisent. Le problème, c’est que beaucoup de gens commencent par chercher dans la bibliothèque d’extensions.

Pour décider entre installer une extension et l’écrire soi-même : si le besoin se limite à “modifier un comportement fixe”, comme changer l’expéditeur par défaut, ajouter un bout de JS personnalisé ou modifier la longueur de l’extrait, écrire sa propre extension est plus propre que d’en installer une tierce. Pour le cadre général du choix d’une extension, voir Stack technique pour un site WordPress indépendant.

Le point contre-intuitif : un functions.php de plus en plus long n’est pas un signe de maîtrise, c’est une bombe à retardement pour le prochain changement de thème. Lors d’une mise à jour ou d’un changement de thème, le functions.php est écrasé et tout le code disparaît. La bonne méthode : déplacer la logique personnalisée dans une extension dédiée et laisser le thème s’occuper uniquement de l’apparence.

Le nommage obéit aussi à des règles. Le nom du dossier de l’extension combine un préfixe et la fonction, par exemple crater-inquiry-source, pour éviter les doublons avec les extensions officielles. Les noms de fonctions prennent le même préfixe, crater_register_source, pour éviter les conflits avec d’autres extensions.

Les conflits de noms de fonctions sont l’erreur fatale la plus fréquente des extensions WordPress. Si deux extensions déclarent function register_source(), celle qui se charge en dernier provoque un écran blanc. Toute fonction personnalisée doit porter un préfixe unique.

Ampleur du besoin Forme recommandée Nombre de fichiers
1 à 3 petites modifications Extension en fichier unique 1 fichier php
3 à 5 fonctionnalités liées Extension en fichier unique + constantes 1 fichier php
Plus de 5 fonctionnalités distinctes Extension en arborescence de dossiers Fichier principal + includes + assets
Nécessite une page de réglages Extension en arborescence de dossiers Ajouter admin-page.php

2. Structure minimale d’une extension en fichier unique

Une extension WordPress fonctionnelle n’a besoin que d’un seul fichier php, avec un en-tête de commentaire au début qui indique à WP qu’il s’agit d’une extension. Placez le fichier dans wp-content/plugins/ et il apparaît dans l’administration.

L’en-tête de commentaire suit un format fixe : Plugin Name, Description, Version, Author. Sans ce commentaire, WP ne le reconnaît pas comme une extension.

Exemple 1 : changer l’expéditeur par défaut, tout le plugin tient en 10 lignes. On accroche les filtres wp_mail_from et wp_mail_from_name pour remplacer wordpress@ par noreply@example.com. Aucune page de réglages nécessaire.

Ce type de plugin uniquement à base de filtres est idéal pour démarrer. Pas de base de données, pas de page d’administration, pas de tâche planifiée : en cas de problème, on désactive le plugin et tout revient à la normale.

Si vous devez stocker de la configuration, utilisez la table options de WordPress : un enregistrement, un tableau. Ne créez pas votre propre table. Pour des réglages simples, get_option et update_option suffisent.

Type de hook Hooks courants Utilité
Filtre (filter) wp_mail_from、the_content Modifier le contenu affiché
Action wp_head、admin_init Insérer du code ou déclencher une action
Action dans le back-office admin_menu、admin_init Ajouter une page de réglages, uniquement dans l’administration
Action côté public wp_enqueue_scripts Charger le CSS/JS, uniquement côté public

L’endroit où vous accrochez un hook est décisif pour les performances. admin_init ne se déclenche que dans l’administration, wp_head uniquement au chargement des pages publiques. N’accrochez pas une logique réservée à l’administration sur init : chaque requête publique l’exécuterait pour rien.

Pour charger votre propre CSS et JS, passez par le hook wp_enqueue_scripts, jamais par un echo direct dans wp_head. Un echo direct provoque un double chargement des scripts.

Exemple 3 : le plugin doit charger un script de validation sur une page de formulaire. Utilisez wp_enqueue_script, déclarez la dépendance jquery et passez le cinquième paramètre à true pour charger le script en pied de page, sans bloquer le rendu initial.

3. Activation, désinstallation et nettoyage des données

Quand un plugin crée des tables ou écrit des options, il doit gérer l’activation et la désinstallation. Créer les tables à l’activation, les supprimer à la désinstallation, sinon la base garde des résidus après la suppression du plugin.

register_activation_hook se déclenche à l’activation du plugin : idéal pour créer des tables ou écrire les options par défaut. uninstall.php se déclenche à la suppression du plugin : idéal pour nettoyer les données. Attention, désactivation et désinstallation sont deux choses différentes : la désactivation ne supprime pas les données, seule la suppression le fait.

Exemple 2 : le plugin enregistre la provenance des demandes dans une table personnalisée. À l’activation, on crée la table avec dbDelta ; à la désinstallation, on la supprime avec DROP TABLE. Si l’utilisateur désactive puis réactive, les données sont toujours là ; s’il supprime le plugin, la table disparaît avec.

Durée de vie Hook Ce qu’il faut faire
Activer le plugin register_activation_hook Créer les tables, écrire les options par défaut
Désactiver le plugin register_deactivation_hook Supprimer les tâches planifiées, sans toucher aux données
Supprimer le plugin uninstall.php Supprimer les tables et les options
À chaque chargement plugins_loaded Définir les constantes, charger le domaine de traduction

Côté sécurité : dans un plugin personnalisé, tout traitement de saisie utilisateur doit passer par $wpdb->prepare, jamais par une concaténation SQL directe. Pour la configuration de base des rôles et des droits d’administration, voir Bonnes pratiques de sécurité WordPress. Pour la compatibilité des versions PHP, voir Mise à niveau de la version PHP d’un site indépendant.

4. Publication et maintenance : un plugin privé ne va pas sur le dépôt officiel

Un plugin privé écrit pour le site d’un seul client : inutile de le soumettre sur wordpress.org. Compressez-le en zip et importez-le depuis l’administration, ou clonez-le directement avec git dans wp-content/mu-plugins : les plugins MU sont activés par défaut et ne peuvent pas être désactivés par erreur.

La gestion des versions est indispensable. Incrémentez le numéro Version dans l’en-tête du plugin à chaque modification pour que les mises à jour apparaissent dans l’administration. Ne modifiez jamais les fichiers du plugin directement sur le site en production : travaillez en local, puis envoyez.

Plusieurs plugins développés en interne par l’équipe crater suivent cette logique, voir Stack technique crater. Ils répondent chacun à un petit problème précis et ne dépassent jamais deux fichiers.

Mode de diffusion Cas d’usage Avantages Risque
Import du zip depuis l’administration Plugin privé livré au client Le client peut le désactiver lui-même Risque de suppression accidentelle
Répertoire mu-plugins La logique principale ne doit pas s’arrêter Activé par défaut, impossible à désactiver par erreur Mise à jour manuelle
Soumission sur wordpress.org Plugin public générique Mise à jour automatique Passage en revue et documentation à rédiger

Ne négligez pas la gestion du numéro de version. Incrémentez le Version de l’en-tête du plugin à chaque modification pour que la mention « mise à jour disponible » apparaisse dans la liste des plugins de l’administration.

Un plugin privé que vous maintenez vous-même n’a pas de canal de mise à jour officiel : gérez-le avec git et déployez en faisant un git pull directement dans le répertoire du serveur. Ne modifiez pas les fichiers directement sur le site en production.

FAQ — Questions fréquentes

Dans quels cas écrire son propre plugin plutôt qu’installer une extension tierce ?
Si le besoin se limite à modifier un ou deux comportements fixes, par exemple l’adresse d’expédition des e-mails, l’ajout d’un bout de JS personnalisé ou la longueur des extraits, un plugin d’un seul fichier de moins de 300 lignes est plus propre. Au-delà de cinq fonctionnalités indépendantes ou dès qu’il faut une page de réglages dans l’administration, envisagez une arborescence de dossiers. Pour des besoins génériques comme des formulaires complexes ou le SEO, installez plutôt un plugin éprouvé.
Peut-on écrire le code dans le functions.php du thème ?
Non. Le functions.php suit le thème : à chaque mise à jour ou changement de thème, tout le code personnalisé disparaît. C’est précisément la raison de déplacer la logique dans un plugin indépendant. Le plugin est découplé du thème, le code continue de fonctionner quand vous changez de thème. Si vous cédez à la facilité en écrivant dans le functions.php, il faudra tout réécrire au prochain changement de thème.
Combien de lignes de code faut-il pour un plugin WordPress minimal qui fonctionne ?
Un plugin d’un seul fichier a besoin au minimum d’un fichier php avec un en-tête de commentaire (Plugin Name, Description, Version), puis du add_action ou add_filter nécessaire. Pour changer l’adresse d’expédition des e-mails, 10 lignes suffisent. Placez le fichier dans wp-content/plugins/ et il apparaîtra dans l’administration, prêt à être activé.
Si le plugin crée des tables de données, faut-il les supprimer soi-même à la désinstallation ?
Oui. Créez uninstall.php dans le répertoire du plugin et mettez-y les DROP TABLE et delete_option. register_activation_hook se charge de créer les tables à l’activation, uninstall.php de nettoyer les données à la suppression. Attention : deactivate désactive sans supprimer les données, uninstall supprime et efface, ne confondez pas les deux.
Faut-il soumettre un plugin privé sur wordpress.org ?
Non. Pour un plugin privé destiné à un seul site client, il suffit de créer un zip et de l’envoyer depuis l’administration, ou de le placer dans wp-content/mu-plugins. Les plugins MU sont activés par défaut et ne peuvent pas être désactivés par erreur, ce qui les rend adaptés à la logique métier critique. Seuls les plugins génériques, publics, que vous voulez partager, méritent d’être soumis sur wordpress.org.
Pourquoi mon code de plugin s’exécute-t-il à chaque chargement du site ?
C’est probablement parce que la logique d’administration est accrochée à des hooks globaux comme init ou plugins_loaded. Le code réservé à l’administration doit être accroché à admin_init ou admin_menu, qui ne se déclenchent que sous wp-admin. Pour charger du CSS/JS côté public, utilisez wp_enqueue_scripts, n’utilisez pas echo directement dans le hook.

Pour conclure

La valeur d’un plugin maison ne réside pas dans la démonstration technique, mais dans la récupération d’une petite logique maîtrisée depuis une bibliothèque de plugins tierce. Une mise à jour tierce peut changer le comportement, votre propre code reste toujours entre vos mains.

Trois étapes concrètes : déplacez une à une la logique personnalisée de functions.php vers un plugin à fichier unique ; si vous créez des tables, écrivez un uninstall pour les nettoyer ; pour un plugin privé, faites un zip ou placez-le dans mu-plugins, sans le soumettre à la bibliothèque officielle.

Une question fréquente : faut-il utiliser Composer ou un framework pour écrire un plugin ? Pour un plugin léger destiné à un seul site client, non.

Ajouter un framework signifie un dossier vendor de plusieurs dizaines de Mo et une gestion des dépendances à chaque mise à jour. Pour 300 lignes de code, le framework ne fait que compliquer les choses.

Ce n’est que lorsque le plugin devient un produit public, avec des tests unitaires et un travail à plusieurs, que Composer et les namespaces se justifient.

Un point de plus sur la sécurité : pour traiter un formulaire d’administration dans un plugin, il faut vérifier le nonce avec check_admin_referer pour éviter les falsifications de requête intersites. C’est le geste standard des formulaires d’administration WordPress, l’oublier revient à exposer l’interface d’administration.

Pour afficher une saisie utilisateur sur une page, utilisez les fonctions d’échappement esc_html, esc_attr, etc., n’utilisez pas echo directement. Même une valeur de configuration saisie dans votre propre administration doit être échappée à l’affichage public.

Un dernier point sur l’ordre d’apprentissage. Pour un premier plugin, commencez par de simples filtres comme changer l’expéditeur ou la longueur de l’extrait, sans toucher à la base de données ni aux pages d’administration.

Faites tourner une fois la boucle complète « écrire le code, envoyer, activer, voir le résultat », puis ajoutez des fonctionnalités progressivement. Commencer directement par un plugin avec tables de données et pages d’administration, c’est se perdre dans les hooks et le cycle de vie.

Écrire des plugins de plus en plus petits, c’est la bonne direction. Avant chaque fonctionnalité, demandez-vous si elle tient en vingt lignes avec les hooks existants. Si oui, pas de table, pas de page d’administration.

Garder le code d’un plugin sous 300 lignes, c’est une responsabilité envers soi-même et envers le client. Au-delà, il vaut mieux utiliser un plugin tiers éprouvé.

Un dernier rappel : n’empilez pas la logique personnalisée dans le functions.php du thème. Déplacez-la dans un plugin indépendant, le thème pourra être changé sereinement et le client pourra le maintenir lui-même sur le long terme.

Une phrase à retenir : ce qui se règle en 20 lignes de hook ne mérite pas une extension tierce de 5000 lignes. Moins de code, moins de dérapages possibles.

Cratère
Cratère Expert Cratère

Officiel de l'organisme · Cratère Crater

Cratère (Crater) est une société de services d'expansion internationale spécialisée dans le développement de sites WordPress autonomes, le référencement Google et la croissance globale GEO, au service des secteurs du matériel intelligent, de l'équipement industriel, du SaaS et des énergies nouvelles.

Capacités produitLivre blanc méthodologiqueRapports sectoriels
965partages
78articles de base de connaissances