Publié sur Google Google
Clémence De Bastier profile picture
Clémence De Bastier
Google star 1Google star 2Google star 3Google star 4Google star 5Trustindex vérifie que la source originale de l'avis est Google.
Super prestataire, réactif et professionnel.
Publié sur Google Google
Service client weprecious profile picture
Service client weprecious
Google star 1Google star 2Google star 3Google star 4Google star 5Trustindex vérifie que la source originale de l'avis est Google.
Excellente expérience avec Aurelien et son Equipe. Tres réactifs et compétents. Je recommande.
Publié sur Google Google
Thom profile picture
Thom
Google star 1Google star 2Google star 3Google star 4Google star 5Trustindex vérifie que la source originale de l'avis est Google.
Je recommande les services d'Aurélien, disponible, pro et réactif !
Publié sur Google Google
Aude Sorey profile picture
Aude Sorey
Google star 1Google star 2Google star 3Google star 4Google star 5Trustindex vérifie que la source originale de l'avis est Google.
Merci pour votre aide. Rien ne fonctionnait et il ne faut pas s'étonner quand c'est un ami qui fait le site gratuitement, d'avoir des bugs ! Je recommande vos services avec plaisir à mes contacts.
Publié sur Google Google
Kevin Concha profile picture
Kevin Concha
Google star 1Google star 2Google star 3Google star 4Google star 5Trustindex vérifie que la source originale de l'avis est Google.
Aurelien a tout de suite compris mes attentes, et il m'a fait un site à la hauteur de mes exigences et est très minutieux dans les délais. J'ai d'autres sites à créer, c'est sans hésitation que je passerai par lui. Merci
Publié sur Google Google
Cédric APEP profile picture
Cédric APEP
Google star 1Google star 2Google star 3Google star 4Google star 5Trustindex vérifie que la source originale de l'avis est Google.
Rapide et efficace
Ordinateur portable affichant du code source, illustrant les bonnes pratiques de développement selon les principes SOLID.

Les principes SOLID sont cinq règles de conception orientée objet popularisées par Robert C. Martin : responsabilité unique (S), ouvert/fermé (O), substitution de Liskov (L), ségrégation des interfaces (I) et inversion des dépendances (D). En PHP, ils servent à écrire des classes petites, remplaçables et testables. Dans WordPress, ils aident surtout à organiser une extension ou un thème autour des hooks, sans finir avec une classe de plusieurs milliers de lignes que plus personne n’ose modifier.

Développeur WordPress depuis 14 ans, je conçois des extensions WordPress sur mesure, et SOLID est la grille que j’utilise dès que l’une d’elles commence à grossir. Ci-dessous, chaque principe est illustré par du code PHP 8 tiré d’une même extension de démonstration (des inscriptions à des événements), écrite pour cet article. Vous verrez aussi où s’arrêter : WordPress reste largement procédural, et tout n’a pas besoin d’interfaces.

Les 5 principes SOLID en bref

Robert C. Martin a formalisé ces principes dans son article « Design Principles and Design Patterns » (2000) ; l’acronyme SOLID a été proposé ensuite par Michael Feathers (article SOLID sur Wikipédia). Voici ce que chacun signifie et à quoi il ressemble dans une extension WordPress.

Les cinq principes SOLID et leur traduction dans WordPress
Lettre Principe L’idée en une phrase Dans WordPress
S Responsabilité unique (Single Responsibility) Une classe n’a qu’une seule raison de changer. Une classe pour le type de contenu, une pour le shortcode, une pour les e-mails.
O Ouvert/fermé (Open/Closed) On étend le comportement sans modifier le code existant. Des hooks (do_action(), apply_filters()) comme points d’extension.
L Substitution de Liskov (Liskov Substitution) Une implémentation peut en remplacer une autre sans casser le code appelant. Tous les caches renvoient null en cas d’absence, même si get_transient() renvoie false.
I Ségrégation des interfaces (Interface Segregation) Aucune classe ne dépend de méthodes qu’elle n’utilise pas. Des interfaces Hookable et Activatable plutôt qu’une interface Module géante.
D Inversion des dépendances (Dependency Inversion) Le code métier dépend d’abstractions, pas de détails techniques. Un service qui reçoit un Mailer au lieu d’appeler wp_mail() directement.

