Skip to content

PHP Dependency Injection Container — Complete Guide to PHP-DI and Autowiring

DodaTech Updated 2026-06-28 4 min read

In this tutorial, you will learn about PHP Dependency Injection Container. We cover key concepts, practical examples, and best practices to help you master this topic.

PHP dependency injection containers like PHP-DI resolve class dependencies automatically through autowiring, with configuration via PHP attributes, definitions, and factory methods.

What You'll Learn

By the end of this tutorial, you'll configure PHP-DI, use autowiring with attributes, define services with factories, handle environment-specific configuration, and integrate DI in applications.

Why DI Containers Matter

Manual dependency wiring becomes unmanageable as applications grow. DI containers automate resolution, manage service lifecycles, and enable testing through dependency substitution.

Real-World Use

A REST API using PHP-DI autowires all controllers, services, and repositories. Swapping the database driver from MySQL to PostgreSQL requires changing only one definition.

DI Container Path

flowchart LR
  A[PSR-11 Container] --> B[PHP-DI Container]
  B --> C[PSR-17 HTTP Factory]
  B --> D[Middleware Slim]
  C --> E[Router PHP]
  B --> F{You Are Here}
  style F fill:#f90,color:#fff

PHP-DI Setup

Install and configure PHP-DI with autowiring.

<?php
use DI\ContainerBuilder;
require_once "vendor/autoload.php";
$builder = new ContainerBuilder();
$builder->useAutowiring(true);
$builder->useAttributes(true);
$container = $builder->build();
$service = $container->get(UserService::class);

Definition Files

Define service configurations in a PHP file.

<?php
// config/services.php
use function DI\autowire;
use function DI\get;
use function DI\create;
return [
    PDO::class => function () {
        return new PDO(
            $_ENV["DB_DSN"] ?? "mysql:host=localhost;dbname=app",
            $_ENV["DB_USER"] ?? "root",
            $_ENV["DB_PASS"] ?? ""
        );
    },
    LoggerInterface::class => autowire(Monolog\Logger::class)
        ->constructor("app", [get(Monolog\Handler\StreamHandler::class)]),
    Monolog\Handler\StreamHandler::class => autowire()
        ->constructor("/var/log/app.log"),
    UserRepository::class => autowire(),
    UserService::class => autowire(),
];

Autowiring with Attributes

PHP-DI supports PHP 8 attributes for injection configuration.

<?php
use DI\Attribute\Injectable;
use Psr\Container\ContainerInterface;
#[Injectable]
class OrderService {
    public function __construct(
        private OrderRepository $repository,
        private PaymentGateway $gateway,
        private LoggerInterface $logger,
    ) {}
    public function placeOrder(array $items): Order {
        $this->logger->info("Placing order");
        $order = $this->repository->create($items);
        $this->gateway->charge($order->total);
        return $order;
    }
}

Factory Methods

Use factory methods for complex object creation.

<?php
use function DI\factory;
class PdoFactory {
    public static function create(): PDO {
        return new PDO(
            sprintf("mysql:host=%s;dbname=%s", $_ENV["DB_HOST"], $_ENV["DB_NAME"]),
            $_ENV["DB_USER"],
            $_ENV["DB_PASS"],
            [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]
        );
    }
}
// config/services.php
return [
    PDO::class => factory([PdoFactory::class, "create"]),
];

Environment-Specific Configuration

Use different configurations for development and production.

<?php
$builder = new ContainerBuilder();
$builder->useAutowiring(true);
$env = $_ENV["APP_ENV"] ?? "production";
$builder->addDefinitions(__DIR__ . "/config/services.php");
if ($env === "development") {
    $builder->addDefinitions(__DIR__ . "/config/services_dev.php");
}
$container = $builder->build();

Common Mistakes

1. Overusing the Container as Service Locator

Inject the container into controllers instead of specific dependencies. This hides dependencies and makes testing harder.

2. Not Using Autowiring for Simple Cases

If a class has no special construction needs, autowire it. Manual definitions for everything is unnecessary.

3. Forgetting to Register Interface Bindings

Interfaces need explicit mapping to implementations. Autowiring works on concrete classes, not interfaces.

4. Circular Dependencies

A circular dependency chain causes infinite Recursion. Restructure to break the cycle.

5. Heavy Object Creation in Definitions

Factory closures should create and return objects, not perform heavy computation.

Practice Questions

1. What is autowiring?

Automatic resolution of constructor dependencies by type-hint, without manual configuration.

2. How do you bind an interface to an implementation?

$container->set(Interface::class, \DI\autowire(Implementation::class)) in definitions.

3. What is the difference between autowire() and create()?

autowire() resolves constructor arguments from the container. create() creates using provided arguments.

4. How do you handle multiple implementations of the same interface?

Use named bindings or tags to distinguish them. Inject using specific names.

5. Challenge: Create a DI container configuration for a multi-environment application.

<?php
$builder = new ContainerBuilder();
$builder->useAutowiring(true);
$builder->addDefinitions(__DIR__ . "/config/common.php");
if ($_ENV["APP_ENV"] === "test") {
    $builder->addDefinitions(__DIR__ . "/config/test.php");
}
$container = $builder->build();

FAQ

Is PHP-DI the only DI container?

No. Symfony DI, Laravel container, and Dice are popular alternatives. PHP-DI focuses on autowiring.

Does PHP-DI support caching?

Yes. Enable definition caching in production with $builder->enableCompilation() for better performance.

Can I use PHP-DI without autowiring?

Yes. Define everything manually with factory closures.

How does PHP-DI compare to Symfony DI?

PHP-DI is simpler and attribute-focused. Symfony DI has more features for enterprise applications.

What is AOP in PHP-DI?

Aspect-oriented programming allows intercepting method calls for logging, caching, or transactions.

Mini Project: Application Bootstrap with PHP-DI

Build an application bootstrap that uses PHP-DI for dependency injection.

<?php
use DI\ContainerBuilder;
use function DI\autowire;
use function DI\create;
class AppBootstrap {
    private \DI\Container $container;
    public function __construct() {
        $builder = new ContainerBuilder();
        $builder->useAutowiring(true);
        $builder->useAttributes(true);
        $builder->addDefinitions([
            PDO::class => fn() => new PDO($_ENV["DB_DSN"], $_ENV["DB_USER"], $_ENV["DB_PASS"]),
            LoggerInterface::class => fn() => new Monolog\Logger("app", [
                new Monolog\Handler\StreamHandler("php://stdout"),
            ]),
        ]);
        $this->container = $builder->build();
    }
    public function run(): void {
        $controller = $this->container->get(AppController::class);
        $controller->handle();
    }
}

What's Next

PHP Middleware Slim PHP Router PHP PSR-11 Container

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro