Principes SOLID en PHP : application avancée et exemples pratiques

Formateur Python en ligne pour débutants

Apprenez Python facilement sans surcharge théorique. Résolvez des tâches pratiques avec vérification automatique et écrivez du code directement dans le navigateur.

Start Course

Introduction : Pourquoi SOLID n'est pas un simple dogme, mais un outil

\n

La plupart des développeurs savent que SOLID regroupe cinq principes de programmation orientée objet : Single Responsibility, Open-Closed, Liskov Substitution, Interface Segregation et Dependency Inversion. Cependant, dans la pratique, la compréhension se résume souvent à une application mécanique des règles. Dans cet article, nous allons plus loin : nous analysons les scénarios avancés d'application de SOLID en PHP, examinons les antipatterns qui violent ces principes, et montrons comment le refactoring avec SOLID rend le code flexible et testable.

\n

Ce contenu est destiné aux développeurs de niveau Middle+ et Senior qui codent déjà en PHP et souhaitent améliorer l'architecture de leurs projets.

\n\n

1. Principe de responsabilité unique (SRP) : quand une classe « en sait trop »

\n

Formulation classique : « Une classe ne doit avoir qu'une seule raison de changer ». Au niveau avancé, il est important de comprendre que « raison de changer » correspond à une exigence métier. Si votre classe change en raison de modifications des règles de validation, du format de sortie ou de la logique de sauvegarde, c'est une violation du SRP.

\n\n

Exemple de violation (God Object)

\n
class OrderProcessor {\n    public function process(array $data): void {\n        // 1. Validation des données\n        if (empty($data['user_id']) || empty($data['items'])) {\n            throw new InvalidArgumentException('Invalid order data');\n        }\n        \n        // 2. Calcul du total\n        $total = 0;\n        foreach ($data['items'] as $item) {\n            $total += $item['price'] * $item['quantity'];\n        }\n        \n        // 3. Sauvegarde en base de données\n        $sql = "INSERT INTO orders (user_id, total, status) VALUES (?, ?, 'pending')";\n        // ... exécution de la requête\n        \n        // 4. Envoi d'email\n        mail($data['user_email'], 'Order confirmation', "Total: $total");\n    }\n}
\n\n

Refactoring avec SRP

\n

Nous séparons en trois classes avec des zones de responsabilité claires :

\n
class OrderValidator {\n    public function validate(array $data): void {\n        if (empty($data['user_id']) || empty($data['items'])) {\n            throw new InvalidArgumentException('Invalid order data');\n        }\n    }\n}\n\nclass OrderCalculator {\n    public function calculateTotal(array $items): float {\n        return array_reduce($items, function ($carry, $item) {\n            return $carry + $item['price'] * $item['quantity'];\n        }, 0.0);\n    }\n}\n\nclass OrderRepository {\n    public function save(int $userId, float $total, string $status = 'pending'): int {\n        // Logique de sauvegarde en base de données\n        return 42; // ID de la commande\n    }\n}\n\nclass OrderProcessor {\n    public function __construct(\n        private OrderValidator $validator,\n        private OrderCalculator $calculator,\n        private OrderRepository $repository,\n        private Mailer $mailer\n    ) {}\n    \n    public function process(array $data): void {\n        $this->validator->validate($data);\n        $total = $this->calculator->calculateTotal($data['items']);\n        $orderId = $this->repository->save($data['user_id'], $total);\n        $this->mailer->send($data['user_email'], 'Order #' . $orderId);\n    }\n}
\n\n

2. Principe d'ouverture/fermeture (OCP) : extension sans modification

\n

L'essence de l'OCP : les classes doivent être ouvertes à l'extension, mais fermées à la modification. En pratique, cela signifie utiliser le polymorphisme et les abstractions plutôt que des constructions conditionnelles.

\n\n

Violation de l'OCP : switch-case pour chaque nouveau type

\n
class PaymentProcessor {\n    public function pay(string $type, float $amount): void {\n        switch ($type) {\n            case 'credit_card':\n                // traitement de la carte\n                break;\n            case 'paypal':\n                // traitement PayPal\n                break;\n            // Lors de l'ajout d'un nouveau type — on modifie la classe !\n        }\n    }\n}                

Blogs

Book Recommendations