Avant de coder : WordPress, PHP et la programmation orientée objet

WordPress reste largement procédural : des fonctions globales, et un système de hooks qui fonctionne comme un gestionnaire d’événements global. Le cœur n’a pas de conteneur d’injection de dépendances, et le manuel des standards PHP de WordPress précise qu’il n’existe aucun calendrier pour introduire les namespaces dans le cœur. SOLID s’applique donc à votre code : extension, thème, mu-plugin. Pas au cœur.

Les exemples utilisent la promotion de propriétés dans le constructeur et le type mixed (PHP 8.0), ainsi que les propriétés readonly (PHP 8.1). Or PHP 8.1 n’est plus maintenu depuis le 31 décembre 2025 et PHP 8.2 ne reçoit plus que des correctifs de sécurité jusqu’au 31 décembre 2026 (versions supportées sur php.net). D’où un minimum fixé à PHP 8.2, à déclarer avec le champ Requires PHP de l’en-tête de l’extension : WordPress refuse alors de l’activer sur un serveur trop ancien. Pour mémoire, WordPress accepte PHP 7.4 au minimum depuis la version 7.0 (annonce sur make.wordpress.org) mais recommande PHP 8.3 ou plus.

Côté conventions, je suis les bonnes pratiques du manuel des extensions et les standards de codage :

  • un namespace en mots capitalisés séparés par des underscores (Devwp\Solid_Demo) et des préfixes (devwp_solid_demo_) pour tout ce qui reste global : hooks, options, transients ;
  • une classe ou une interface par fichier, nommé d’après la classe (class-event-post-type.php) ;
  • une visibilité toujours déclarée, des déclarations de type partout où c’est possible ;
  • defined( 'ABSPATH' ) || exit; en tête de chaque fichier PHP de l’extension.

Si vous débutez, commencez par apprendre à créer une extension WordPress pas à pas : cet article suppose que vous savez déjà ce qu’est un hook. Et pour que ces règles soient appliquées sans y penser, prenez le temps de vérifier votre code avec PHPCS et les WordPress Coding Standards.

S : le principe de responsabilité unique (SRP)

Le principe de responsabilité unique dit qu’une classe doit avoir une seule raison de changer. Pas « une seule méthode » : une seule raison, c’est-à-dire une seule préoccupation (le modèle de données, l’affichage, l’export, les e-mails), portée par une seule catégorie de demandes de modification.

Le symptôme dans WordPress : la classe qui fait tout

C’est un schéma que l’on rencontre souvent en reprenant une extension : une classe principale dont le constructeur accroche tout, et des méthodes qui mélangent requêtes, HTML et envoi d’e-mails.

<?php
// À éviter : une seule classe, au moins cinq raisons de changer.
final class Events_Plugin {

	public function __construct() {
		add_action( 'init', array( $this, 'register_post_type' ) );
		add_shortcode( 'devwp_latest_event', array( $this, 'render_shortcode' ) );
		add_action( 'admin_menu', array( $this, 'add_settings_page' ) );
		add_action( 'admin_post_devwp_export', array( $this, 'export_csv' ) );
		add_action( 'devwp_registered', array( $this, 'send_confirmation_email' ), 10, 2 );
	}

	// Suivent des centaines de lignes qui mélangent requêtes, HTML, export et e-mails.
}

Modifier le texte d’un e-mail oblige à rouvrir la classe qui déclare le type de contenu ; impossible de tester l’export sans charger tout le reste ; et chaque instanciation déclenche des effets de bord.

Exemple : une classe par responsabilité, des hooks dans une méthode dédiée

<?php
// includes/class-event-post-type.php et includes/class-event-shortcode.php,
// regroupés ici pour la lecture.

namespace Devwp\Solid_Demo;

defined( 'ABSPATH' ) || exit;

/**
 * Une seule raison de changer : la déclaration du type de contenu.
 */
final class Event_Post_Type implements Hookable {

