PHP Academy · Leçon

Inversion des dépendances en pratique

Reliez les adaptateurs au cœur grâce à l’inversion des dépendances.

Leçon 4 sur 413 étapes

Inversion des dépendances en pratique est une leçon PHP Academy gratuite sur CoddyKit. Ceci est la leçon 4 sur 4. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage PHP Academy, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours PHP Academy comprend 4 leçons au total.

Inversion et injection

Ces deux notions distinctes sont souvent confondues. L’injection de dépendances est une technique : transmettre les collaborateurs au lieu de les construire. L’inversion des dépendances (le D de SOLID) est un principe : la politique de haut niveau et les détails de bas niveau dépendent tous deux d’une abstraction, et l’abstraction appartient au module de haut niveau. Cette leçon explique comment rendre le second principe concret — en reliant des adaptateurs concrets à un cœur qui définit les interfaces.

Le principe, avec précision

Le DIP affirme que :

  • Les modules de haut niveau ne doivent pas dépendre des modules de bas niveau. Tous deux dépendent d’abstractions.
  • Les abstractions ne doivent pas dépendre des détails. Les détails dépendent des abstractions.

La subtilité est la suivante : l’interface appartient au consommateur, et non à l’implémenteur. Votre domaine déclare PaymentGateway ; l’adaptateur Stripe s’y conforme — et non l’inverse.

Définir l’abstraction dans le cœur

Placez l’interface à côté du code qui en a besoin et exprimez-la dans les termes du domaine. Aucun type Stripe ne doit s’y infiltrer.

<?php
// src/Billing/Application/Port/PaymentGateway.php
interface PaymentGateway
{
    public function charge(Money $amount, CardToken $token): ChargeId;
}

final class Money {
    public function __construct(public readonly int $cents, public readonly string $currency) {}
}
final class CardToken { public function __construct(public readonly string $value) {} }
final class ChargeId { public function __construct(public readonly string $value) {} }

Faire correspondre un adaptateur à celle-ci

L’adaptateur d’infrastructure implémente l’interface du cœur et traduit les appels vers le SDK du fournisseur. La flèche de dépendance va de l’adaptateur (détail) vers l’abstraction (politique) : l’inversion est réalisée.

<?php
final class StripePaymentGateway implements PaymentGateway
{
    public function __construct(private \Stripe\StripeClient $stripe) {}

    public function charge(Money $amount, CardToken $token): ChargeId {
        $intent = $this->stripe->paymentIntents->create([
            'amount'   => $amount->cents,
            'currency' => strtolower($amount->currency),
            'payment_method' => $token->value,
            'confirm'  => true,
        ]);
        return new ChargeId($intent->id);
    }
}

L’injection par constructeur par défaut

Injectez les dépendances par le constructeur et indiquez l’abstraction dans la déclaration de type. Les dépendances deviennent explicites, immuables et impossibles à oublier. Évitez l’injection par accesseur ou par propriété pour les collaborateurs obligatoires : elle permet de créer des objets partiellement construits.

<?php
final class CheckoutService
{
    public function __construct(
        private PaymentGateway $payments,   // abstraction, not StripeClient
        private OrderRepository $orders,
    ) {}

    public function pay(OrderId $id, CardToken $token): ChargeId {
        $order  = $this->orders->get($id);
        $charge = $this->payments->charge($order->total(), $token);
        $order->markPaid($charge);
        $this->orders->save($order);
        return $charge;
    }
}

La racine de composition

Tout l’assemblage concret s’effectue à un seul endroit — la racine de composition — aussi près que possible de main() ou du point d’entrée. Rien d’autre ne crée l’infrastructure avec new. C’est le seul endroit qui sait que Stripe existe.

<?php
// public/index.php — composition root
$stripe   = new \Stripe\StripeClient(getenv('STRIPE_SECRET'));
$gateway  = new StripePaymentGateway($stripe);
$orders   = new PdoOrderRepository(new PDO(getenv('DB_DSN')));
$checkout = new CheckoutService($gateway, $orders);

// Everything below depends only on abstractions
$controller = new CheckoutController($checkout);

Assembler avec un conteneur DI

Pour les applications non triviales, un conteneur (PHP-DI, Symfony) automatise l’assemblage. L’opération essentielle consiste à associer les interfaces aux implémentations. L’assemblage automatique résout les constructeurs grâce aux types ; vous ne déclarez que la correspondance interface→classe.

<?php
use function DI\autowire;
use function DI\get;

return [
    PaymentGateway::class => autowire(StripePaymentGateway::class),
    OrderRepository::class => autowire(PdoOrderRepository::class),
    \Stripe\StripeClient::class => fn() => new \Stripe\StripeClient(getenv('STRIPE_SECRET')),
    PDO::class => fn() => new PDO(getenv('DB_DSN')),
];

Remplacer les adaptateurs prouve le principe

