Passed
Push — master ( e5acb2...2b13b1 )
by Evgeny
07:45
created

ServiceLocator::get()   C

Complexity

Conditions 11
Paths 26

Size

Total Lines 48
Code Lines 30

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 27
CRAP Score 12.0562

Importance

Changes 0
Metric Value
cc 11
eloc 30
c 0
b 0
f 0
nc 26
nop 2
dl 0
loc 48
ccs 27
cts 34
cp 0.7941
crap 12.0562
rs 5.2653

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * Copyright 2016 - 2018, Cake Development Corporation (http://cakedc.com)
4
 *
5
 * Licensed under The MIT License
6
 * Redistributions of files must retain the above copyright notice.
7
 *
8
 * @copyright Copyright 2016 - 2018, Cake Development Corporation (http://cakedc.com)
9
 * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
10
 */
11
12
namespace CakeDC\Api\Service\Locator;
13
14
use CakeDC\Api\Service\Service;
15
use Cake\Core\App;
16
use Cake\Core\Configure;
17
use Cake\Utility\Inflector;
18
use RuntimeException;
19
20
/**
21
 * Provides a default registry/factory for Service objects.
22
 */
23
class ServiceLocator implements LocatorInterface
24
{
25
26
    /**
27
     * Configuration for aliases.
28
     *
29
     * @var array
30
     */
31
    protected $_config = [];
32
33
    /**
34
     * Instances that belong to the registry.
35
     *
36
     * @var array
37
     */
38
    protected $_instances = [];
39
40
    /**
41
     * Contains a list of Method objects that were created out of the
42
     * built-in Method class. The list is indexed by method names
43
     *
44
     * @var array
45
     */
46
    protected $_fallbacked = [];
47
48
    /**
49
     * Contains a list of options that were passed to get() method.
50
     *
51
     * @var array
52
     */
53
    protected $_options = [];
54
55
    /**
56
     * Stores a list of options to be used when instantiating an object
57
     * with a matching alias.
58
     *
59
     * @param string|array $alias Name of the alias or array to completely overwrite current config.
60
     * @param array|null $options list of options for the alias
61
     * @return $this
62
     * @throws \RuntimeException When you attempt to configure an existing table instance.
63
     */
64
    public function setConfig($alias, $options = null)
65
    {
66
        if (!is_string($alias)) {
67
            $this->_config = $alias;
68
69
            return $this;
70
        }
71
72
        if (isset($this->_instances[$alias])) {
73
            throw new RuntimeException(sprintf(
74
                'You cannot configure "%s", it has already been constructed.',
75
                $alias
76
            ));
77
        }
78
79
        $this->_config[$alias] = $options;
80
81
        return $this;
82
    }
83
84
    /**
85
     * Returns configuration for an alias or the full configuration array for all aliases.
86
     *
87
     * @param string|null $alias Alias to get config for, null for complete config.
88
     * @return array The config data.
89
     */
90
    public function getConfig($alias = null)
91
    {
92
        if ($alias === null) {
93
            return $this->_config;
94
        }
95
96
        return isset($this->_config[$alias]) ? $this->_config[$alias] : [];
97
    }
98
99
    /**
100
     * Stores a list of options to be used when instantiating an object
101
     * with a matching alias.
102
     *
103
     * The options that can be stored are those that are recognized by `get()`
104
     * If second argument is omitted, it will return the current settings
105
     * for $alias.
106
     *
107
     * If no arguments are passed it will return the full configuration array for
108
     * all aliases
109
     *
110
     * @param string|null $alias Name of the alias
111
     * @param array|null $options list of options for the alias
112
     * @return array The config data.
113
     * @throws RuntimeException When you attempt to configure an existing method instance.
114
     * @deprecated 3.6.0 Use setConfig()/getConfig() instead.
115
     */
116
    public function config($alias = null, $options = null)
117
    {
118
        deprecationWarning(
119
            'ServiceLocator::config() is deprecated. ' .
120
            'Use getConfig()/setConfig() instead.'
121
        );
122
        if ($alias !== null) {
123
            if (is_string($alias) && $options === null) {
124
                return $this->getConfig($alias);
125
            }
126
127
            $this->setConfig($alias, $options);
128
        }
129
130
        return $this->getConfig($alias);
131
    }
132
133
    /**
134
     * Get a method instance from the registry.
135
     *
136
     * Methods are only created once until the registry is flushed.
137
     * This means that aliases must be unique across your application.
138
     * This is important because method associations are resolved at runtime
139
     * and cyclic references need to be handled correctly.
140
     *
141
     * The options that can be passed are the same as in `Method::__construct()`, but the
142
     * key `className` is also recognized.
143
     *
144
     * If $options does not contain `className` CakePHP will attempt to construct the
145
     * class name based on the alias. If this class does not exist,
146
     * then the default `CakeDC\OracleDriver\ORM\Method` class will be used. By setting the `className`
147
     * option you can define the specific class to use. This className can
148
     * use a plugin short class reference.
149
     *
150
     * If you use a `$name` that uses plugin syntax only the name part will be used as
151
     * key in the registry. This means that if two plugins, or a plugin and app provide
152
     * the same alias, the registry will only store the first instance.
153
     *
154
     * If no `method` option is passed, the method name will be the underscored version
155
     * of the provided $alias.
156
     *
157
     * If no `connection` option is passed the method's defaultConnectionName() method
158
     * will be called to get the default connection name to use.
159
     *
160
     * @param string $alias The alias name you want to get.
161
     * @param array $options The options you want to build the method with.
162
     *   If a method has already been loaded the options will be ignored.
163
     * @return \CakeDC\Api\Service\Service
164
     * @throws \RuntimeException When you try to configure an alias that already exists.
165
     */
166 91
    public function get($alias, array $options = [])
167
    {
168 91
        $alias = Inflector::camelize($alias);
169
170 91
        if (isset($this->_instances[$alias]) && empty($options['refresh'])) {
171
            if (!empty($options) && !$this->_compareOptions($alias, $options)) {
172
                throw new RuntimeException(sprintf(
173
                    'You cannot configure "%s", it already exists in the registry.',
174
                    $alias
175
                ));
176
            }
177
178
            return $this->_instances[$alias];
179
        }
180
181 91
        $this->_options[$alias] = $options;
182 91
        list(, $classAlias) = pluginSplit($alias);
183 91
        $options = ['alias' => $classAlias] + $options;
184
185 91
        if (isset($this->_config[$alias])) {
186
            $options += $this->_config[$alias];
187
        }
188
189 91
        if (empty($options['className'])) {
190 91
            $options['className'] = Inflector::camelize($alias);
191 91
        }
192
193 91
        $className = $this->_getClassName($alias, $options);
194 91
        if (!$className && strpos($options['className'], '.') === false) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $className of type string|false is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
195 34
            $_options = $options;
196 34
            $_options['className'] = 'CakeDC/Api.' . $_options['className'];
197 34
            $className = $this->_getClassName($alias, $_options);
198 34
        }
199 91
        if ($className) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $className of type string|false is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
200 77
            $options['className'] = $className;
201 77
            $options['service'] = Inflector::underscore($alias);
202 77
        } else {
203 23
            $fallbackClass = Configure::read('Api.ServiceFallback');
204 23
            if ($fallbackClass) {
205 23
                $options['className'] = $fallbackClass;
206 23
                $options['service'] = Inflector::underscore($alias);
207 23
            }
208
        }
209
210 91
        $this->_instances[$alias] = $this->_create($options);
211
212 91
        return $this->_instances[$alias];
213
    }
214
215
    /**
216
     * Gets the method class name.
217
     *
218
     * @param string $alias The alias name you want to get.
219
     * @param array $options Method options array.
220
     * @return string
221
     */
222 91
    protected function _getClassName($alias, array $options = [])
223
    {
224 91
        $useVersions = Configure::read('Api.useVersioning');
225 91
        if ($useVersions) {
226
            $versionPrefix = Configure::read('Api.versionPrefix');
227
            if (empty($versionPrefix)) {
228
                $versionPrefix = 'v';
229
            }
230
            if (empty($options['version'])) {
231
                $options['version'] = $versionPrefix . Configure::read('Api.defaultVersion');
232
            }
233
            $version = '/' . $options['version'];
234
        } else {
235 91
            $version = '';
236
        }
237 91
        if (empty($options['className'])) {
238
            $options['className'] = Inflector::camelize($alias);
239
        }
240
241 91
        return App::className($options['className'], 'Service' . $version, 'Service');
242
    }
243
244
    /**
245
     * Wrapper for creating method instances
246
     *
247
     * @param array $options The alias to check for.
248
     * @return \CakeDC\Api\Service\Service
249
     */
250 91
    protected function _create(array $options)
251
    {
252 91
        return new $options['className']($options);
253
    }
254
255
    /**
256
     * {@inheritDoc}
257
     */
258
    public function exists($alias)
259
    {
260
        return isset($this->_instances[$alias]);
261
    }
262
263
    /**
264
     * {@inheritDoc}
265
     */
266
    public function set($alias, Service $object)
267
    {
268
        return $this->_instances[$alias] = $object;
269
    }
270
271
    /**
272
     * {@inheritDoc}
273
     */
274 108
    public function clear()
275
    {
276 108
        $this->_instances = [];
277 108
        $this->_config = [];
278 108
        $this->_fallbacked = [];
279 108
    }
280
281
    /**
282
     * Returns the list of methods that were created by this registry that could
283
     * not be instantiated from a specific subclass. This method is useful for
284
     * debugging common mistakes when setting up associations or created new method
285
     * classes.
286
     *
287
     * @return array
288
     */
289
    public function genericInstances()
290
    {
291
        return $this->_fallbacked;
292
    }
293
294
    /**
295
     * {@inheritDoc}
296
     */
297
    public function remove($alias)
298
    {
299
        unset(
300
            $this->_instances[$alias],
301
            $this->_config[$alias],
302
            $this->_fallbacked[$alias]
303
        );
304
    }
305
306
    /**
307
     * Compare services options.
308
     *
309
     * @param string $alias Service alias.
310
     * @param array $options Options.
311
     * @return bool
312
     */
313
    protected function _compareOptions($alias, array $options)
314
    {
315
        $currentOptions = $this->_options[$alias];
316
        unset($currentOptions['controller']);
317
        unset($options['controller']);
318
319
        return $currentOptions == $options;
320
    }
321
}
322