Completed
Push — prepare-travis-for-js ( a7ee60 )
by Carsten
08:12 queued 01:33
created

ServiceLocator::getComponents()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

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