Comme le cœur ne dépend que de PaymentGateway, changer de fournisseur ou tester hors ligne ne nécessite qu’une modification d’une ligne. Voici une fausse implémentation utilisée dans un test unitaire : le CheckoutService reste inchangé et n’importe jamais Stripe.

<?php
final class FakeGateway implements PaymentGateway {
    public array $charges = [];
    public function charge(Money $a, CardToken $t): ChargeId {
        $this->charges[] = $a;
        return new ChargeId('ch_test_' . count($this->charges));
    }
}

$fake = new FakeGateway();
$id = $fake->charge(new Money(2500, 'EUR'), new CardToken('tok_visa'));
echo $id->value, ' charges=', count($fake->charges), PHP_EOL; // ch_test_1 charges=1

Éviter le piège du localisateur de services

Injecter le conteneur lui-même et récupérer les dépendances dans les méthodes constitue l’anti-modèle du localisateur de services. Cela masque les dépendances, neutralise la vérification des types et recouple votre code au conteneur. Injectez explicitement ce dont vous avez besoin.

<?php
// ANTI-PATTERN: hidden dependencies, container leaks everywhere
final class BadCheckout {
    public function __construct(private ContainerInterface $c) {}
    public function pay($id, $token) {
        $gateway = $this->c->get(PaymentGateway::class); // hidden!
        // ...
    }
}

Où le conteneur peut apparaître

Le conteneur n’est légitime que dans une seule couche : la racine de composition et le code de liaison du framework qui l’entoure (par exemple, une fabrique de contrôleurs). Votre domaine et vos classes applicatives doivent rester indépendants du conteneur : elles reçoivent des objets ordinaires par leurs constructeurs et pourraient être instanciées manuellement. Un bon test consiste à se demander : pourriez-vous assembler toute l’application dans un seul fichier PHP sans conteneur ? Si oui, vos dépendances sont explicites.

Assemblage différé sans localisateur de services

Parfois, une dépendance est coûteuse à construire ou n’est nécessaire que sous certaines conditions. Résistez à la tentation d’injecter le conteneur : injectez plutôt une fermeture de fabrique. La dépendance reste explicite et typée, tandis que sa construction est différée jusqu’à son utilisation effective.

<?php
final class ReportService
{
    /** @param Closure():PaymentGateway $gatewayFactory */
    public function __construct(private Closure $gatewayFactory) {}

    public function refundIfNeeded(bool $needed): void {
        if (!$needed) return;
        $gateway = ($this->gatewayFactory)(); // built only when required
        // $gateway->charge(...) etc.
    }
}

// Composition root supplies the factory, not the container
$svc = new ReportService(fn() => new StripePaymentGateway($stripe ?? null));
echo 'lazy dependency wired', PHP_EOL;

Vérification rapide

À qui doit appartenir l’interface PaymentGateway ?

Récapitulatif

Vous avez relié les adaptateurs au cœur de la bonne manière :

  • L’inversion n’est pas l’injection : l’injection est le mécanisme, tandis que l’inversion consiste à faire appartenir l’abstraction au consommateur.
  • Le cœur définit l’interface ; les adaptateurs d’infrastructure s’y conforment.
  • Utilisez l’injection par constructeur d’abstractions ; réalisez tout l’assemblage concret dans une seule racine de composition (ou une configuration de conteneur qui associe interface→classe).
  • Remplacer les adaptateurs et utiliser des doublures dans les tests ne nécessite qu’une modification d’une ligne.
  • Évitez l’anti-modèle du localisateur de services — gardez le conteneur uniquement à la périphérie.
Gratuit pour commencer

Apprends PHP avec un tuteur IA — gratuit

Écris et exécute du vrai code dans ton navigateur, obtiens de l'aide instantanée d'un tuteur IA disponible 24h/24, et reprends là où tu t'es arrêté sur le web ou dans l'app.

Cours
49
Leçons
195

Questions Fréquemment Posées

La leçon « Inversion des dépendances en pratique » est-elle gratuite ?

Oui — le texte complet de « Inversion des dépendances en pratique » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours PHP Academy, passe à CoddyKit PRO. Le cours PHP Academy comprend 4 leçons au total.

Qu'est-ce que j'apprendrai dans « Inversion des dépendances en pratique » ?

Reliez les adaptateurs au cœur grâce à l’inversion des dépendances. Tu pratiques PHP Academy avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.

Dois-je avoir de l'expérience pour commencer PHP Academy ?

Aucune expérience préalable n'est requise. PHP Academy sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 4 sur 4.

Combien de temps prend la leçon « Inversion des dépendances en pratique » ?

La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.

Peux-tu écrire et exécuter du code dans cette leçon PHP Academy ?

Oui. Chaque leçon PHP Academy inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.

Toutes les leçons de ce cours

  1. De l’architecture en couches à l’architecture propre
  2. Ports et adaptateurs expliqués
  3. Cas d’utilisation et services applicatifs
  4. Inversion des dépendances en pratique
← Retour à PHP Academy