	public function register_hooks(): void {
		add_action( 'init', array( $this, 'register' ) );
	}

	public function register(): void {
		register_post_type(
			'devwp_event',
			array(
				'label'        => __( 'Événements', 'devwp-solid-demo' ),
				'public'       => true,
				'show_in_rest' => true,
				'supports'     => array( 'title', 'editor', 'thumbnail' ),
			)
		);
	}
}

/**
 * Une autre raison de changer : l'affichage du dernier événement publié.
 */
final class Event_Shortcode implements Hookable {

	public function register_hooks(): void {
		add_shortcode( 'devwp_latest_event', array( $this, 'render' ) );
	}

	public function render(): string {
		$events = get_posts(
			array(
				'post_type'      => 'devwp_event',
				'posts_per_page' => 1,
			)
		);

		if ( empty( $events ) ) {
			return '';
		}

		$event = reset( $events );

		return sprintf(
			'<a href="%1$s">%2$s</a>',
			esc_url( get_permalink( $event ) ),
			esc_html( get_the_title( $event ) )
		);
	}
}

Le point clé : les hooks ne sont pas enregistrés dans le constructeur mais dans register_hooks(). On peut ainsi créer l’objet sans effet de bord, le tester, et décider à un seul endroit quand il s’accroche à WordPress. L’interface Hookable est présentée plus bas, dans la section I. Les sorties sont échappées (esc_url(), esc_html()) et les chaînes traduisibles avec le domaine de l’extension.

À l’échelle du projet, la même idée explique pourquoi, au lieu d’ajouter un système de gestion d’événements directement dans un thème, je crée une extension dédiée. Le thème gère la présentation, l’extension les fonctionnalités. Cette approche simplifie la maintenance et les mises à jour, car les modifications d’une fonctionnalité n’affectent pas d’autres aspects du site. Et changer de thème ne fait pas disparaître les événements.

O : le principe ouvert/fermé (OCP)

Ce principe affirme qu’une entité logicielle doit être ouverte à l’extension, mais fermée à la modification. Appliqué à WordPress, cela revient à concevoir thèmes et extensions de sorte qu’on puisse les étendre ou les personnaliser par des actions et des filtres, sans modifier leur code source.

Les hooks, mécanisme ouvert/fermé natif de WordPress

Les actions et les filtres ne servent pas qu’à se brancher sur le cœur : vous pouvez créer vos propres hooks avec do_action() et apply_filters() dans votre code. C’est exactement ce qui permet de personnaliser une extension sans modifier son code, et c’est la même logique qu’un thème enfant pour les gabarits d’un thème. Dans mes extensions, j’adopte ce principe en exposant des points d’extension clairs, permettant ainsi aux autres développeurs d’ajouter des fonctionnalités ou de modifier des comportements sans risquer de briser le fonctionnement initial. Documentez chaque hook par un bloc de commentaire : rôle, paramètres, valeur attendue (voir l’exemple ci-dessous).

Exemple : un registre de formats d’export extensible par filtre

<?php
namespace Devwp\Solid_Demo;

defined( 'ABSPATH' ) || exit;

interface Exporter {
	public function id(): string;
	public function export( array $rows ): string;
}

final class Csv_Exporter implements Exporter {

	public function id(): string {
		return 'csv';
	}

	public function export( array $rows ): string {
		// En production, neutralisez aussi les cellules qui commencent par =, +, - ou @
		// (injection de formules à l'ouverture dans un tableur).
		$handle = fopen( 'php://temp', 'r+' );
		foreach ( $rows as $row ) {
			fputcsv( $handle, $row, ',', '"', '' );
		}
		rewind( $handle );
		$csv = (string) stream_get_contents( $handle );
		fclose( $handle );

		return $csv;
	}
}

/**
 * Fermée à la modification, ouverte à l'extension grâce à un filtre.
 */
final class Export_Registry {

