mirror of
https://github.com/yiisoft/yii2.git
synced 2025-11-09 01:27:20 +08:00
Merge branch 'module-di-config'
This commit is contained in:
@ -135,6 +135,28 @@ an [entry script](structure-entry-scripts.md), where the class name is already g
|
|||||||
More details about configuring the `components` property of an application can be found
|
More details about configuring the `components` property of an application can be found
|
||||||
in the [Applications](structure-applications.md) section and the [Service Locator](concept-service-locator.md) section.
|
in the [Applications](structure-applications.md) section and the [Service Locator](concept-service-locator.md) section.
|
||||||
|
|
||||||
|
Since version 2.0.11, the application configuration supports [Dependency Injection Container](concept-di-container.md)
|
||||||
|
configuration using `container` property. For example:
|
||||||
|
|
||||||
|
```php
|
||||||
|
$config = [
|
||||||
|
'id' => 'basic',
|
||||||
|
'basePath' => dirname(__DIR__),
|
||||||
|
'extensions' => require(__DIR__ . '/../vendor/yiisoft/extensions.php'),
|
||||||
|
'container' => [
|
||||||
|
'definitions' => [
|
||||||
|
'yii\widgets\LinkPager' => ['maxButtonCount' => 5]
|
||||||
|
],
|
||||||
|
'singletons' => [
|
||||||
|
// Dependency Injection Container singletons configuration
|
||||||
|
]
|
||||||
|
]
|
||||||
|
];
|
||||||
|
```
|
||||||
|
|
||||||
|
To know more about the possible values of `definitions` and `singletons` configuration arrays and real-life examples,
|
||||||
|
please read [Advanced Practical Usage](concept-di-container.md#advanced-practical-usage) subsection of the
|
||||||
|
[Dependency Injection Container](concept-di-container.md) article.
|
||||||
|
|
||||||
### Widget Configurations <span id="widget-configurations"></span>
|
### Widget Configurations <span id="widget-configurations"></span>
|
||||||
|
|
||||||
|
|||||||
@ -224,11 +224,13 @@ and the container will automatically resolve dependencies by instantiating them
|
|||||||
them into the newly created objects. The dependency resolution is recursive, meaning that
|
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.
|
if a dependency has other dependencies, those dependencies will also be resolved automatically.
|
||||||
|
|
||||||
You can use [[yii\di\Container::get()]] to create new objects. The method takes a dependency name,
|
You can use [[yii\di\Container::get()|get()]] to either create or get object instance.
|
||||||
which can be a class name, an interface name or an alias name. The dependency name may or may
|
The method takes a dependency name, which can be a class name, an interface name or an alias name.
|
||||||
not be registered via `set()` or `setSingleton()`. You may optionally provide a list of class
|
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.
|
constructor parameters and a [configuration](concept-configurations.md) to configure the newly created object.
|
||||||
For example,
|
|
||||||
|
For example:
|
||||||
|
|
||||||
```php
|
```php
|
||||||
// "db" is a previously registered alias name
|
// "db" is a previously registered alias name
|
||||||
@ -312,10 +314,10 @@ Yii creates a DI container when you include the `Yii.php` file in the [entry scr
|
|||||||
of your application. The DI container is accessible via [[Yii::$container]]. When you call [[Yii::createObject()]],
|
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.
|
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
|
As aforementioned, the DI container will automatically resolve the dependencies (if any) and inject them
|
||||||
into the newly created object. Because Yii uses [[Yii::createObject()]] in most of its core code to create
|
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]].
|
new objects, this means you can customize the objects globally by dealing with [[Yii::$container]].
|
||||||
|
|
||||||
For example, you can customize globally the default number of pagination buttons of [[yii\widgets\LinkPager]]:
|
For example, let's customize globally the default number of pagination buttons of [[yii\widgets\LinkPager]].
|
||||||
|
|
||||||
```php
|
```php
|
||||||
\Yii::$container->set('yii\widgets\LinkPager', ['maxButtonCount' => 5]);
|
\Yii::$container->set('yii\widgets\LinkPager', ['maxButtonCount' => 5]);
|
||||||
@ -368,6 +370,138 @@ cannot be instantiated. This is because you need to tell the DI container how to
|
|||||||
Now if you access the controller again, an instance of `app\components\BookingService` will be
|
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.
|
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 the 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-service-locator.md#application-configurations) subsection of
|
||||||
|
the [Configurations](concept-configurations.md) guide article.
|
||||||
|
|
||||||
|
Everything works, but in case we need to create 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 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 dependency, registered with `tempFileStorage` name 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>
|
When to Register Dependencies <span id="when-to-register-dependencies"></span>
|
||||||
-----------------------------
|
-----------------------------
|
||||||
@ -375,8 +509,9 @@ 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
|
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:
|
as early as possible. The following are the recommended practices:
|
||||||
|
|
||||||
* If you are the developer of an application, you can register dependencies in your
|
* If you are the developer of an application, you can register your dependencies using application configuration.
|
||||||
application's [entry script](structure-entry-scripts.md) or in a script that is included by the entry script.
|
Please, read the [Application Configurations](concept-service-locator.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
|
* If you are the developer of a redistributable [extension](structure-extensions.md), you can register dependencies
|
||||||
in the bootstrapping class of the extension.
|
in the bootstrapping class of the extension.
|
||||||
|
|
||||||
|
|||||||
@ -57,6 +57,7 @@ Yii Framework 2 Change Log
|
|||||||
- Enh #12988: Changed `textarea` method within the `yii\helpers\BaseHtml` class to allow users to control whether html entities found within `$value` will be double-encoded or not (cyphix333)
|
- Enh #12988: Changed `textarea` method within the `yii\helpers\BaseHtml` class to allow users to control whether html entities found within `$value` will be double-encoded or not (cyphix333)
|
||||||
- Enh #13074: Improved `yii\log\SyslogTarget` with `$options` to be able to change the default `openlog` options. (timbeks)
|
- Enh #13074: Improved `yii\log\SyslogTarget` with `$options` to be able to change the default `openlog` options. (timbeks)
|
||||||
- Enh #13050: Added `yii\filters\HostControl` allowing protection against 'host header' attacks (klimov-paul)
|
- Enh #13050: Added `yii\filters\HostControl` allowing protection against 'host header' attacks (klimov-paul)
|
||||||
|
- Enh #11758: Implemented Dependency Injection Container configuration using Application configuration array (silverfire)
|
||||||
- Enh: Added constants for specifying `yii\validators\CompareValidator::$type` (cebe)
|
- Enh: Added constants for specifying `yii\validators\CompareValidator::$type` (cebe)
|
||||||
- Enh #12854: Added `RangeNotSatisfiableHttpException` to cover HTTP error 416 file request exceptions (zalatov)
|
- Enh #12854: Added `RangeNotSatisfiableHttpException` to cover HTTP error 416 file request exceptions (zalatov)
|
||||||
|
|
||||||
|
|||||||
@ -246,6 +246,12 @@ abstract class Application extends Module
|
|||||||
$this->setTimeZone('UTC');
|
$this->setTimeZone('UTC');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isset($config['container'])) {
|
||||||
|
$this->setContainer($config['container']);
|
||||||
|
|
||||||
|
unset($config['container']);
|
||||||
|
}
|
||||||
|
|
||||||
// merge core components with custom components
|
// merge core components with custom components
|
||||||
foreach ($this->coreComponents() as $id => $component) {
|
foreach ($this->coreComponents() as $id => $component) {
|
||||||
if (!isset($config['components'][$id])) {
|
if (!isset($config['components'][$id])) {
|
||||||
@ -652,4 +658,15 @@ abstract class Application extends Module
|
|||||||
exit($status);
|
exit($status);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures [[Yii::$container]] with the $config
|
||||||
|
*
|
||||||
|
* @param array $config values given in terms of name-value pairs
|
||||||
|
* @since 2.0.11
|
||||||
|
*/
|
||||||
|
public function setContainer($config)
|
||||||
|
{
|
||||||
|
Yii::configure(Yii::$container, $config);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -566,4 +566,84 @@ class Container extends Component
|
|||||||
}
|
}
|
||||||
return $args;
|
return $args;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers class definitions within this container.
|
||||||
|
*
|
||||||
|
* @param array $definitions array of definitions. There are two allowed formats of array.
|
||||||
|
* The first format:
|
||||||
|
* - key: class name, interface name or alias name. The key will be passed to the [[set()]] method
|
||||||
|
* as a first argument `$class`.
|
||||||
|
* - value: the definition associated with `$class`. Possible values are described in
|
||||||
|
* [[set()]] documentation for the `$definition` parameter. Will be passed to the [[set()]] method
|
||||||
|
* as the second argument `$definition`.
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* ```php
|
||||||
|
* $container->setDefinitions([
|
||||||
|
* 'yii\web\Request' => 'app\components\Request',
|
||||||
|
* 'yii\web\Response' => [
|
||||||
|
* 'class' => 'app\components\Response',
|
||||||
|
* 'format' => 'json'
|
||||||
|
* ],
|
||||||
|
* 'foo\Bar' => function () {
|
||||||
|
* $qux = new Qux;
|
||||||
|
* $foo = new Foo($qux);
|
||||||
|
* return new Bar($foo);
|
||||||
|
* }
|
||||||
|
* ]);
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* The second format:
|
||||||
|
* - key: class name, interface name or alias name. The key will be passed to the [[set()]] method
|
||||||
|
* as a first argument `$class`.
|
||||||
|
* - value: array of two elements. The first element will be passed the [[set()]] method as the
|
||||||
|
* second argument `$definition`, the second one — as `$params`.
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* ```php
|
||||||
|
* $container->setDefinitions([
|
||||||
|
* 'foo\Bar' => [
|
||||||
|
* ['class' => 'app\Bar'],
|
||||||
|
* [Instance::of('baz')]
|
||||||
|
* ]
|
||||||
|
* ]);
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @see set() to know more about possible values of definitions
|
||||||
|
* @since 2.0.11
|
||||||
|
*/
|
||||||
|
public function setDefinitions(array $definitions)
|
||||||
|
{
|
||||||
|
foreach ($definitions as $class => $definition) {
|
||||||
|
if (count($definition) === 2 && array_values($definition) === $definition) {
|
||||||
|
$this->set($class, $definition[0], $definition[1]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->set($class, $definition);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers class definitions as singletons within this container by calling [[setSingleton()]]
|
||||||
|
*
|
||||||
|
* @param array $singletons array of singleton definitions. See [[setDefinitions()]]
|
||||||
|
* for allowed formats of array.
|
||||||
|
*
|
||||||
|
* @see setDefinitions() for allowed formats of $singletons parameter
|
||||||
|
* @see setSingleton() to know more about possible values of definitions
|
||||||
|
* @since 2.0.11
|
||||||
|
*/
|
||||||
|
public function setSingletons(array $singletons)
|
||||||
|
{
|
||||||
|
foreach ($singletons as $class => $definition) {
|
||||||
|
if (count($definition) === 2 && array_values($definition) === $definition) {
|
||||||
|
$this->setSingleton($class, $definition[0], $definition[1]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->setSingleton($class, $definition);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
32
tests/framework/base/ApplicationTest.php
Normal file
32
tests/framework/base/ApplicationTest.php
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace yiiunit\framework\base;
|
||||||
|
|
||||||
|
use Yii;
|
||||||
|
use yii\log\Dispatcher;
|
||||||
|
use yiiunit\data\ar\Cat;
|
||||||
|
use yiiunit\data\ar\Order;
|
||||||
|
use yiiunit\data\ar\Type;
|
||||||
|
use yiiunit\TestCase;
|
||||||
|
|
||||||
|
class ApplicationTest extends TestCase
|
||||||
|
{
|
||||||
|
public function testContainerSettingsAffectBootstrap()
|
||||||
|
{
|
||||||
|
$this->mockApplication([
|
||||||
|
'container' => [
|
||||||
|
'definitions' => [
|
||||||
|
Dispatcher::className() => DispatcherMock::className()
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'bootstrap' => ['log']
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertInstanceOf(DispatcherMock::className(), Yii::$app->log);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DispatcherMock extends Dispatcher
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
@ -10,6 +10,9 @@ namespace yiiunit\framework\di;
|
|||||||
use Yii;
|
use Yii;
|
||||||
use yii\di\Container;
|
use yii\di\Container;
|
||||||
use yii\di\Instance;
|
use yii\di\Instance;
|
||||||
|
use yiiunit\data\ar\Cat;
|
||||||
|
use yiiunit\data\ar\Order;
|
||||||
|
use yiiunit\data\ar\Type;
|
||||||
use yiiunit\framework\di\stubs\Bar;
|
use yiiunit\framework\di\stubs\Bar;
|
||||||
use yiiunit\framework\di\stubs\Foo;
|
use yiiunit\framework\di\stubs\Foo;
|
||||||
use yiiunit\framework\di\stubs\Qux;
|
use yiiunit\framework\di\stubs\Qux;
|
||||||
@ -223,4 +226,58 @@ class ContainerTest extends TestCase
|
|||||||
};
|
};
|
||||||
$this->assertNull($container->invoke($closure));
|
$this->assertNull($container->invoke($closure));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testSetDependencies()
|
||||||
|
{
|
||||||
|
$container = new Container();
|
||||||
|
$container->setDefinitions([
|
||||||
|
'model.order' => Order::className(),
|
||||||
|
Cat::className() => Type::className(),
|
||||||
|
'test\TraversableInterface' => [
|
||||||
|
['class' => 'yiiunit\data\base\TraversableObject'],
|
||||||
|
[['item1', 'item2']]
|
||||||
|
],
|
||||||
|
'qux.using.closure' => function () {
|
||||||
|
return new Qux();
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
$container->setDefinitions([]);
|
||||||
|
|
||||||
|
$this->assertInstanceOf(Order::className(), $container->get('model.order'));
|
||||||
|
$this->assertInstanceOf(Type::className(), $container->get(Cat::className()));
|
||||||
|
|
||||||
|
$traversable = $container->get('test\TraversableInterface');
|
||||||
|
$this->assertInstanceOf('yiiunit\data\base\TraversableObject', $traversable);
|
||||||
|
$this->assertEquals('item1', $traversable->current());
|
||||||
|
|
||||||
|
$this->assertInstanceOf('yiiunit\framework\di\stubs\Qux', $container->get('qux.using.closure'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testContainerSingletons()
|
||||||
|
{
|
||||||
|
$container = new Container();
|
||||||
|
$container->setSingletons([
|
||||||
|
'model.order' => Order::className(),
|
||||||
|
'test\TraversableInterface' => [
|
||||||
|
['class' => 'yiiunit\data\base\TraversableObject'],
|
||||||
|
[['item1', 'item2']]
|
||||||
|
],
|
||||||
|
'qux.using.closure' => function () {
|
||||||
|
return new Qux();
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
$container->setSingletons([]);
|
||||||
|
|
||||||
|
$order = $container->get('model.order');
|
||||||
|
$sameOrder = $container->get('model.order');
|
||||||
|
$this->assertSame($order, $sameOrder);
|
||||||
|
|
||||||
|
$traversable = $container->get('test\TraversableInterface');
|
||||||
|
$sameTraversable = $container->get('test\TraversableInterface');
|
||||||
|
$this->assertSame($traversable, $sameTraversable);
|
||||||
|
|
||||||
|
$foo = $container->get('qux.using.closure');
|
||||||
|
$sameFoo = $container->get('qux.using.closure');
|
||||||
|
$this->assertSame($foo, $sameFoo);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user