Completed
Push — 2.1 ( 0cb910...fafa40 )
by
unknown
40:37 queued 36:49
created

ServiceLocator::__isset()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 8
ccs 0
cts 4
cp 0
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 4
nc 2
nop 1
crap 6
1
<?php
2
/**
3
 * @link http://www.yiiframework.com/
4
 * @copyright Copyright (c) 2008 Yii Software LLC
5
 * @license http://www.yiiframework.com/license/
6
 */
7
8
namespace yii\di;
9
10
use Closure;
11
use Yii;
12
use yii\base\Component;
13
use yii\base\InvalidConfigException;
14
15
/**
16
 * ServiceLocator implements a [service locator](http://en.wikipedia.org/wiki/Service_locator_pattern).
17
 *
18
 * To use ServiceLocator, you first need to register component IDs with the corresponding component
19
 * definitions with the locator by calling [[set()]] or [[setComponents()]].
20
 * You can then call [[get()]] to retrieve a component with the specified ID. The locator will automatically
21
 * instantiate and configure the component according to the definition.
22
 *
23
 * For example,
24
 *
25
 * ```php
26
 * $locator = new \yii\di\ServiceLocator;
27
 * $locator->setComponents([
28
 *     'db' => [
29
 *         'class' => \yii\db\Connection::class,
30
 *         'dsn' => 'sqlite:path/to/file.db',
31
 *     ],
32
 *     'cache' => [
33
 *         'class' => \yii\caching\DbCache::class,
34
 *         'db' => 'db',
35
 *     ],
36
 * ]);
37
 *
38
 * $db = $locator->get('db');  // or $locator->db
39
 * $cache = $locator->get('cache');  // or $locator->cache
40
 * ```
41
 *
42
 * Because [[\yii\base\Module]] extends from ServiceLocator, modules and the application are all service locators.
43
 * Modules add [tree traversal](guide:concept-service-locator#tree-traversal) for service resolution.
44
 *
45
 * For more details and usage information on ServiceLocator, see the [guide article on service locators](guide:concept-service-locator).
46
 *
47
 * @property array $components The list of the component definitions or the loaded component instances (ID =>
48
 * definition or instance).
49
 *
50
 * @author Qiang Xue <[email protected]>
51
 * @since 2.0
52
 */