	/**
	 * @return Exporter[]
	 */
	public function all(): array {
		/**
		 * Filtre la liste des formats d'export disponibles.
		 *
		 * @param Exporter[] $exporters Formats enregistrés.
		 */
		$exporters = apply_filters( 'devwp_solid_demo_exporters', array( new Csv_Exporter() ) );

		// Un filtre peut renvoyer n'importe quoi : on ne garde que les vrais Exporter.
		return array_filter(
			(array) $exporters,
			static fn ( $exporter ): bool => $exporter instanceof Exporter
		);
	}
}

// Dans une autre extension, sans toucher au code ci-dessus :
// add_filter(
// 	'devwp_solid_demo_exporters',
// 	static function ( array $exporters ): array {
// 		$exporters[] = new \Mon_Extension\Json_Exporter(); // Implémente Exporter.
// 		return $exporters;
// 	}
// );

Pourquoi le filtrage par instanceof ? Un filtre peut renvoyer n’importe quoi, y compris une valeur cassée par une autre extension. Le registre ne garde que les objets qui respectent le contrat Exporter. Ajouter un format JSON ou XLSX revient à écrire une nouvelle classe ailleurs : Export_Registry ne change pas.

Une remarque pratique : pour retirer un callback avec remove_action(), il faut la même référence que celle qui a été accrochée, c’est-à-dire une fonction nommée ou l’instance exacte de l’objet. Une fonction anonyme, elle, ne peut pas être retirée par une autre extension, qui n’a aucun moyen d’en obtenir la référence. Si un comportement de votre extension doit pouvoir être désactivé, un filtre documenté (un booléen, par exemple) est plus fiable que de compter sur remove_action().

L : le principe de substitution de Liskov (LSP)

Formulé par Barbara Liskov, ce principe est le plus abstrait des cinq. Il dit que les objets d’un programme doivent être remplaçables par des instances de leurs sous-types sans affecter l’exécution correcte du programme. Concrètement : mêmes types, mêmes garanties, pas d’exception surprise. Le code appelant ne doit jamais avoir besoin de savoir quelle implémentation il manipule.

Ce que PHP vérifie, et ce qu’il ne vérifie pas

PHP contrôle les signatures : un type de retour peut être plus précis que celui du parent (covariance), un type de paramètre plus large (contravariance), et toute incompatibilité provoque une erreur fatale (covariance et contravariance sur php.net). Le comportement, en revanche, reste de votre responsabilité : la valeur renvoyée en cas d’absence, les exceptions levées, les effets de bord.

Exemple WordPress : get_transient() renvoie false, le contrat dit null

<?php
namespace Devwp\Solid_Demo;

defined( 'ABSPATH' ) || exit;

/**
 * Contrat : get() renvoie null si la clé est absente ou expirée.
 */
interface Cache_Store {
	public function get( string $key ): mixed;
	public function set( string $key, mixed $value, int $ttl ): void;
}

/**
 * get_transient() renvoie false quand la clé est absente ou expirée :
 * pour respecter le contrat, on convertit ce false en null.
 */
final class Transient_Store implements Cache_Store {

	public function get( string $key ): mixed {
		$value = get_transient( 'devwp_solid_demo_' . $key );

		return false === $value ? null : $value;
	}

	public function set( string $key, mixed $value, int $ttl ): void {
		set_transient( 'devwp_solid_demo_' . $key, $value, $ttl );
	}
}

/**
 * Implémentation pour les tests : même contrat (null si la clé est absente).
 * Elle ignore la durée de vie, ce qui suffit le temps d'un test.
 */
final class Memory_Store implements Cache_Store {

	private array $items = array();

	public function get( string $key ): mixed {
		return $this->items[ $key ] ?? null;
	}

	public function set( string $key, mixed $value, int $ttl ): void {
		$this->items[ $key ] = $value;
	}
}

get_transient() renvoie false quand la clé n’existe pas ou a expiré. Si Transient_Store renvoyait ce false tel quel, le code appelant devrait tester null pour Memory_Store et false pour l’autre : la substitution serait cassée. La conversion règle le problème, avec une limite à connaître : une valeur false stockée volontairement serait confondue avec une absence (stockez plutôt 0 ou un tableau).

