Commit 72c99663 by Qiang Xue

Refactored container.

parent 43fb937a
...@@ -86,7 +86,7 @@ class RequestPanel extends Panel ...@@ -86,7 +86,7 @@ class RequestPanel extends Panel
$action = null; $action = null;
} }
/** @var \yii\web\Session $session */ /** @var \yii\web\Session $session */
$session = Yii::$app->get('session', [], false); $session = Yii::$app->has('session', true) ? Yii::$app->get('session') : null;
return [ return [
'flashes' => $session ? $session->getAllFlashes() : [], 'flashes' => $session ? $session->getAllFlashes() : [],
......
...@@ -26,7 +26,7 @@ class CacheController extends Controller ...@@ -26,7 +26,7 @@ class CacheController extends Controller
public function actionIndex() public function actionIndex()
{ {
$caches = []; $caches = [];
$components = Yii::$app->getComponentDefinitions(); $components = Yii::$app->getComponents();
foreach ($components as $name => $component) { foreach ($components as $name => $component) {
if ($component instanceof Cache) { if ($component instanceof Cache) {
$caches[$name] = get_class($component); $caches[$name] = get_class($component);
......
...@@ -16,16 +16,11 @@ namespace yii\di; ...@@ -16,16 +16,11 @@ namespace yii\di;
interface ContainerInterface interface ContainerInterface
{ {
/** /**
* Returns the list of the loaded shared component instances. * Returns the list of the component definitions or the loaded shared component instances.
* @return array the list of the loaded shared component instances (type or ID => component). * @param boolean $returnDefinitions whether to return component definitions or the loaded shared component instances.
* @return array the list of the component definitions or the loaded shared component instances (type or ID => definition or instance).
*/ */
public function getComponents(); public function getComponents($returnDefinitions = true);
/**
* Returns the component definitions registered with this container.
* @return array the component definitions registered with this container (type or ID => definition).
*/
public function getComponentDefinitions();
/** /**
* Registers a set of component definitions in this container. * Registers a set of component definitions in this container.
...@@ -43,12 +38,20 @@ interface ContainerInterface ...@@ -43,12 +38,20 @@ interface ContainerInterface
public function setComponents($components); public function setComponents($components);
/** /**
* Returns a value indicating whether the container has the component definition of the specified type or ID. * Returns a value indicating whether the container has the specified component definition or has instantiated the shared component.
* This method may return different results depending on the value of `$checkInstance`.
*
* - If `$checkInstance` is false (default), the method will return a value indicating whether the container has the specified
* component definition.
* - If `$checkInstance` is true, the method will return a value indicating whether the container has
* instantiated the specified shared component.
*
* @param string $typeOrID component type (a fully qualified namespaced class/interface name, e.g. `yii\db\Connection`) or ID (e.g. `db`). * @param string $typeOrID component type (a fully qualified namespaced class/interface name, e.g. `yii\db\Connection`) or ID (e.g. `db`).
* @param boolean $checkInstance whether the method should check if the component is shared and instantiated.
* @return boolean whether the container has the component definition of the specified type or ID * @return boolean whether the container has the component definition of the specified type or ID
* @see set() * @see set()
*/ */
public function has($typeOrID); public function has($typeOrID, $checkInstance = false);
/** /**
* Returns an instance of a component with the specified type or ID. * Returns an instance of a component with the specified type or ID.
...@@ -58,18 +61,13 @@ interface ContainerInterface ...@@ -58,18 +61,13 @@ interface ContainerInterface
* If a component is not shared, this method will create a new instance every time. * If a component is not shared, this method will create a new instance every time.
* *
* @param string $typeOrID component type (a fully qualified namespaced class/interface name, e.g. `yii\db\Connection`) or ID (e.g. `db`). * @param string $typeOrID component type (a fully qualified namespaced class/interface name, e.g. `yii\db\Connection`) or ID (e.g. `db`).
* @param array $params the parameters to be passed to the object constructor * @return object the component of the specified type or ID
* if the method needs to create a new object instance.
* @param boolean $create whether to create an instance of a component if it is not previously created.
* This is mainly useful for shared instance.
* @return object|null the component of the specified type or ID, null if the component `$create` is false
* and the component was not instantiated before.
* @throws \yii\base\InvalidConfigException if `$typeOrID` refers to a nonexistent component ID * @throws \yii\base\InvalidConfigException if `$typeOrID` refers to a nonexistent component ID
* or if there is cyclic dependency detected * or if there is cyclic dependency detected
* @see has() * @see has()
* @see set() * @see set()
*/ */
public function get($typeOrID, $params = [], $create = true); public function get($typeOrID);
/** /**
* Registers a component definition with this container. * Registers a component definition with this container.
......
...@@ -33,14 +33,23 @@ trait ContainerTrait ...@@ -33,14 +33,23 @@ trait ContainerTrait
/** /**
* Returns a value indicating whether the container has the component definition of the specified type or ID. * Returns a value indicating whether the container has the specified component definition or has instantiated the shared component.
* This method may return different results depending on the value of `$checkInstance`.
*
* - If `$checkInstance` is false (default), the method will return a value indicating whether the container has the specified
* component definition.
* - If `$checkInstance` is true, the method will return a value indicating whether the container has
* instantiated the specified shared component.
*
* @param string $typeOrID component type (a fully qualified namespaced class/interface name, e.g. `yii\db\Connection`) or ID (e.g. `db`). * @param string $typeOrID component type (a fully qualified namespaced class/interface name, e.g. `yii\db\Connection`) or ID (e.g. `db`).
* When a class/interface name is given, make sure it does NOT have a leading backslash.
* @param boolean $checkInstance whether the method should check if the component is shared and instantiated.
* @return boolean whether the container has the component definition of the specified type or ID * @return boolean whether the container has the component definition of the specified type or ID
* @see set() * @see set()
*/ */
public function has($typeOrID) public function has($typeOrID, $checkInstance = false)
{ {
return isset($this->_definitions[$typeOrID]); return $checkInstance ? isset($this->_components[$typeOrID]) : isset($this->_definitions[$typeOrID]);
} }
private $_building = []; private $_building = [];
...@@ -52,30 +61,19 @@ trait ContainerTrait ...@@ -52,30 +61,19 @@ trait ContainerTrait
* the same component instance each time it is called. * the same component instance each time it is called.
* If a component is not shared, this method will create a new instance every time. * If a component is not shared, this method will create a new instance every time.
* *
* @param string $typeOrID component type (a fully qualified namespaced class/interface name, e.g. `yii\db\Connection`) or ID (e.g. `db`). * @param string $typeOrID component type (a fully qualified namespaced class/interface name, e.g. `yii\db\Connection`)
* @param array $params the parameters to be passed to the object constructor * or ID (e.g. `db`). When a class/interface name is given, make sure it does NOT have a leading backslash.
* if the method needs to create a new object instance. * @return object the component of the specified type or ID
* @param boolean $create whether to create an instance of a component if it is not previously created.
* This is mainly useful for shared instance.
* @return object|null the component of the specified type or ID, null if the component `$create` is false
* and the component was not instantiated before.
* @throws InvalidConfigException if `$typeOrID` refers to a nonexistent component ID * @throws InvalidConfigException if `$typeOrID` refers to a nonexistent component ID
* or if there is cyclic dependency detected * or if there is cyclic dependency detected
* @see has() * @see has()
* @see set() * @see set()
*/ */
public function get($typeOrID, $params = [], $create = true) public function get($typeOrID)
{ {
// try shared component
if (isset($this->_components[$typeOrID])) { if (isset($this->_components[$typeOrID])) {
return $this->_components[$typeOrID]; return $this->_components[$typeOrID];
} }
$typeOrID = ltrim($typeOrID, '\\');
if (isset($this->_components[$typeOrID])) {
return $this->_components[$typeOrID];
} elseif (!$create) {
return null;
}
if (isset($this->_building[$typeOrID])) { if (isset($this->_building[$typeOrID])) {
throw new InvalidConfigException("A cyclic dependency of \"$typeOrID\" is detected."); throw new InvalidConfigException("A cyclic dependency of \"$typeOrID\" is detected.");
...@@ -86,20 +84,20 @@ trait ContainerTrait ...@@ -86,20 +84,20 @@ trait ContainerTrait
$definition = $this->_definitions[$typeOrID]; $definition = $this->_definitions[$typeOrID];
if (is_string($definition)) { if (is_string($definition)) {
// a type or ID // a type or ID
$component = $this->get($definition, $params); $component = $this->get($definition);
} elseif ($definition instanceof Closure || is_array($definition) && isset($definition[0], $definition[1])) { } elseif ($definition instanceof Closure || is_array($definition) && isset($definition[0], $definition[1])) {
// a PHP callable // a PHP callable
$component = call_user_func($definition, $typeOrID, $params, $this); $component = call_user_func($definition, $typeOrID, $this);
} elseif (is_object($definition)) { } elseif (is_object($definition)) {
// an object // an object
$component = $definition; $component = $definition;
} else { } else {
// a configuration array // a configuration array
$component = $this->buildComponent($definition, $params); $component = $this->buildComponent($definition);
} }
} elseif (strpos($typeOrID, '\\') !== false) { } elseif (strpos($typeOrID, '\\') !== false) {
// a class name // a class name
$component = $this->buildComponent($typeOrID, $params); $component = $this->buildComponent($typeOrID);
} else { } else {
throw new InvalidConfigException("Unknown component ID: $typeOrID"); throw new InvalidConfigException("Unknown component ID: $typeOrID");
} }
...@@ -152,12 +150,15 @@ trait ContainerTrait ...@@ -152,12 +150,15 @@ trait ContainerTrait
* - an ID: e.g. `db`. This declares a shared component with an ID. The class name should * - an ID: e.g. `db`. This declares a shared component with an ID. The class name should
* be declared in `$definition`. When [[get()]] is called, the same component instance will be returned. * be declared in `$definition`. When [[get()]] is called, the same component instance will be returned.
* *
* Note that when a class/interface name is given, make sure it does NOT have a leading backslash.
*
* @param mixed $definition the component definition to be registered with this container. * @param mixed $definition the component definition to be registered with this container.
* It can be one of the followings: * It can be one of the followings:
* *
* - a PHP callable: either an anonymous function or an array representing a class method (e.g. `['Foo', 'bar']`). * - a PHP callable: either an anonymous function or an array representing a class method (e.g. `['Foo', 'bar']`).
* The callable will be called by [[get()]] to return an object associated with the specified component type. * The callable will be called by [[get()]] to return an object associated with the specified component type.
* The signature of the function should be: `function ($container)`, where `$container` is this container. * The signature of the function should be: `function ($type, $container)`, where
* `$type` is the type or ID of the component to be created, and `$container` is this container.
* - an object: When [[get()]] is called, this object will be returned. No new object will be created. * - an object: When [[get()]] is called, this object will be returned. No new object will be created.
* This essentially makes the component a shared one, regardless how it is specified in `$typeOrID`. * This essentially makes the component a shared one, regardless how it is specified in `$typeOrID`.
* - a configuration array: the array contains name-value pairs that will be used to initialize the property * - a configuration array: the array contains name-value pairs that will be used to initialize the property
...@@ -173,7 +174,6 @@ trait ContainerTrait ...@@ -173,7 +174,6 @@ trait ContainerTrait
if ($notShared = $typeOrID[0] === '*') { if ($notShared = $typeOrID[0] === '*') {
$typeOrID = substr($typeOrID, 1); $typeOrID = substr($typeOrID, 1);
} }
$typeOrID = ltrim($typeOrID, '\\');
if ($definition === null) { if ($definition === null) {
unset($this->_components[$typeOrID], $this->_definitions[$typeOrID]); unset($this->_components[$typeOrID], $this->_definitions[$typeOrID]);
...@@ -206,21 +206,13 @@ trait ContainerTrait ...@@ -206,21 +206,13 @@ trait ContainerTrait
} }
/** /**
* Returns the list of the loaded shared component instances. * Returns the list of the component definitions or the loaded shared component instances.
* @return array the list of the loaded shared component instances (type or ID => component). * @param boolean $returnDefinitions whether to return component definitions or the loaded shared component instances.
* @return array the list of the component definitions or the loaded shared component instances (type or ID => definition or instance).
*/ */
public function getComponents() public function getComponents($returnDefinitions = true)
{ {
return $this->_components; return $returnDefinitions ? $this->_definitions : $this->_components;
}
/**
* Returns the component definitions registered with this container.
* @return array the component definitions registered with this container (type or ID => definition).
*/
public function getComponentDefinitions()
{
return $this->_definitions;
} }
/** /**
...@@ -262,17 +254,10 @@ trait ContainerTrait ...@@ -262,17 +254,10 @@ trait ContainerTrait
* Builds a new component instance based on the given class name or configuration array. * Builds a new component instance based on the given class name or configuration array.
* This method is mainly called by [[get()]]. * This method is mainly called by [[get()]].
* @param string|array $type a class name or configuration array * @param string|array $type a class name or configuration array
* @param array $params the constructor parameters
* @return object the new component instance * @return object the new component instance
*/ */
protected function buildComponent($type, $params) protected function buildComponent($type)
{ {
// a class name or configuration
if (empty($params)) {
return Yii::createObject($type); return Yii::createObject($type);
} else {
array_unshift($params, $type);
return call_user_func_array(['Yii', 'createObject'], $params);
}
} }
} }
...@@ -247,10 +247,10 @@ abstract class Target extends Component ...@@ -247,10 +247,10 @@ abstract class Target extends Component
$request = Yii::$app->getRequest(); $request = Yii::$app->getRequest();
$ip = $request instanceof Request ? $request->getUserIP() : '-'; $ip = $request instanceof Request ? $request->getUserIP() : '-';
/** @var \yii\web\User $user */ /** @var \yii\web\User $user */
$user = Yii::$app->get('user', [], false); $user = Yii::$app->has('user', true) ? Yii::$app->get('user') : null;
$userID = $user ? $user->getId(false) : '-'; $userID = $user ? $user->getId(false) : '-';
/** @var \yii\web\Session $session */ /** @var \yii\web\Session $session */
$session = Yii::$app->get('session', [], false); $session = Yii::$app->has('session', true) ? Yii::$app->get('session') : null;
$sessionID = $session && $session->getIsActive() ? $session->getId() : '-'; $sessionID = $session && $session->getIsActive() ? $session->getId() : '-';
return "[$ip] [$userID] [$sessionID]"; return "[$ip] [$userID] [$sessionID]";
} }
......
...@@ -13,7 +13,7 @@ use yiiunit\TestCase; ...@@ -13,7 +13,7 @@ use yiiunit\TestCase;
class Creator class Creator
{ {
public static function create($type, $params, $container) public static function create($type, $container)
{ {
return new $type; return new $type;
} }
...@@ -25,17 +25,6 @@ class TestClass extends Object ...@@ -25,17 +25,6 @@ class TestClass extends Object
public $prop2; public $prop2;
} }
class TestClass2 extends Object
{
public $prop1 = 1;
public $prop2;
public function __construct($a, $config = [])
{
$this->prop1 = $a;
parent::__construct($config);
}
}
/** /**
* @author Qiang Xue <qiang.xue@gmail.com> * @author Qiang Xue <qiang.xue@gmail.com>
...@@ -119,10 +108,6 @@ class ContainerTest extends TestCase ...@@ -119,10 +108,6 @@ class ContainerTest extends TestCase
$object2 = $container->get($className); $object2 = $container->get($className);
$this->assertTrue($object2 instanceof $className); $this->assertTrue($object2 instanceof $className);
$this->assertTrue($object === $object2); $this->assertTrue($object === $object2);
// check leading slash is handled properly
$object3 = $container->get('\\' . $className);
$this->assertTrue($object3 instanceof $className);
$this->assertTrue($object === $object3);
} }
public function testNonShared() public function testNonShared()
...@@ -142,10 +127,6 @@ class ContainerTest extends TestCase ...@@ -142,10 +127,6 @@ class ContainerTest extends TestCase
$object2 = $container->get($className); $object2 = $container->get($className);
$this->assertTrue($object2 instanceof $className); $this->assertTrue($object2 instanceof $className);
$this->assertTrue($object !== $object2); $this->assertTrue($object !== $object2);
// check leading slash is handled properly
$object3 = $container->get('\\' . $className);
$this->assertTrue($object3 instanceof $className);
$this->assertTrue($object !== $object3);
// shared as non-shared // shared as non-shared
$object = new TestClass; $object = new TestClass;
...@@ -167,13 +148,4 @@ class ContainerTest extends TestCase ...@@ -167,13 +148,4 @@ class ContainerTest extends TestCase
$this->assertTrue($object instanceof TestClass); $this->assertTrue($object instanceof TestClass);
$this->assertEquals(100, $object->prop1); $this->assertEquals(100, $object->prop1);
} }
public function testConstructorParams()
{
// with configuration: shared
$container = new Container;
$className = TestClass2::className();
$object = $container->get($className, [100]);
$this->assertEquals(100, $object->prop1);
}
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment