|
|
|
Dependency Injection Container
|
|
|
|
==============================
|
|
|
|
|
|
|
|
A dependency injection (DI) container is an object that knows how to instantiate and configure objects and
|
|
|
|
all their dependent objects. [Martin Fowler's article](http://martinfowler.com/articles/injection.html) has well
|
|
|
|
explained why DI container is useful. Here we will mainly explain the usage of the DI container provided by Yii.
|
|
|
|
|
|
|
|
|
|
|
|
Dependency Injection <span id="dependency-injection"></span>
|
|
|
|
--------------------
|
|
|
|
|
|
|
|
Yii provides the DI container feature through the class [[yii\di\Container]]. It supports the following kinds of
|
|
|
|
dependency injection:
|
|
|
|
|
|
|
|
* Constructor injection;
|
|
|
|
* Method injection;
|
|
|
|
* Setter and property injection;
|
|
|
|
* PHP callable injection;
|
|
|
|
|
|
|
|
|
|
|
|
### Constructor Injection <span id="constructor-injection"></span>
|
|
|
|
|
|
|
|
The DI container supports constructor injection with the help of type hints for constructor parameters.
|
|
|
|
The type hints tell the container which classes or interfaces are dependent when it is used to create a new object.
|
|
|
|
The container will try to get the instances of the dependent classes or interfaces and then inject them
|
|
|
|
into the new object through the constructor. For example,
|
|
|
|
|
|
|
|
```php
|
|
|
|
class Foo
|
|
|
|
{
|
|
|
|
public function __construct(Bar $bar)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$foo = $container->get('Foo');
|
|
|
|
// which is equivalent to the following:
|
|
|
|
$bar = new Bar;
|
|
|
|
$foo = new Foo($bar);
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### Method Injection <span id="method-injection"></span>
|
|
|
|
|
|
|
|
Usually the dependencies of a class are passed to the constructor and are available inside of the class during the whole lifecycle.
|
|
|
|
With Method Injection it is possible to provide a dependency that is only needed by a single method of the class
|
|
|
|
and passing it to the constructor may not be possible or may cause too much overhead in the majority of use cases.
|
|
|
|
|
|
|
|
A class method can be defined like the `doSomething()` method in the following example:
|
|
|
|
|
|
|
|
```php
|
|
|
|
class MyClass extends \yii\base\Component
|
|
|
|
{
|
|
|
|
public function __construct(/*Some lightweight dependencies here*/, $config = [])
|
|
|
|
{
|
|
|
|
// ...
|
|
|
|
}
|
|
|
|
|
|
|
|
public function doSomething($param1, \my\heavy\Dependency $something)
|
|
|
|
{
|
|
|
|
// do something with $something
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
You may call that method either by passing an instance of `\my\heavy\Dependency` yourself or using [[yii\di\Container::invoke()]] like the following:
|
|
|
|
|
|
|
|
```php
|
|
|
|
$obj = new MyClass(/*...*/);
|
|
|
|
Yii::$container->invoke([$obj, 'doSomething'], ['param1' => 42]); // $something will be provided by the DI container
|
|
|
|
```
|
|
|
|
|
|
|
|
### Setter and Property Injection <span id="setter-and-property-injection"></span>
|
|
|
|
|
|
|
|
Setter and property injection is supported through [configurations](concept-configurations.md).
|
|
|
|
When registering a dependency or when creating a new object, you can provide a configuration which
|
|
|
|
will be used by the container to inject the dependencies through the corresponding setters or properties.
|
|
|
|
For example,
|
|
|
|
|
|
|
|
```php
|
|
|
|
use yii\base\Object;
|
|
|
|
|
|
|
|
class Foo extends Object
|
|
|
|
{
|
|
|
|
public $bar;
|
|
|
|
|
|
|
|
private $_qux;
|
|
|
|
|
|
|
|
public function getQux()
|
|
|
|
{
|
|
|
|
return $this->_qux;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function setQux(Qux $qux)
|
|
|
|
{
|
|
|
|
$this->_qux = $qux;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$container->get('Foo', [], [
|
|
|
|
'bar' => $container->get('Bar'),
|
|
|
|
'qux' => $container->get('Qux'),
|
|
|
|
]);
|
|
|
|
```
|
|
|
|
|
|
|
|
> Info: The [[yii\di\Container::get()]] method takes its third parameter as a configuration array that should
|
|
|
|
be applied to the object being created. If the class implements the [[yii\base\Configurable]] interface (e.g.
|
|
|
|
[[yii\base\Object]]), the configuration array will be passed as the last parameter to the class constructor;
|
|
|
|
otherwise, the configuration will be applied *after* the object is created.
|
|
|
|
|
|
|
|
|
|
|
|
### PHP Callable Injection <span id="php-callable-injection"></span>
|
|
|
|
|
|
|
|
In this case, the container will use a registered PHP callable to build new instances of a class.
|
|
|
|
Each time when [[yii\di\Container::get()]] is called, the corresponding callable will be invoked.
|
|
|
|
The callable is responsible to resolve the dependencies and inject them appropriately to the newly
|
|
|
|
created objects. For example,
|
|
|
|
|
|
|
|
```php
|
|
|
|
$container->set('Foo', function () {
|
|
|
|
$foo = new Foo(new Bar);
|
|
|
|
// ... other initializations ...
|
|
|
|
return $foo;
|
|
|
|
});
|
|
|
|
|
|
|
|
$foo = $container->get('Foo');
|
|
|
|
```
|
|
|
|
|
|
|
|
To hide the complex logic for building a new object, you may use a static class method as callable. For example,
|
|
|
|
|
|
|
|
```php
|
|
|
|
class FooBuilder
|
|
|
|
{
|
|
|
|
public static function build()
|
|
|
|
{
|
|
|
|
$foo = new Foo(new Bar);
|
|
|
|
// ... other initializations ...
|
|
|
|
return $foo;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$container->set('Foo', ['app\helper\FooBuilder', 'build']);
|
|
|
|
|
|
|
|
$foo = $container->get('Foo');
|
|
|
|
```
|
|
|
|
|
|
|
|
By doing so, the person who wants to configure the `Foo` class no longer needs to be aware of how it is built.
|
|
|
|
|
|
|
|
|
|
|
|
Registering Dependencies <span id="registering-dependencies"></span>
|
|
|
|
------------------------
|
|
|
|
|
|
|
|
You can use [[yii\di\Container::set()]] to register dependencies. The registration requires a dependency name
|
|
|
|
as well as a dependency definition. A dependency name can be a class name, an interface name, or an alias name;
|
|
|
|
and a dependency definition can be a class name, a configuration array, or a PHP callable.
|
|
|
|
|
|
|
|
```php
|
|
|
|
$container = new \yii\di\Container;
|
|
|
|
|
|
|
|
// register a class name as is. This can be skipped.
|
|
|
|
$container->set('yii\db\Connection');
|
|
|
|
|
|
|
|
// register an interface
|
|
|
|
// When a class depends on the interface, the corresponding class
|
|
|
|
// will be instantiated as the dependent object
|
|
|
|
$container->set('yii\mail\MailInterface', 'yii\swiftmailer\Mailer');
|
|
|
|
|
|
|
|
// register an alias name. You can use $container->get('foo')
|
|
|
|
// to create an instance of Connection
|
|
|
|
$container->set('foo', 'yii\db\Connection');
|
|
|
|
|
|
|
|
// register a class with configuration. The configuration
|
|
|
|
// will be applied when the class is instantiated by get()
|
|
|
|
$container->set('yii\db\Connection', [
|
|
|
|
'dsn' => 'mysql:host=127.0.0.1;dbname=demo',
|
|
|
|
'username' => 'root',
|
|
|
|
'password' => '',
|
|
|
|
'charset' => 'utf8',
|
|
|
|
]);
|
|
|
|
|
|
|
|
// register an alias name with class configuration
|
|
|
|
// In this case, a "class" element is required to specify the class
|
|
|
|
$container->set('db', [
|
|
|
|
'class' => 'yii\db\Connection',
|
|
|
|
'dsn' => 'mysql:host=127.0.0.1;dbname=demo',
|
|
|
|
'username' => 'root',
|
|
|
|
'password' => '',
|
|
|
|
'charset' => 'utf8',
|
|
|
|
]);
|
|
|
|
|
|
|
|
// register a PHP callable
|
|
|
|
// The callable will be executed each time when $container->get('db') is called
|
|
|
|
$container->set('db', function ($container, $params, $config) {
|
|
|
|
return new \yii\db\Connection($config);
|
|
|
|
});
|
|
|
|
|
|
|
|
// register a component instance
|
|
|
|
// $container->get('pageCache') will return the same instance each time it is called
|
|
|
|
$container->set('pageCache', new FileCache);
|
|
|
|
```
|
|
|
|
|
|
|
|
> Tip: If a dependency name is the same as the corresponding dependency definition, you do not
|
|
|
|
need to register it with the DI container.
|
|
|
|
|
|
|
|
A dependency registered via `set()` will generate an instance each time the dependency is needed.
|
|
|
|
You can use [[yii\di\Container::setSingleton()]] to register a dependency that only generates
|
|
|
|
a single instance:
|
|
|
|
|
|
|
|
```php
|
|
|
|
$container->setSingleton('yii\db\Connection', [
|
|
|
|
'dsn' => 'mysql:host=127.0.0.1;dbname=demo',
|
|
|
|
'username' => 'root',
|
|
|
|
'password' => '',
|
|
|
|
'charset' => 'utf8',
|
|
|
|
]);
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Resolving Dependencies <span id="resolving-dependencies"></span>
|
|
|
|
----------------------
|
|
|
|
|
|
|
|
Once you have registered dependencies, you can use the DI container to create new objects,
|
|
|
|
and the container will automatically resolve dependencies by instantiating them and injecting
|
|
|
|
them into the newly created objects. The dependency resolution is recursive, meaning that
|
|
|
|
if a dependency has other dependencies, those dependencies will also be resolved automatically.
|
|
|
|
|
|
|
|
You can use [[yii\di\Container::get()|get()]] to either create or get object instance.
|
|
|
|
The method takes a dependency name, which can be a class name, an interface name or an alias name.
|
|
|
|
The dependency name may be registered via [[yii\di\Container::set()|set()]]
|
|
|
|
or [[yii\di\Container::setSingleton()|setSingleton()]]. You may optionally provide a list of class
|
|
|
|
constructor parameters and a [configuration](concept-configurations.md) to configure the newly created object.
|
|
|
|
|
|
|
|
For example:
|
|
|
|
|
|
|
|
```php
|
|
|
|
// "db" is a previously registered alias name
|
|
|
|
$db = $container->get('db');
|
|
|
|
|
|
|
|
// equivalent to: $engine = new \app\components\SearchEngine($apiKey, $apiSecret, ['type' => 1]);
|
|
|
|
$engine = $container->get('app\components\SearchEngine', [$apiKey, $apiSecret], ['type' => 1]);
|
|
|
|
```
|
|
|
|
|
|
|
|
Behind the scene, the DI container does much more work than just creating a new object.
|
|
|
|
The container will first inspect the class constructor to find out dependent class or interface names
|
|
|
|
and then automatically resolve those dependencies recursively.
|
|
|
|
|
|
|
|
The following code shows a more sophisticated example. The `UserLister` class depends on an object implementing
|
|
|
|
the `UserFinderInterface` interface; the `UserFinder` class implements this interface and depends on
|
|
|
|
a `Connection` object. All these dependencies are declared through type hinting of the class constructor parameters.
|
|
|
|
With property dependency registration, the DI container is able to resolve these dependencies automatically
|
|
|
|
and creates a new `UserLister` instance with a simple call of `get('userLister')`.
|
|
|
|
|
|
|
|
```php
|
|
|
|
namespace app\models;
|
|
|
|
|
|
|
|
use yii\base\Object;
|
|
|
|
use yii\db\Connection;
|
|
|
|
use yii\di\Container;
|
|
|
|
|
|
|
|
interface UserFinderInterface
|
|
|
|
{
|
|
|
|
function findUser();
|
|
|
|
}
|
|
|
|
|
|
|
|
class UserFinder extends Object implements UserFinderInterface
|
|
|
|
{
|
|
|
|
public $db;
|
|
|
|
|
|
|
|
public function __construct(Connection $db, $config = [])
|
|
|
|
{
|
|
|
|
$this->db = $db;
|
|
|
|
parent::__construct($config);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function findUser()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class UserLister extends Object
|
|
|
|
{
|
|
|
|
public $finder;
|
|
|
|
|
|
|
|
public function __construct(UserFinderInterface $finder, $config = [])
|
|
|
|
{
|
|
|
|
$this->finder = $finder;
|
|
|
|
parent::__construct($config);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$container = new Container;
|
|
|
|
$container->set('yii\db\Connection', [
|
|
|
|
'dsn' => '...',
|
|
|
|
]);
|
|
|
|
$container->set('app\models\UserFinderInterface', [
|
|
|
|
'class' => 'app\models\UserFinder',
|
|
|
|
]);
|
|
|
|
$container->set('userLister', 'app\models\UserLister');
|
|
|
|
|
|
|
|
$lister = $container->get('userLister');
|
|
|
|
|
|
|
|
// which is equivalent to:
|
|
|
|
|
|
|
|
$db = new \yii\db\Connection(['dsn' => '...']);
|
|
|
|
$finder = new UserFinder($db);
|
|
|
|
$lister = new UserLister($finder);
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Practical Usage <span id="practical-usage"></span>
|
|
|
|
---------------
|
|
|
|
|
|
|
|
Yii creates a DI container when you include the `Yii.php` file in the [entry script](structure-entry-scripts.md)
|
|
|
|
of your application. The DI container is accessible via [[Yii::$container]]. When you call [[Yii::createObject()]],
|
|
|
|
the method will actually call the container's [[yii\di\Container::get()|get()]] method to create a new object.
|
|
|
|
As aforementioned, the DI container will automatically resolve the dependencies (if any) and inject them
|
|
|
|
into obtained object. Because Yii uses [[Yii::createObject()]] in most of its core code to create
|
|
|
|
new objects, this means you can customize the objects globally by dealing with [[Yii::$container]].
|
|
|
|
|
|
|
|
For example, let's customize globally the default number of pagination buttons of [[yii\widgets\LinkPager]].
|
|
|
|
|
|
|
|
```php
|
|
|
|
\Yii::$container->set('yii\widgets\LinkPager', ['maxButtonCount' => 5]);
|
|
|
|
```
|
|
|
|
|
|
|
|
Now if you use the widget in a view with the following code, the `maxButtonCount` property will be initialized
|
|
|
|
as 5 instead of the default value 10 as defined in the class.
|
|
|
|
|
|
|
|
```php
|
|
|
|
echo \yii\widgets\LinkPager::widget();
|
|
|
|
```
|
|
|
|
|
|
|
|
You can still override the value set via DI container, though:
|
|
|
|
|
|
|
|
```php
|
|
|
|
echo \yii\widgets\LinkPager::widget(['maxButtonCount' => 20]);
|
|
|
|
```
|
|
|
|
|
|
|
|
> Note: Properties given in the widget call will always override the definition in the DI container.
|
|
|
|
> Even if you specify an array, e.g. `'options' => ['id' => 'mypager']` these will not be merged
|
|
|
|
> with other options but replace them.
|
|
|
|
|
|
|
|
Another example is to take advantage of the automatic constructor injection of the DI container.
|
|
|
|
Assume your controller class depends on some other objects, such as a hotel booking service. You
|
|
|
|
can declare the dependency through a constructor parameter and let the DI container to resolve it for you.
|
|
|
|
|
|
|
|
```php
|
|
|
|
namespace app\controllers;
|
|
|
|
|
|
|
|
use yii\web\Controller;
|
|
|
|
use app\components\BookingInterface;
|
|
|
|
|
|
|
|
class HotelController extends Controller
|
|
|
|
{
|
|
|
|
protected $bookingService;
|
|
|
|
|
|
|
|
public function __construct($id, $module, BookingInterface $bookingService, $config = [])
|
|
|
|
{
|
|
|
|
$this->bookingService = $bookingService;
|
|
|
|
parent::__construct($id, $module, $config);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
If you access this controller from browser, you will see an error complaining the `BookingInterface`
|
|
|
|
cannot be instantiated. This is because you need to tell the DI container how to deal with this dependency:
|
|
|
|
|
|
|
|
```php
|
|
|
|
\Yii::$container->set('app\components\BookingInterface', 'app\components\BookingService');
|
|
|
|
```
|
|
|
|
|
|
|
|
Now if you access the controller again, an instance of `app\components\BookingService` will be
|
|
|
|
created and injected as the 3rd parameter to the controller's constructor.
|
|
|
|
|
|
|
|
Advanced Practical Usage <span id="advanced-practical-usage"></span>
|
|
|
|
---------------
|
|
|
|
|
|
|
|
Say we work on API application and have:
|
|
|
|
|
|
|
|
- `app\components\Request` class that extends `yii\web\Request` and provides additional functionality
|
|
|
|
- `app\components\Response` class that extends `yii\web\Response` and should have `format` property
|
|
|
|
set to `json` on creation
|
|
|
|
- `app\storage\FileStorage` and `app\storage\DocumentsReader` classes that implement some logic on
|
|
|
|
working with documents that are located in some file storage:
|
|
|
|
|
|
|
|
```php
|
|
|
|
class FileStorage
|
|
|
|
{
|
|
|
|
public function __contruct($root) {
|
|
|
|
// whatever
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class DocumentsReader
|
|
|
|
{
|
|
|
|
public function __contruct(FileStorage $fs) {
|
|
|
|
// whatever
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
It is possible to configure multiple definitions at once, passing configuration array to
|
|
|
|
[[yii\di\Container::setDefinitions()|setDefinitions()]] or [[yii\di\Container::setSingletons()|setSingletons()]] method.
|
|
|
|
Iterating over the configuration array, the methods will call [[yii\di\Container::set()|set()]]
|
|
|
|
or [[yii\di\Container::setSingleton()|setSingleton()]] respectively for each item.
|
|
|
|
|
|
|
|
The configuration array format is:
|
|
|
|
|
|
|
|
- `key`: class name, interface name or alias name. The key will be passed to the
|
|
|
|
[[yii\di\Container::set()|set()]] method as a first argument `$class`.
|
|
|
|
- `value`: the definition associated with `$class`. Possible values are described in [[yii\di\Container::set()|set()]]
|
|
|
|
documentation for the `$definition` parameter. Will be passed to the [[set()]] method as
|
|
|
|
the second argument `$definition`.
|
|
|
|
|
|
|
|
For example, let's configure our container to follow the aforementioned requirements:
|
|
|
|
|
|
|
|
```php
|
|
|
|
$container->setDefinitions([
|
|
|
|
'yii\web\Request' => 'app\components\Request',
|
|
|
|
'yii\web\Response' => [
|
|
|
|
'class' => 'app\components\Response',
|
|
|
|
'format' => 'json'
|
|
|
|
],
|
|
|
|
'app\storage\DocumentsReader' => function () {
|
|
|
|
$fs = new app\storage\FileStorage('/var/tempfiles');
|
|
|
|
return new app\storage\DocumentsReader($fs);
|
|
|
|
}
|
|
|
|
]);
|
|
|
|
|
|
|
|
$reader = $container->get('app\storage\DocumentsReader);
|
|
|
|
// Will create DocumentReader object with its dependencies as described in the config
|
|
|
|
```
|
|
|
|
|
|
|
|
> Tip: Container may be configured in declarative style using application configuration since version 2.0.11.
|
|
|
|
Check out the [Application Configurations](concept-configurations.md#application-configurations) subsection of
|
|
|
|
the [Configurations](concept-configurations.md) guide article.
|
|
|
|
|
|
|
|
Everything works, but in case we need to create `DocumentWriter` class,
|
|
|
|
we shall copy-paste the line that creates `FileStorage` object, that is not the smartest way, obviously.
|
|
|
|
|
|
|
|
As described in the [Resolving Dependencies](#resolving-dependencies) subsection, [[yii\di\Container::set()|set()]]
|
|
|
|
and [[yii\di\Container::setSingleton()|setSingleton()]] can optionally take dependency's constructor parameters as
|
|
|
|
a third argument. To set the constructor parameters, you may use the following configuration array format:
|
|
|
|
|
|
|
|
- `key`: class name, interface name or alias name. The key will be passed to the
|
|
|
|
[[yii\di\Container::set()|set()]] method as a first argument `$class`.
|
|
|
|
- `value`: array of two elements. The first element will be passed to the [[yii\di\Container::set()|set()]] method as the
|
|
|
|
second argument `$definition`, the second one — as `$params`.
|
|
|
|
|
|
|
|
Let's modify our example:
|
|
|
|
|
|
|
|
```php
|
|
|
|
$container->setDefinitions([
|
|
|
|
'tempFileStorage' => [ // we've created an alias for convenience
|
|
|
|
['class' => 'app\storage\FileStorage'],
|
|
|
|
['/var/tempfiles'] // could be extracted from some config files
|
|
|
|
],
|
|
|
|
'app\storage\DocumentsReader' => [
|
|
|
|
['class' => 'app\storage\DocumentsReader'],
|
|
|
|
[Instance::of('tempFileStorage')]
|
|
|
|
],
|
|
|
|
'app\storage\DocumentsWriter' => [
|
|
|
|
['class' => 'app\storage\DocumentsWriter'],
|
|
|
|
[Instance::of('tempFileStorage')]
|
|
|
|
]
|
|
|
|
]);
|
|
|
|
|
|
|
|
$reader = $container->get('app\storage\DocumentsReader);
|
|
|
|
// Will behave exactly the same as in the previous example.
|
|
|
|
```
|
|
|
|
|
|
|
|
You might notice `Instance::of('tempFileStorage')` notation. It means, that the [[yii\di\Container|Container]]
|
|
|
|
will implicitly provide a dependency registered with the name of `tempFileStorage` and pass it as the first argument
|
|
|
|
of `app\storage\DocumentsWriter` constructor.
|
|
|
|
|
|
|
|
> Note: [[yii\di\Container::setDefinitions()|setDefinitions()]] and [[yii\di\Container::setSingletons()|setSingletons()]]
|
|
|
|
methods are available since version 2.0.11.
|
|
|
|
|
|
|
|
Another step on configuration optimization is to register some dependencies as singletons.
|
|
|
|
A dependency registered via [[yii\di\Container::set()|set()]] will be instantiated each time it is needed.
|
|
|
|
Some classes do not change the state during runtime, therefore they may be registered as singletons
|
|
|
|
in order to increase the application performance.
|
|
|
|
|
|
|
|
A good example could be `app\storage\FileStorage` class, that executes some operations on file system with a simple
|
|
|
|
API (e.g. `$fs->read()`, `$fs->write()`). These operations do not change the internal class state, so we can
|
|
|
|
create its instance once and use it multiple times.
|
|
|
|
|
|
|
|
```php
|
|
|
|
$container->setSingletons([
|
|
|
|
'tempFileStorage' => [
|
|
|
|
['class' => 'app\storage\FileStorage'],
|
|
|
|
['/var/tempfiles']
|
|
|
|
],
|
|
|
|
]);
|
|
|
|
|
|
|
|
$container->setDefinitions([
|
|
|
|
'app\storage\DocumentsReader' => [
|
|
|
|
['class' => 'app\storage\DocumentsReader'],
|
|
|
|
[Instance::of('tempFileStorage')]
|
|
|
|
],
|
|
|
|
'app\storage\DocumentsWriter' => [
|
|
|
|
['class' => 'app\storage\DocumentsWriter'],
|
|
|
|
[Instance::of('tempFileStorage')]
|
|
|
|
]
|
|
|
|
]);
|
|
|
|
|
|
|
|
$reader = $container->get('app\storage\DocumentsReader');
|
|
|
|
```
|
|
|
|
|
|
|
|
When to Register Dependencies <span id="when-to-register-dependencies"></span>
|
|
|
|
-----------------------------
|
|
|
|
|
|
|
|
Because dependencies are needed when new objects are being created, their registration should be done
|
|
|
|
as early as possible. The following are the recommended practices:
|
|
|
|
|
|
|
|
* If you are the developer of an application, you can register your dependencies using application configuration.
|
|
|
|
Please, read the [Application Configurations](concept-configurations.md#application-configurations) subsection of
|
|
|
|
the [Configurations](concept-configurations.md) guide article.
|
|
|
|
* If you are the developer of a redistributable [extension](structure-extensions.md), you can register dependencies
|
|
|
|
in the bootstrapping class of the extension.
|
|
|
|
|
|
|
|
|
|
|
|
Summary <span id="summary"></span>
|
|
|
|
-------
|
|
|
|
|
|
|
|
Both dependency injection and [service locator](concept-service-locator.md) are popular design patterns
|
|
|
|
that allow building software in a loosely-coupled and more testable fashion. We highly recommend you to read
|
|
|
|
[Martin's article](http://martinfowler.com/articles/injection.html) to get a deeper understanding of
|
|
|
|
dependency injection and service locator.
|
|
|
|
|
|
|
|
Yii implements its [service locator](concept-service-locator.md) on top of the dependency injection (DI) container.
|
|
|
|
When a service locator is trying to create a new object instance, it will forward the call to the DI container.
|
|
|
|
The latter will resolve the dependencies automatically as described above.
|
|
|
|
|