1 | <?php |
||
2 | /** |
||
3 | * @link https://www.yiiframework.com/ |
||
4 | * @copyright Copyright (c) 2008 Yii Software LLC |
||
5 | * @license https://www.yiiframework.com/license/ |
||
6 | */ |
||
7 | |||
8 | namespace yii\di; |
||
9 | |||
10 | use Yii; |
||
11 | use yii\base\InvalidConfigException; |
||
12 | |||
13 | /** |
||
14 | * Instance represents a reference to a named object in a dependency injection (DI) container or a service locator. |
||
15 | * |
||
16 | * You may use [[get()]] to obtain the actual object referenced by [[id]]. |
||
17 | * |
||
18 | * Instance is mainly used in two places: |
||
19 | * |
||
20 | * - When configuring a dependency injection container, you use Instance to reference a class name, interface name |
||
21 | * or alias name. The reference can later be resolved into the actual object by the container. |
||
22 | * - In classes which use service locator to obtain dependent objects. |
||
23 | * |
||
24 | * The following example shows how to configure a DI container with Instance: |
||
25 | * |
||
26 | * ```php |
||
27 | * $container = new \yii\di\Container; |
||
28 | * $container->set('cache', [ |
||
29 | * 'class' => 'yii\caching\DbCache', |
||
30 | * 'db' => Instance::of('db') |
||
31 | * ]); |
||
32 | * $container->set('db', [ |
||
33 | * 'class' => 'yii\db\Connection', |
||
34 | * 'dsn' => 'sqlite:path/to/file.db', |
||
35 | * ]); |
||
36 | * ``` |
||
37 | * |
||
38 | * And the following example shows how a class retrieves a component from a service locator: |
||
39 | * |
||
40 | * ```php |
||
41 | * class DbCache extends Cache |
||
42 | * { |
||
43 | * public $db = 'db'; |
||
44 | * |
||
45 | * public function init() |
||
46 | * { |
||
47 | * parent::init(); |
||
48 | * $this->db = Instance::ensure($this->db, 'yii\db\Connection'); |
||
49 | * } |
||
50 | * } |
||
51 | * ``` |
||
52 | * |
||
53 | * @author Qiang Xue <[email protected]> |
||
54 | * @since 2.0 |
||
55 | */ |
||
56 | class Instance |
||
57 | { |
||
58 | /** |
||
59 | * @var string the component ID, class name, interface name or alias name |
||
60 | */ |
||
61 | public $id; |
||
62 | /** |
||
63 | * @var bool if null should be returned instead of throwing an exception |
||
64 | */ |
||
65 | public $optional; |
||
66 | |||
67 | |||
68 | /** |
||
69 | * Constructor. |
||
70 | * @param string $id the component ID |
||
71 | * @param bool $optional if null should be returned instead of throwing an exception |
||
72 | */ |
||
73 | 554 | protected function __construct($id, $optional = false) |
|
74 | { |
||
75 | 554 | $this->id = $id; |
|
76 | 554 | $this->optional = $optional; |
|
77 | } |
||
78 | |||
79 | /** |
||
80 | * Creates a new Instance object. |
||
81 | * @param string $id the component ID |
||
82 | * @param bool $optional if null should be returned instead of throwing an exception |
||
83 | * @return Instance the new Instance object. |
||
84 | */ |
||
85 | 14 | public static function of($id, $optional = false) |
|
86 | { |
||
87 | 14 | return new static($id, $optional); |
|
88 | } |
||
89 | |||
90 | /** |
||
91 | * Resolves the specified reference into the actual object and makes sure it is of the specified type. |
||
92 | * |
||
93 | * The reference may be specified as a string or an Instance object. If the former, |
||
94 | * it will be treated as a component ID, a class/interface name or an alias, depending on the container type. |
||
95 | * |
||
96 | * If you do not specify a container, the method will first try `Yii::$app` followed by `Yii::$container`. |
||
97 | * |
||
98 | * For example, |
||
99 | * |
||
100 | * ```php |
||
101 | * use yii\db\Connection; |
||
102 | * |
||
103 | * // returns Yii::$app->db |
||
104 | * $db = Instance::ensure('db', Connection::class); |
||
105 | * // returns an instance of Connection using the given configuration |
||
106 | * $db = Instance::ensure(['dsn' => 'sqlite:path/to/my.db'], Connection::class); |
||
107 | * ``` |
||
108 | * |
||
109 | * @param object|string|array|static $reference an object or a reference to the desired object. |
||
110 | * You may specify a reference in terms of a component ID or an Instance object. |
||
111 | * Starting from version 2.0.2, you may also pass in a configuration array for creating the object. |
||
112 | * If the "class" value is not specified in the configuration array, it will use the value of `$type`. |
||
113 | * @param string|null $type the class/interface name to be checked. If null, type check will not be performed. |
||
114 | * @param ServiceLocator|Container|null $container the container. This will be passed to [[get()]]. |
||
115 | * @return object the object referenced by the Instance, or `$reference` itself if it is an object. |
||
116 | * @throws InvalidConfigException if the reference is invalid |
||
117 | */ |
||
118 | 911 | public static function ensure($reference, $type = null, $container = null) |
|
119 | { |
||
120 | 911 | if (is_array($reference)) { |
|
121 | 5 | $class = isset($reference['class']) ? $reference['class'] : $type; |
|
122 | 5 | if (!$container instanceof Container) { |
|
123 | 3 | $container = Yii::$container; |
|
124 | } |
||
125 | 5 | unset($reference['class']); |
|
126 | 5 | $component = $container->get($class, [], $reference); |
|
127 | 5 | if ($type === null || $component instanceof $type) { |
|
128 | 4 | return $component; |
|
129 | } |
||
130 | |||
131 | 1 | throw new InvalidConfigException('Invalid data type: ' . $class . '. ' . $type . ' is expected.'); |
|
132 | 910 | } elseif (empty($reference)) { |
|
133 | 1 | throw new InvalidConfigException('The required component is not specified.'); |
|
134 | } |
||
135 | |||
136 | 909 | if (is_string($reference)) { |
|
137 | 540 | $reference = new static($reference); |
|
138 | 477 | } elseif ($type === null || $reference instanceof $type) { |
|
139 | 475 | return $reference; |
|
140 | } |
||
141 | |||
142 | 542 | if ($reference instanceof self) { |
|
143 | try { |
||
144 | 541 | $component = $reference->get($container); |
|
145 | 3 | } catch (\ReflectionException $e) { |
|
146 | throw new InvalidConfigException('Failed to instantiate component or class "' . $reference->id . '".', 0, $e); |
||
147 | } |
||
148 | 538 | if ($type === null || $component instanceof $type) { |
|
149 | 537 | return $component; |
|
150 | } |
||
151 | |||
152 | 1 | throw new InvalidConfigException('"' . $reference->id . '" refers to a ' . get_class($component) . " component. $type is expected."); |
|
153 | } |
||
154 | |||
155 | 1 | $valueType = is_object($reference) ? get_class($reference) : gettype($reference); |
|
156 | 1 | throw new InvalidConfigException("Invalid data type: $valueType. $type is expected."); |
|
157 | } |
||
158 | |||
159 | /** |
||
160 | * Returns the actual object referenced by this Instance object. |
||
161 | * @param ServiceLocator|Container|null $container the container used to locate the referenced object. |
||
162 | * If null, the method will first try `Yii::$app` then `Yii::$container`. |
||
163 | * @return object the actual object referenced by this Instance object. |
||
164 | */ |
||
165 | 551 | public function get($container = null) |
|
166 | { |
||
167 | try { |
||
168 | 551 | if ($container) { |
|
169 | 15 | return $container->get($this->id); |
|
170 | } |
||
171 | 536 | if (Yii::$app && Yii::$app->has($this->id)) { |
|
172 | 534 | return Yii::$app->get($this->id); |
|
0 ignored issues
–
show
Bug
Best Practice
introduced
by
![]() |
|||
173 | } |
||
174 | |||
175 | 4 | return Yii::$container->get($this->id); |
|
176 | 7 | } catch (\Exception $e) { |
|
177 | 7 | if ($this->optional) { |
|
178 | 2 | return null; |
|
179 | } |
||
180 | 5 | throw $e; |
|
181 | } catch (\Throwable $e) { |
||
182 | if ($this->optional) { |
||
183 | return null; |
||
184 | } |
||
185 | throw $e; |
||
186 | } |
||
187 | } |
||
188 | |||
189 | /** |
||
190 | * Restores class state after using `var_export()`. |
||
191 | * |
||
192 | * @param array $state |
||
193 | * @return Instance |
||
194 | * @throws InvalidConfigException when $state property does not contain `id` parameter |
||
195 | * @see https://www.php.net/manual/en/function.var-export.php |
||
196 | * @since 2.0.12 |
||
197 | */ |
||
198 | 2 | public static function __set_state($state) |
|
199 | { |
||
200 | 2 | if (!isset($state['id'])) { |
|
201 | 1 | throw new InvalidConfigException('Failed to instantiate class "Instance". Required parameter "id" is missing'); |
|
202 | } |
||
203 | |||
204 | 1 | return new self($state['id']); |
|
205 | } |
||
206 | } |
||
207 |