12k
All articles

Créer un plugin WordPress à partir de zéro

Créez un plugin WordPress de zéro avec hooks, activation et désactivation, page de réglages, sanitisation, échappement et sauvegarde sécurisée.

OpenReplay Team
OpenReplay Team
Créer un plugin WordPress à partir de zéro

Un plugin WordPress, dans sa forme la plus simple, est un dossier placé dans wp-content/plugins contenant un fichier PHP qui commence par un en-tête sous forme de commentaire indiquant le nom du plugin. Enregistrez ce fichier et WordPress affichera le plugin dans l’écran Extensions.

Si vous avez l’habitude de glisser des bouts de code dans le functions.php d’un thème, vous en connaissez déjà le revers : changez de thème et le code disparaît avec lui. Déplacer ce code dans un plugin permet à la fonctionnalité de survivre à un changement de thème, ce qui est la raison principale pour laquelle le comportement du site relève des plugins et la présentation relève des thèmes.

Cet article construit un petit plugin de bout en bout : une notice de site affichée dans le pied de page, modifiable depuis une page de réglages qui enregistre les données en toute sécurité. Chaque bloc de code correspond à un état valide du plugin, vous pouvez donc vous arrêter après n’importe quelle section et activer ce que vous avez.

Points clés à retenir

  • Un plugin est un dossier dans wp-content/plugins contenant un fichier PHP dont l’en-tête en commentaire indique le nom du plugin ; Plugin Name est le seul champ d’en-tête obligatoire.
  • Le code d’un plugin ne fait rien tant que vous n’avez pas rattaché une fonction à un hook avec add_action() ou add_filter().
  • Un callback de filtre doit retourner la valeur qu’il reçoit ; s’il ne retourne rien, la valeur filtrée devient vide.
  • Une page de réglages sécurisée superpose quatre éléments : une vérification de capacité, un nonce, une désinfection des données en entrée et un échappement en sortie.
  • register_activation_hook() sert à définir les valeurs par défaut, la désactivation sert au nettoyage temporaire, et la suppression définitive des données relève de la désinstallation.

Comment créer le fichier et l’en-tête d’un plugin WordPress

Créez le dossier wp-content/plugins/site-notice/ et, à l’intérieur, le fichier site-notice.php. WordPress construit l’écran Extensions en lisant les fichiers PHP du dossier plugins et en repérant ceux qui commencent par un en-tête de plugin. Chaque fichier portant un en-tête compte comme un plugin distinct : placez donc l’en-tête dans un seul fichier.

<?php
/**
 * Plugin Name:       Site Notice
 * Description:       Shows a short notice in the site footer, editable from Settings.
 * Version:           1.0.0
 * Requires at least: 6.5
 * Requires PHP:      7.4
 * Author:            OpenReplay Team
 * License:           GPL-2.0-or-later
 * Text Domain:       site-notice
 */

La page consacrée aux exigences d’en-tête répertorie tous les champs reconnus, y compris Update URI et Requires Plugins. Voici ceux que vous utiliserez le plus souvent :

ChampCe que WordPress en faitObligatoire
Plugin NameL’affiche dans la liste des extensionsOui
DescriptionL’affiche sous le nomNon
VersionL’affiche ; sert aux comparaisons de mises à jourNon
Requires at least / Requires PHPBloque l’activation sur les environnements trop anciensNon
Author, License, Text DomainAttribution, licence, slug de traductionNon

Enregistrez le fichier et le plugin apparaît dans l’écran Extensions. Il s’active sans erreur et ne fait rien.

À quoi servent les hooks d’activation et de désactivation ?

register_activation_hook() exécute votre callback une seule fois, au moment où quelqu’un active le plugin, ce qui en fait un bon endroit pour écrire vos valeurs d’option initiales dans la base de données. register_deactivation_hook() sert à jeter tout ce dont le plugin n’avait besoin que pendant son fonctionnement, un cache étant l’exemple habituel. Supprimer définitivement des éléments, options et tables personnalisées comprises, relève plutôt de la désinstallation, car les utilisateurs désactivent souvent un plugin avec l’intention de le réactiver plus tard.

Ajoutez sous l’en-tête :

function orp_activate() {
	add_option( 'orp_notice_text', 'Welcome to the site.' );
}
register_activation_hook( __FILE__, 'orp_activate' );

function orp_deactivate() {
	delete_transient( 'orp_notice_cache' );
}
register_deactivation_hook( __FILE__, 'orp_deactivate' );

Le premier argument, __FILE__, désigne le fichier principal du plugin. Notre plugin ne met encore rien en cache, mais le callback de désactivation illustre le principe : nettoyer les artefacts temporaires, laisser l’option enregistrée intacte. Le préfixe orp_ sur chaque nom de fonction et d’option évite les collisions avec le cœur de WordPress et les autres plugins.