Autres violations fréquentes : une implémentation qui lève une exception « non supporté » pour une méthode du contrat, ou qui renvoie un WP_Error là où les autres renvoient une valeur. Dès que l’appelant doit tester la classe concrète, le contrat ne sert plus à rien. Bonus de Memory_Store : les tests unitaires tournent sans base de données.

I : le principe de ségrégation des interfaces (ISP)

Il pose qu’aucun client, c’est-à-dire aucun code qui utilise une interface, ne doit être forcé de dépendre de méthodes qu’il n’utilise pas : mieux vaut plusieurs petites interfaces ciblées qu’une interface « monolithique ». Dans une extension, la tentation classique est une interface Module qui impose register_hooks(), activate(), deactivate(), uninstall(), un menu d’administration et des routes REST, et qui oblige la moitié des classes à implémenter des méthodes vides.

<?php
namespace Devwp\Solid_Demo;

defined( 'ABSPATH' ) || exit;

// De petites interfaces plutôt qu'une interface Module qui imposerait
// register_hooks(), activate(), deactivate(), uninstall(), add_admin_menu()...
interface Hookable {
	public function register_hooks(): void;
}

interface Activatable {
	public function activate(): void;
}

/**
 * N'a besoin que de l'activation : elle n'implémente que Activatable.
 */
final class Event_Capabilities implements Activatable {

	public function activate(): void {
		// Les administrateurs n'ont pas d'office les capacités personnalisées.
		foreach ( array( 'administrator', 'editor' ) as $role_name ) {
			$role = get_role( $role_name );

			if ( $role instanceof \WP_Role ) {
				$role->add_cap( 'export_devwp_events' );
			}
		}
	}
}

Event_Post_Type et Event_Shortcode implémentent Hookable ; Event_Capabilities n’a besoin que de l’interface Activatable. Une classe peut implémenter les deux : Event_Post_Type pourrait aussi être Activatable pour enregistrer le type de contenu puis vider les règles de réécriture à l’activation. La capacité export_devwp_events, donnée aux administrateurs et aux éditeurs, protégerait l’écran d’export (non montré) ; pensez à la retirer à la désinstallation. Voir aussi les interfaces dans le manuel PHP.

D : le principe d’inversion des dépendances (DIP) et l’injection de dépendances

Il demande de dépendre d’abstractions plutôt que d’implémentations concrètes : le code métier ne doit pas dépendre directement des détails techniques (wp_mail(), un SDK d’API, $wpdb), et ces détails doivent eux-mêmes se conformer à une abstraction. L’injection de dépendances est la technique qui le met en œuvre : on passe l’implémentation au constructeur au lieu de la créer à l’intérieur de la classe.

Une précision, car on lit souvent l’inverse : utiliser des hooks n’est pas, en soi, de l’inversion des dépendances. Les hooks relèvent de l’inversion de contrôle : ils découplent par événements (« quand ceci arrive, appelle cela »), pas par une abstraction typée. Les deux se complètent.

Exemple : un service de notification qui dépend d’une interface Mailer

<?php
namespace Devwp\Solid_Demo;

defined( 'ABSPATH' ) || exit;

interface Mailer {
	public function send( string $to, string $subject, string $message ): bool;
}

/**
 * Détail d'implémentation : l'API WordPress.
 */
final class Wp_Mailer implements Mailer {

	public function send( string $to, string $subject, string $message ): bool {
		return wp_mail( $to, $subject, $message );
	}
}

/**
 * La logique métier dépend de l'abstraction Mailer, pas de wp_mail().
 */
final class Registration_Notifier {

	public function __construct(
		private readonly Mailer $mailer
	) {}

	public function notify( string $email, string $event_title ): bool {
		if ( ! is_email( $email ) ) {
			return false;
		}

		return $this->mailer->send(
			$email,
			/* translators: %s: event title. */
			sprintf( __( 'Inscription confirmée : %s', 'devwp-solid-demo' ), $event_title ),
			__( 'Merci, votre inscription est enregistrée.', 'devwp-solid-demo' )
		);
	}
}

/**
 * En test : aucun e-mail réel n'est envoyé, on vérifie ce qui aurait été envoyé.
 */
