Les design patterns PHP les plus utiles en 2026 (avec exemples)
Les design patterns PHP sont des solutions éprouvées à des problèmes récurrents de conception. Ce guide pratique vous présente les 8 patterns les plus utiles — Singleton, Repository, Factory, Observer — avec des exemples PHP 8 concrets.
Les design patterns sont des solutions réutilisables à des problèmes de conception logicielle récurrents. Les maîtriser transforme votre code PHP en quelque chose de maintenable, extensible et lisible par toute l’équipe. Voici les plus utiles avec des exemples PHP 8 directs.
1. Repository Pattern
Sépare la logique d’accès aux données de la logique métier. Le code qui utilise les données n’a pas besoin de savoir d’où elles viennent (MySQL, API, fichier) :
interface ArticleRepositoryInterface {
public function findById(int $id): ?Article;
public function findAll(): array;
public function save(Article $article): void;
}
class MySQLArticleRepository implements ArticleRepositoryInterface {
public function findById(int $id): ?Article {
$row = $this->pdo->prepare('SELECT * FROM articles WHERE id = ?');
$row->execute([$id]);
return $row->fetch() ? Article::fromArray($row->fetch()) : null;
}
// ...
}
2. Factory Pattern
Crée des objets sans exposer la logique de création :
class NotificationFactory {
public static function create(string $channel): NotificationInterface {
return match($channel) {
'email' => new EmailNotification(),
'sms' => new SMSNotification(),
'slack' => new SlackNotification(),
default => throw new InvalidArgumentException("Channel inconnu: $channel"),
};
}
}
$notif = NotificationFactory::create('email');
$notif->send($message);
3. Observer Pattern
Permet à des objets de s’abonner à des événements d’autres objets. C’est la base des systèmes d’événements :
interface EventListener {
public function handle(Event $event): void;
}
class EventDispatcher {
private array $listeners = [];
public function listen(string $event, EventListener $listener): void {
$this->listeners[$event][] = $listener;
}
public function dispatch(Event $event): void {
foreach ($this->listeners[$event::class] ?? [] as $listener) {
$listener->handle($event);
}
}
}
// Usage
$dispatcher->listen(UserRegistered::class, new SendWelcomeEmail());
$dispatcher->listen(UserRegistered::class, new CreateUserProfile());
$dispatcher->dispatch(new UserRegistered($user));
4. Decorator Pattern
Ajoute des comportements à un objet sans modifier sa classe :
interface Logger { public function log(string $message): void; }
class FileLogger implements Logger {
public function log(string $message): void { file_put_contents('app.log', $message . "n", FILE_APPEND); }
}
class TimestampLogger implements Logger {
public function __construct(private Logger $inner) {}
public function log(string $message): void {
$this->inner->log('[' . date('c') . '] ' . $message);
}
}
$logger = new TimestampLogger(new FileLogger());
$logger->log('Application démarrée'); // [2026-01-15T...] Application démarrée
5. Strategy Pattern
Encapsule des algorithmes interchangeables :
interface PricingStrategy {
public function calculate(float $basePrice): float;
}
class StandardPricing implements PricingStrategy {
public function calculate(float $basePrice): float { return $basePrice; }
}
class MemberPricing implements PricingStrategy {
public function calculate(float $basePrice): float { return $basePrice * 0.85; }
}
class Cart {
public function __construct(private PricingStrategy $pricing) {}
public function total(float $price): float { return $this->pricing->calculate($price); }
}
6. Singleton (et pourquoi l’éviter)
Le Singleton garantit qu’une classe n’a qu’une seule instance. Souvent cité, mais à éviter en général car il crée un couplage global difficile à tester. Préférez l’injection de dépendances et laissez votre conteneur DI gérer les instances unique.
7. Command Pattern
Encapsule une action dans un objet — permet d’annuler, rejouer, logger les commandes :
interface Command { public function execute(): void; }
class PublishArticleCommand implements Command {
public function __construct(private Article $article, private ArticleRepository $repo) {}
public function execute(): void {
$this->article->publish();
$this->repo->save($this->article);
}
}
$bus->dispatch(new PublishArticleCommand($article, $repo));
8. Value Object
Représente une valeur immuable avec sa propre logique de validation :
final class Email {
private string $value;
public function __construct(string $email) {
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
throw new InvalidArgumentException("Email invalide: $email");
}
$this->value = strtolower($email);
}
public function __toString(): string { return $this->value; }
public function equals(Email $other): bool { return $this->value === $other->value; }
}
$email = new Email('Alice@EXAMPLE.COM'); // alice@example.com — toujours valide
Les Value Objects éliminent les classes entières de bugs liés aux données invalides. C’est l’un des patterns DDD les plus accessibles et les plus rentables à adopter immédiatement.