Pourquoi le code de mon plugin ne s’exécute-t-il pas ?

Le code d’un fichier de plugin ne s’exécute pas tout seul. WordPress n’exécute une fonction qu’une fois que vous l’avez rattachée à un hook ; jusque-là, le fichier reste inerte. add_action() prend un nom de hook et un callable, avec une priorité facultative dont la valeur par défaut est 10.

function orp_print_notice() {
	$notice = get_option( 'orp_notice_text' );
	if ( $notice ) {
		echo '<p class="orp-notice">' . esc_html( $notice ) . '</p>';
	}
}
add_action( 'wp_footer', 'orp_print_notice' );

Lorsque le thème déclenche wp_footer, WordPress appelle toutes les fonctions qui y sont rattachées, y compris la nôtre. Chargez n’importe quelle page du front-end et la notice apparaît juste avant la balise de fermeture du body.

Ajouter un filtre : modifier une valeur et la retourner

Une action permet à votre fonction de s’exécuter à un point précis du cycle de vie de WordPress ; un filtre transmet une valeur à votre fonction et attend en retour la valeur modifiée. Un callback de filtre doit retourner une valeur. S’il ne retourne rien, PHP retourne null, WordPress propage ce null, et ce que vous filtriez disparaît du site. Oubliez le return dans un callback sur body_class et l’élément body perd ses classes ; oubliez-le sur the_content et tous les articles s’affichent vides.

function orp_body_class( $classes ) {
	if ( get_option( 'orp_notice_text' ) ) {
		$classes[] = 'orp-has-notice';
	}
	return $classes;
}
add_filter( 'body_class', 'orp_body_class' );

Cette fonction reçoit le tableau des classes du body, y ajoute une classe lorsqu’une notice est définie, et retourne le tableau pour que les thèmes puissent styliser les pages différemment tant que la notice est active.

Comment construire une page de réglages WordPress ?

Une page de réglages repose sur trois enregistrements : une page de menu sur le hook admin_menu, puis un réglage accompagné de sa section et de son champ sur le hook admin_init, comme l’expose le chapitre sur la Settings API. add_options_page() place la page sous Réglages ; register_setting() nomme l’option et, point crucial, son sanitize_callback.

function orp_settings_menu() {
	add_options_page( 'Site Notice', 'Site Notice', 'manage_options',
		'orp-site-notice', 'orp_settings_page_html' );
}
add_action( 'admin_menu', 'orp_settings_menu' );

function orp_settings_init() {
	register_setting( 'orp_settings', 'orp_notice_text', array(
		'type'              => 'string',
		'sanitize_callback' => 'sanitize_text_field',
	) );
	add_settings_section( 'orp_main', 'Notice', '__return_false', 'orp-site-notice' );
	add_settings_field( 'orp_notice_text', 'Notice text',
		'orp_notice_field_html', 'orp-site-notice', 'orp_main' );
}
add_action( 'admin_init', 'orp_settings_init' );

function orp_notice_field_html() {
	$value = get_option( 'orp_notice_text', '' );
	echo '<input type="text" name="orp_notice_text" value="'
		. esc_attr( $value ) . '" class="regular-text">';
}

function orp_settings_page_html() {
	?>
	<div class="wrap">
		<h1><?php echo esc_html( get_admin_page_title() ); ?></h1>
		<form action="options.php" method="post">
			<?php
			settings_fields( 'orp_settings' );
			do_settings_sections( 'orp-site-notice' );
			submit_button( 'Save Notice' );
			?>
		</form>
	</div>
	<?php
}

Le formulaire est envoyé à options.php, et le cœur de WordPress se charge de l’enregistrement. Notez que register_setting() accepte également un argument default ; nous définissons le nôtre dans le hook d’activation : choisissez donc un mécanisme et tenez-vous-y.

Comment traiter la valeur soumise en toute sécurité ?

Une vérification de capacité répond à la question de savoir si cet utilisateur est autorisé à enregistrer des réglages ; un nonce répond à la question de savoir si cet utilisateur avait réellement l’intention de soumettre ce formulaire. Une page de réglages laissée sur le site d’un client a besoin des deux, et les nonces ne doivent jamais se substituer à la vérification de capacité.

Ici, la Settings API en couvre l’essentiel : settings_fields() génère le nonce et le cœur le vérifie au retour du formulaire, et le cœur bloque l’enregistrement à moins que l’utilisateur courant ne dispose de la capacité manage_options, celle que wp-admin/options.php applique par défaut aux pages de réglages. Si vous écrivez un jour votre propre gestionnaire de formulaire à la place, générez le nonce avec wp_nonce_field() et vérifiez-le avec check_admin_referer(). Ajoutez la garde explicite au callback de la page, à l’image de l’exemple du handbook lui-même :