final class Spy_Mailer implements Mailer {

	public array $sent = array();

	public function send( string $to, string $subject, string $message ): bool {
		$this->sent[] = $to;

		return true;
	}
}

Registration_Notifier ne sait pas comment l’e-mail part. En test, on lui passe Spy_Mailer et aucun message réel n’est envoyé. Le jour où les e-mails devront passer par l’API d’un service d’envoi transactionnel, on écrit une autre classe qui implémente Mailer, sans toucher au service. La même structure vaut pour une passerelle de paiement ou n’importe quelle API externe. La syntaxe private readonly Mailer $mailer déclare et affecte la propriété en une ligne (promotion de constructeur sur php.net).

Où assembler les objets : le fichier principal de l’extension

<?php
/**
 * Plugin Name:       Démo SOLID : inscriptions à des événements
 * Description:       Extension pédagogique illustrant les principes SOLID.
 * Version:           1.0.0
 * Requires PHP:      8.2
 * Text Domain:       devwp-solid-demo
 * License:           GPL-2.0-or-later
 */

namespace Devwp\Solid_Demo;

defined( 'ABSPATH' ) || exit;

// Chargement des interfaces puis des classes : require_once ou autoloader Composer (PSR-4).

$devwp_solid_demo_services = array(
	new Event_Post_Type(),
	new Event_Shortcode(),
	new Event_Capabilities(),
);

// Au chargement du fichier, pas dans plugins_loaded : pendant la requête d'activation,
// WordPress charge le fichier après ce hook, qui ne se déclenche donc plus.
register_activation_hook(
	__FILE__,
	static function () use ( $devwp_solid_demo_services ): void {
		foreach ( $devwp_solid_demo_services as $service ) {
			if ( $service instanceof Activatable ) {
				$service->activate();
			}
		}
	}
);

add_action(
	'plugins_loaded',
	static function () use ( $devwp_solid_demo_services ): void {
		foreach ( $devwp_solid_demo_services as $service ) {
			if ( $service instanceof Hookable ) {
				$service->register_hooks();
			}
		}

		// C'est ici, et seulement ici, qu'on choisit l'implémentation concrète.
		$notifier = new Registration_Notifier( new Wp_Mailer() );
		add_action( 'devwp_solid_demo_registered', array( $notifier, 'notify' ), 10, 2 );
	}
);

C’est le seul endroit qui connaît les classes concrètes. register_activation_hook() est appelé au chargement du fichier : pendant la requête d’activation, WordPress charge le fichier de l’extension après plugins_loaded, et un register_activation_hook() placé dans ce hook ne serait jamais enregistré. Les autres hooks, eux, sont enregistrés dans plugins_loaded. L’action devwp_solid_demo_registered serait déclenchée par le formulaire d’inscription (non montré) avec do_action( 'devwp_solid_demo_registered', $email, $title ). En vrai projet, chaque classe vit dans son fichier, chargé par un autoloader (Composer en PSR-4) ou par des require_once : les interfaces doivent être chargées avant les classes qui les implémentent.

Un conteneur d’injection de dépendances (PHP-DI ou équivalent) n’a d’intérêt que pour une grosse extension : ici, quelques lignes d’assemblage suffisent et restent lisibles par n’importe quel développeur WordPress.

Les limites de SOLID dans WordPress : ne pas sur-architecturer

Quelques lignes dans le functions.php d’un thème enfant ou une petite extension de trois hooks n’ont pas besoin d’interfaces : des fonctions préfixées et bien nommées suffisent. Chaque abstraction a un coût de lecture, et un code simple que tout le monde comprend vaut mieux qu’une architecture que personne n’ose toucher.

SOLID devient rentable quand apparaissent un ou plusieurs de ces signaux :

  • l’extension grossit et plusieurs personnes y travaillent ;
  • vous voulez des tests automatisés ;
  • le code parle à des services externes (API, paiement, e-mail transactionnel) ;
  • des variantes apparaissent : plusieurs passerelles, plusieurs formats d’export, plusieurs sources de données.

