OOP Mastery (POO)
Thème :
Junior
1 vues ·17/17 leçons

Construire un Conteneur DI Simple

Un Conteneur DI gère automatiquement la création des objets et la résolution des dépendances.

Le Conteneur de Base

<?php
class Container {
    private array $bindings = [];
    private array $instances = [];

    // Lier une interface à une classe concrète
    public function bind(string $abstract, string|callable $concrete): void {
        $this->bindings[$abstract] = $concrete;
    }

    // Lier un singleton
    public function singleton(string $abstract, string|callable $concrete): void {
        $this->bind($abstract, function($container) use ($abstract, $concrete) {
            if (!isset($this->instances[$abstract])) {
                $this->instances[$abstract] = is_callable($concrete)
                    ? $concrete($container)
                    : $container->make($concrete);
            }
            return $this->instances[$abstract];
        });
    }

    // Résoudre une classe
    public function make(string $abstract): object {
        // Vérifier si on a une liaison
        if (isset($this->bindings[$abstract])) {
            $concrete = $this->bindings[$abstract];

            if (is_callable($concrete)) {
                return $concrete($this);
            }

            return $this->make($concrete);
        }

        // Résoudre automatiquement via la réflexion
        return $this->resolve($abstract);
    }

    private function resolve(string $class): object {
        $reflection = new ReflectionClass($class);

        if (!$reflection->isInstantiable()) {
            throw new Exception("Impossible d'instancier $class");
        }

        $constructor = $reflection->getConstructor();

        if ($constructor === null) {
            return new $class();
        }

        $dependencies = [];

        foreach ($constructor->getParameters() as $param) {
            $type = $param->getType();

            if ($type === null || $type->isBuiltin()) {
                if ($param->isDefaultValueAvailable()) {
                    $dependencies[] = $param->getDefaultValue();
                } else {
                    throw new Exception("Impossible de résoudre le param : {$param->getName()}");
                }
            } else {
                $dependencies[] = $this->make($type->getName());
            }
        }

        return $reflection->newInstanceArgs($dependencies);
    }
}

Utiliser le Conteneur

<?php
$container = new Container();

// Lier les interfaces aux implémentations
$container->bind(UserRepositoryInterface::class, MySQLUserRepository::class);
$container->bind(MailerInterface::class, SmtpMailer::class);

// Lier avec une fonction fabrique
$container->singleton(PDO::class, function() {
    return new PDO(
        'mysql:host=localhost;dbname=app',
        'user',
        'pass',
        [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]
    );
});

// Résolution automatique avec les dépendances
$userService = $container->make(UserService::class);
// Le conteneur injecte automatiquement :
// - UserRepositoryInterface (résolu en MySQLUserRepository)
// - MailerInterface (résolu en SmtpMailer)

L’Anti-Pattern Service Locator (À Éviter)

<?php
// MAUVAIS : Anti-pattern Service Locator
class BadService {
    public function __construct(private Container $container) {}

    public function doSomething(): void {
        $repo = $this->container->make(UserRepository::class);  // Dépendance cachée !
    }
}

// BON : Dépendances explicites
class GoodService {
    public function __construct(private UserRepository $repo) {}  // Visible !
}

📘 Cette leçon fait partie du cours Maîtrise de la POO en PHP sur la plateforme d’apprentissage RostoDev.

Prêt à passer à la suite ?

Vous avez terminé cette leçon sur Construire un Conteneur DI Simple. Continuez votre apprentissage avec la prochaine leçon ou explorez d'autres formations.