SkillAgentSearch skills...

Di

PSR-11 compatible DI container and injector

Install / Use

/learn @yiisoft/Di
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

<p align="center"> <a href="https://github.com/yiisoft" target="_blank"> <img src="https://yiisoft.github.io/docs/images/yii_logo.svg" height="100px" alt="Yii"> </a> <h1 align="center">Yii Dependency Injection</h1> <br> </p>

Latest Stable Version Total Downloads Build status Code coverage Mutation testing badge static analysis type-coverage

PSR-11 compatible dependency injection container that's able to instantiate and configure classes resolving dependencies.

Features

  • PSR-11 compatible.
  • Supports property injection, constructor injection, and method injection.
  • Detects circular references.
  • Accepts array definitions. You can use it with mergeable configs.
  • Provides optional autoload fallback for classes without explicit definition.
  • Allows delegated lookup and has a composite container.
  • Supports aliasing.
  • Supports service providers.
  • Has state resetter for long-running workers serving many requests, such as RoadRunner or Swoole.
  • Supports container delegates.
  • Does auto-wiring.

[!NOTE] The container contains only shared instances. If you need a factory, use the dedicated yiisoft/factory package.

Requirements

  • PHP 8.1 - 8.5.
  • Multibyte String PHP extension.

Installation

You could install the package with composer:

composer require yiisoft/di

Using the container

Usage of the DI container is simple: You first initialize it with an array of definitions. The array keys are usually interface names. It will then use these definitions to create an object whenever the application requests that type. This happens, for example, when fetching a type directly from the container somewhere in the application. But objects are also created implicitly if a definition has a dependency on another definition.

Usually one uses a single container for the whole application. It's often configured either in the entry script such as index.php or a configuration file:

use Yiisoft\Di\Container;
use Yiisoft\Di\ContainerConfig;

$config = ContainerConfig::create()
    ->withDefinitions($definitions);

$container = new Container($config);

You could store the definitions in a .php file that returns an array:

return [
    // resolve EngineMarkOne dependencies automatically
    EngineInterface::class => EngineMarkOne::class,
    
    // full definition
    MyServiceInterface::class => [
        'class' => MyService::class,
        
        // call the constructor, pass named argument "amount"
        '__construct()' => [
            'amount' => 42,
            'db' => Reference::to(SecondaryConnection::class), // instance of another dependency
        ],
        
        // set a public property
        '$name' => 'Alex',
        
        // call a public method
        'setDiscount()' => [10],
    ],
    
    // closure for complicated cases
    AnotherServiceInterface::class => static function(ConnectionInterface $db) {
        return new AnotherService($db);
    },
    
    // factory
    MyObjectInterface::class => fn () => MyFactory::create('args'),
    
    // static call
    MyObjectInterface2::class => [MyFactory::class, 'create'],
    
    // direct instance
    MyInterface::class => new MyClass(),
];

You can define an object in several ways:

  • In the simple case, an interface definition maps an id to a particular class.
  • A full definition describes how to instantiate a class in more detail:
    • class has the name of the class to instantiate.
    • __construct() holds an array of constructor arguments.
    • The rest of the config is property values (prefixed with $) and method calls, postfixed with (). They're set/called in the order they appear in the array.
  • Closures are useful if instantiation is tricky and can be better done in code. When using these, arguments are auto-wired by type. ContainerInterface could be used to get current container instance.
  • If it's even more complicated, it's a good idea to move such a code into a factory and reference it as a static call.
  • While it's usually not a good idea, you can also set an already instantiated object into the container.

See yiisoft/definitions for more information.

After you configure the container, you can obtain a service via get():

/** @var \Yiisoft\Di\Container $container */
$object = $container->get('interface_name');

Note, however, that it's bad practice using a container directly. It's much better to rely on auto-wiring as provided by the Injector available from the yiisoft/injector package.

Using aliases

The DI container supports aliases via the Yiisoft\Definitions\Reference class. This way you can retrieve objects by a more handy name:

use Yiisoft\Di\Container;
use Yiisoft\Di\ContainerConfig;

$config = ContainerConfig::create()
    ->withDefinitions([
        EngineInterface::class => EngineMarkOne::class,
        'engine_one' => EngineInterface::class,
    ]);

$container = new Container($config);
$object = $container->get('engine_one');

Using class aliases for specific configuration

To define another instance of a class with specific configuration, you can use native PHP class_alias():

class_alias(Yiisoft\Db\Pgsql\Connection::class, 'MyPgSql');

$config = ContainerConfig::create()                                                                                                                                                     
    ->withDefinitions([
        MyPgSql::class => [ ... ]
    ]);                                                                                                                                                                                 

$container = new Container($config);
$object = $container->get(MyPgSql::class);

It could be then conveniently used by type-hinting:

final class MyService
{
    public function __construct(MyPgSql $myPgSql)
    {
        // ...    
    }
} 

Composite containers

A composite container combines many containers in a single container. When using this approach, you should fetch objects only from the composite container.

use Yiisoft\Di\CompositeContainer;
use Yiisoft\Di\Container;
use Yiisoft\Di\ContainerConfig;

$composite = new CompositeContainer();

$carConfig = ContainerConfig::create()
    ->withDefinitions([
        EngineInterface::class => EngineMarkOne::class,
        CarInterface::class => Car::class
    ]);
$carContainer = new Container($carConfig);

$bikeConfig = ContainerConfig::create()
    ->withDefinitions([
        BikeInterface::class => Bike::class
    ]);

$bikeContainer = new Container($bikeConfig);
$composite->attach($carContainer);
$composite->attach($bikeContainer);

// Returns an instance of a `Car` class.
$car = $composite->get(CarInterface::class);
// Returns an instance of a `Bike` class.
$bike = $composite->get(BikeInterface::class);

Note that containers attached earlier override dependencies of containers attached later.

use Yiisoft\Di\CompositeContainer;
use Yiisoft\Di\Container;
use Yiisoft\Di\ContainerConfig;

$carConfig = ContainerConfig::create()
    ->withDefinitions([
        EngineInterface::class => EngineMarkOne::class,
        CarInterface::class => Car::class
    ]);

$carContainer = new Container($carConfig);

$composite = new CompositeContainer();
$composite->attach($carContainer);

// Returns an instance of a `Car` class.
$car = $composite->get(CarInterface::class);
// Returns an instance of a `EngineMarkOne` class.
$engine = $car->getEngine();

$engineConfig = ContainerConfig::create()
    ->withDefinitions([
        EngineInterface::class => EngineMarkTwo::class,
    ]);

$engineContainer = new Container($engineConfig);

$composite = new CompositeContainer();
$composite->attach($engineContainer);
$composite->attach($carContainer);

// Returns an instance of a `Car` class.
$car = $composite->get(CarInterface::class);
// Returns an instance of a `EngineMarkTwo` class.
$engine = $composite->get(EngineInterface::class);

Using service providers

A service provider is a special class that's responsible for providing complex services or groups of dependencies for the container and extensions of existing services.

A provider should extend from Yiisoft\Di\ServiceProviderInterface and must contain a getDefinitions() and getExtensions() methods. It should only provide services for the container and therefore should only contain code related to this task. It should never implement any business logic or other functionality such as environment bootstrap or applying changes to a database.

The getExtensions() method allows implementing the decorator pattern by wrapping existing services with additional functionality.

A typical service provider could look like:

u

Related Skills

View on GitHub
GitHub Stars235
CategoryDevelopment
Updated1mo ago
Forks46

Languages

PHP

Security Score

100/100

Audited on Jan 27, 2026

No findings