53
class ServiceLocator extends Component
54
{
55
    /**
56
     * @var array shared component instances indexed by their IDs
57
     */
58
    private $_components = [];
59
    /**
60
     * @var array component definitions indexed by their IDs
61
     */
62
    private $_definitions = [];
63
64
65
    /**
66
     * Getter magic method.
67
     * This method is overridden to support accessing components like reading properties.
68
     * @param string $name component or property name
69
     * @return mixed the named property value
70
     */
71 448
    public function __get($name)
72
    {
73 448
        if ($this->has($name)) {
74 142
            return $this->get($name);
75
        }
76
77 318
        return parent::__get($name);
78
    }
79
80
    /**
81
     * Checks if a property value is null.
82
     * This method overrides the parent implementation by checking if the named component is loaded.
83
     * @param string $name the property name or the event name
84
     * @return bool whether the property value is null
85
     */
86
    public function __isset($name)
87
    {
88
        if ($this->has($name)) {
89
            return true;
90
        }
91
92
        return parent::__isset($name);
93
    }
94
95
    /**
96
     * Returns a value indicating whether the locator has the specified component definition or has instantiated the component.
97
     * This method may return different results depending on the value of `$checkInstance`.
98
     *
99
     * - If `$checkInstance` is false (default), the method will return a value indicating whether the locator has the specified
100
     *   component definition.
101
     * - If `$checkInstance` is true, the method will return a value indicating whether the locator has
102
     *   instantiated the specified component.
103
     *
104
     * @param string $id component ID (e.g. `db`).
105
     * @param bool $checkInstance whether the method should check if the component is shared and instantiated.
106
     * @return bool whether the locator has the specified component definition or has instantiated the component.
107
     * @see set()
108
     */
109 3107
    public function has($id, $checkInstance = false)
110
    {
111 3107
        return $checkInstance ? isset($this->_components[$id]) : isset($this->_definitions[$id]);
112
    }
113
114
    /**
115
     * Returns the component instance with the specified ID.
116
     *
117
     * @param string $id component ID (e.g. `db`).
118
     * @param bool $throwException whether to throw an exception if `$id` is not registered with the locator before.
119
     * @return object|null the component of the specified ID. If `$throwException` is false and `$id`
120
     * is not registered before, null will be returned.
121
     * @throws InvalidConfigException if `$id` refers to a nonexistent component ID
122
     * @see has()
123
     * @see set()
124
     */
125 1009
    public function get($id, $throwException = true)
126
    {
127 1009
        if (isset($this->_components[$id])) {
128 709
            return $this->_components[$id];
129
        }
130
131 1009
        if (isset($this->_definitions[$id])) {
132 955
            $definition = $this->_definitions[$id];
133 955
            if (is_object($definition) && !$definition instanceof Closure) {
134 37
                return $this->_components[$id] = $definition;
135
            }
136
137 931
            return $this->_components[$id] = Yii::createObject($definition);
138 150
        } elseif ($throwException) {
139
            throw new InvalidConfigException("Unknown component ID: $id");
140
        }
141
142 150
        return null;
143
    }
144
145
    /**
146
     * Registers a component definition with this locator.
147
     *
148
     * For example,
149
     *
150
     * ```php
151
     * // a class name
152
     * $locator->set('cache', \yii\caching\FileCache::class);
153
     *
154
     * // a configuration array
155
     * $locator->set('db', [
156
     *     'class' => \yii\db\Connection::class,
157
     *     'dsn' => 'mysql:host=127.0.0.1;dbname=demo',
158
     *     'username' => 'root',
159
     *     'password' => '',
160
     *     'charset' => 'utf8',
161
     * ]);
162
     *
163
     * // an anonymous function
164
     * $locator->set('cache', function ($params) {
165
     *     return new \yii\caching\FileCache;
166
     * });
167
     *
168
     * // an instance
169
     * $locator->set('cache', new \yii\caching\FileCache);
170
     * ```
171
     *
172
     * If a component definition with the same ID already exists, it will be overwritten.
173
     *
174
     * @param string $id component ID (e.g. `db`).
175
     * @param mixed $definition the component definition to be registered with this locator.
176
     * It can be one of the following:
177
     *
178
     * - a class name
179
     * - a configuration array: the array contains name-value pairs that will be used to
180
     *   initialize the property values of the newly created object when [[get()]] is called.
181
     *   The `class` element is required and stands for the the class of the object to be created.
182
     * - a PHP callable: either an anonymous function or an array representing a class method (e.g. `['Foo', 'bar']`).
183
     *   The callable will be called by [[get()]] to return an object associated with the specified component ID.
184
     * - an object: When [[get()]] is called, this object will be returned.
185
     *
186
     * @throws InvalidConfigException if the definition is an invalid configuration array
187
     */
188 3161
    public function set($id, $definition)
189
    {
190 3161
        unset($this->_components[$id]);
191
192 3161
        if ($definition === null) {
193 3
            unset($this->_definitions[$id]);
194 3
            return;
195
        }
196
197 3161
        if (is_object($definition) || is_callable($definition, true)) {
198
            // an object, a class name, or a PHP callable
199 81
            $this->_definitions[$id] = $definition;
200 3159
        } elseif (is_array($definition)) {
201
            // a configuration array
202 3159
            if (isset($definition['class'])) {
203 3159
                $this->_definitions[$id] = $definition;
204
            } else {
205 3159
                throw new InvalidConfigException("The configuration for the \"$id\" component must contain a \"class\" element.");
206
            }
207
        } else {
208
            throw new InvalidConfigException("Unexpected configuration type for the \"$id\" component: " . gettype($definition));
209
        }
210 3161
    }
211
212
    /**
213
     * Removes the component from the locator.
214
     * @param string $id the component ID
215
     */
216 1
    public function clear($id)
217
    {
218 1
        unset($this->_definitions[$id], $this->_components[$id]);
219 1
    }
220
221
    /**
222
     * Returns the list of the component definitions or the loaded component instances.
223
     * @param bool $returnDefinitions whether to return component definitions instead of the loaded component instances.
224
     * @return array the list of the component definitions or the loaded component instances (ID => definition or instance).
225
     */
226 4
    public function getComponents($returnDefinitions = true)
227
    {
228 4
        return $returnDefinitions ? $this->_definitions : $this->_components;
229
    }
230
231
    /**
232
     * Registers a set of component definitions in this locator.
233
     *
234
     * This is the bulk version of [[set()]]. The parameter should be an array
235
     * whose keys are component IDs and values the corresponding component definitions.
236
     *
237
     * For more details on how to specify component IDs and definitions, please refer to [[set()]].
238
     *
239
     * If a component definition with the same ID already exists, it will be overwritten.
240
     *
241
     * The following is an example for registering two component definitions:
242
     *
243
     * ```php
244
     * [
245
     *     'db' => [
246
     *         'class' => \yii\db\Connection::class,
247
     *         'dsn' => 'sqlite:path/to/file.db',
248
     *     ],
249
     *     'cache' => [
250
     *         'class' => \yii\caching\DbCache::class,
251
     *         'db' => 'db',
252
     *     ],
253
     * ]
254
     * ```
255
     *
256
     * @param array $components component definitions or instances
257
     */
258 3158
    public function setComponents($components)
259
    {
260 3158
        foreach ($components as $id => $component) {
261 3158
            $this->set($id, $component);
262
        }
263 3158
    }
264
}
265