function orp_settings_page_html() {
	if ( ! current_user_can( 'manage_options' ) ) {
		return;
	}
	// ... form as above ...
}

Vérifiez une capacité, jamais un nom de rôle comme administrator. Les deux couches restantes sont déjà en place : désinfection à l’entrée en base de données (sanitize_text_field comme sanitize_callback) et échappement à la sortie (esc_attr() dans le champ, esc_html() dans le pied de page). Ces deux opérations ne sont pas interchangeables, et négliger l’une ou l’autre est précisément la manière dont une option enregistrée devient un vecteur d’injection.

Activer le plugin et vérifier qu’il fonctionne

Activez Site Notice dans l’écran Extensions, chargez le front-end et vérifiez la présence de la notice dans le pied de page. Modifiez le texte sous Réglages, Site Notice, puis rechargez. Si rien ne se produit, parcourez cette liste dans l’ordre :

  1. Le plugin est-il réellement activé, et pas seulement présent ?
  2. Chaque chaîne passée à add_action() ou add_filter() correspond-elle exactement à un nom de fonction défini ?
  3. Y a-t-il des espaces ou une sortie avant <?php ? WordPress signale une « sortie inattendue » lors de l’activation dans ce cas.
  4. Activez WP_DEBUG dans wp-config.php, avec WP_DEBUG_LOG qui écrit les erreurs dans wp-content/debug.log, et lisez l’erreur PHP réelle au lieu de deviner.

Vous disposez maintenant d’un plugin avec une option par défaut, une action, un filtre qui retourne sa valeur, et une page de réglages enregistrée via un nonce, une vérification de capacité et un désinfecteur. C’est la base à reproduire pour chaque bout de code qui traîne encore dans functions.php : déplacez-le, préfixez-le, rattachez-le à un hook, et conservez intact le couple désinfection en entrée / échappement en sortie.

FAQ

Quelle est la différence entre wp_verify_nonce et check_admin_referer ?

check_admin_referer() vérifie à la fois le nonce et le référent pour les formulaires et les URL des écrans d'administration, et interrompt la requête avec une erreur 403 en cas d'échec de la vérification. wp_verify_nonce() ne contrôle que le nonce et retourne un résultat que vous traitez vous-même, ce qui convient aux gestionnaires Ajax et à d'autres contextes personnalisés. Ni l'un ni l'autre ne remplace une vérification de capacité : les nonces confirment que l'utilisateur avait l'intention d'effectuer l'action, current_user_can() confirme qu'il est autorisé à le faire.

Que deviennent les options enregistrées d'un plugin lors de sa désactivation ?

Rien : la désactivation laisse les options dans la base de données, si bien que les réglages sont intacts lorsque l'utilisateur réactive le plugin. Un callback de désactivation ne doit effacer que les éléments à durée de vie courte, tels que les transients ou les fichiers en cache. Le nettoyage définitif relève de la désinstallation, mise en œuvre soit avec register_uninstall_hook(), soit avec un fichier uninstall.php dans le dossier du plugin. Si vous utilisez uninstall.php, il doit vérifier que la constante WP_UNINSTALL_PLUGIN est définie avant de supprimer quoi que ce soit.

Un plugin WordPress peut-il comporter plusieurs fichiers PHP ?

Oui, mais seul le fichier principal doit contenir le commentaire d'en-tête du plugin. WordPress détecte les plugins en lisant les fichiers PHP du répertoire plugins à la recherche de cet en-tête, et chaque fichier qui en porte un apparaît comme un plugin à part entière. Chargez les fichiers additionnels depuis le fichier principal avec require_once, et faites en sorte que les appels tels que register_activation_hook() pointent vers le fichier principal du plugin, puisque leur premier paramètre doit référencer ce fichier.

Faut-il définir une valeur par défaut dans register_setting ou dans le hook d'activation ?

Utilisez un seul mécanisme, pas les deux. register_setting() accepte un argument default qui est retourné lorsqu'aucune valeur n'existe en base de données, tandis qu'add_option() dans un hook d'activation écrit une véritable ligne une seule fois, au moment de l'activation. L'approche par activation garantit que la valeur existe à chaque requête, y compris les requêtes du front-end ; la valeur par défaut de register_setting() ne s'applique que là où le code d'enregistrement a été exécuté. Mélanger les deux crée deux sources de vérité pour une même option.

DevTools for the frontend

Gain Debugging Superpowers

Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers.

Star on GitHub12k

We use cookies to improve your experience. By using our site, you accept cookies.