Méfiez-vous aussi du singleton (Plugin::instance()), très répandu dans l’écosystème WordPress. C’est le « S » de STUPID, le contre-modèle de SOLID : il cache les dépendances derrière un état global et rend les tests difficiles. L’alternative est celle de l’exemple : créer une seule instance dans le fichier principal et la passer à qui en a besoin.

Pour un thème WordPress sur mesure, la règle est la même : la logique métier reste hors des gabarits. Le gabarit affiche, une classe (ou une extension) calcule.

Enfin, gardez en tête les principes voisins : KISS (rester simple), YAGNI (ne pas coder ce dont on n’a pas encore besoin) et DRY (ne pas dupliquer une même connaissance). En pratique, commencez par S et D : séparer les responsabilités et isoler les appels externes derrière une interface. O, L et I suivent naturellement le jour où une deuxième implémentation apparaît.

Les principes SOLID en vidéo

J’apprécie cette vidéo en français, qui présente les principes SOLID : « PHP 8 – Programmation Orientée Objet – #19 Les principes SOLID », de la chaîne Laravel Jutsu. Elle fait partie d’une série sur la programmation orientée objet en PHP et n’est pas consacrée à WordPress, mais elle complète bien les exemples ci-dessus.

En anglais, la conférence WordCamp de Brian Reed, Creating SOLID (not STUPID) Plugins and Themes (wordpress.tv, 2016), est ancienne, mais elle applique directement ces principes aux extensions et aux thèmes.

Questions fréquentes sur les principes SOLID

Les principes SOLID sont cinq règles de conception orientée objet : responsabilité unique, ouvert/fermé, substitution de Liskov, ségrégation des interfaces et inversion des dépendances. Formalisés par Robert C. Martin au début des années 2000 et regroupés sous l’acronyme SOLID par Michael Feathers, ils visent un code plus facile à modifier, à tester et à réutiliser. Ce sont des repères, pas des lois : on les applique quand ils simplifient le code.
Les principes SOLID sont des règles générales de conception, alors que les design patterns sont des solutions types à des problèmes récurrents. Un patron comme Stratégie (plusieurs implémentations interchangeables d’une même interface, comme les formats d’export de cet article) applique les principes ouvert/fermé et d’inversion des dépendances. Les hooks de WordPress relèvent quant à eux du patron Observateur : des fonctions s’abonnent à un événement nommé.
Ce sont des principes complémentaires : DRY évite de dupliquer une même connaissance, KISS refuse la complexité inutile et SOLID organise les dépendances entre classes. Ils peuvent entrer en tension : multiplier les interfaces au nom de SOLID va contre KISS sur un petit projet. Dans ce cas, la simplicité l’emporte, et SOLID reprend la main quand le code grossit.
Non, ils s’appliquent à tout langage orienté objet : Java, C#, Python, TypeScript ou PHP. En PHP, les interfaces, les déclarations de type, la promotion de constructeur et les propriétés readonly (PHP 8.1 et plus) permettent de les exprimer clairement. Dans WordPress, ils concernent votre code (extensions, thèmes, mu-plugins), pas le cœur, qui reste majoritairement procédural.

En résumé

SOLID n’est pas une architecture à plaquer sur WordPress, mais une grille pour décider où couper son code : une classe par responsabilité, des hooks comme points d’extension, des contrats respectés par toutes les implémentations, de petites interfaces et des dépendances injectées depuis un seul point d’assemblage. Appliquez-la là où l’extension grossit, pas sur trois lignes de functions.php.

Vous reprenez une extension devenue difficile à faire évoluer, ou vous voulez en faire développer une qui tienne dans la durée ? Parlons-en.

Mis à jour en octobre 2026 par un développeur WordPress freelance senior.

Faites tourner cet article !

À propos de l'auteur : Développeur WordPress freelance

Développeur WordPress freelance
Développeur WordPress Full Stack avec 14 ans d’expérience, j’accompagne les agences et les entreprises dans la création, la refonte et l’optimisation de leurs sites WordPress et boutiques WooCommerce. Développement sur mesure, maintenance, sécurité ou dépannage : je mets mon expertise au service de sites performants, fiables et adaptés à vos objectifs.