Completed
Push — master ( 95ab99...c92deb )
by Augusto
02:11
created

Container::removeDuplicatedSpaces()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 1
crap 1
1
<?php
2
3
namespace Respect\Config;
4
5
use InvalidArgumentException as Argument;
6
use ArrayObject;
7
use ReflectionClass;
8
use ReflectionFunction;
9
use Interop\Container\ContainerInterface;
10
11
class Container extends ArrayObject implements ContainerInterface
12
{
13
    protected $configurator;
14
15 37
    public function __construct($configurator = null)
16
    {
17 37
        $this->configurator = $configurator;
18 37
    }
19
20 2
    public function __isset($name)
21
    {
22 2
        return $this->has($name);
23
    }
24
    
25 3
    public function has($name) 
26
    {
27 3
        if ($this->configurator) {
28 2
            $this->configure();
29
        }
30
31 3
        return parent::offsetExists($name);
0 ignored issues
show
Comprehensibility Bug introduced by
It seems like you call parent on a different method (offsetExists() instead of has()). Are you sure this is correct? If so, you might want to change this to $this->offsetExists().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
32
    }
33
34 5
    public function __invoke($spec)
35
    {
36 5
        if (is_callable($spec)) {
37 3
            if (is_array($spec)) {
38 1
                list($class, $method) = $spec;
39 1
                $class = new ReflectionClass($class);
40 1
                $object = $class->newInstance();
41 1
                $mirror = $class->getMethod($method);
42
            } else {
43 2
                $object = false;
44 2
                $mirror = new ReflectionFunction($spec);
45
            }
46 3
            $container = $this;
47 3
            $arguments = array_map(
48
                function ($param) use ($container) {
49 3
                    if ($paramClass = $param->getClass()) {
50 3
                        $paramClassName = $paramClass->getName();
51
52 3
                        return $container->getItem($paramClassName);
53
                    }
54 3
                },
55 3
                $mirror->getParameters()
56
            );
57 3
            if ($object) {
58 1
                return $mirror->invokeArgs($object, $arguments);
59
            }
60
61 2
            return $mirror->invokeArgs($arguments);
62
        }
63 4
        if ((bool) array_filter(func_get_args(), 'is_object')) {
64 2
            foreach (func_get_args() as $dependency) {
65 2
                $this[get_class($dependency)] = $dependency;
66
            }
67
        }
68
69 4
        foreach ($spec as $name => $item) {
70 4
            parent::offsetSet($name, $item);
0 ignored issues
show
Comprehensibility Bug introduced by
It seems like you call parent on a different method (offsetSet() instead of __invoke()). Are you sure this is correct? If so, you might want to change this to $this->offsetSet().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
71
        }
72
73 4
        if ($this->configurator) {
74 2
            $this->configure();
75
        }
76
77 4
        return $this;
78
    }
79
80 1
    public function __call($name, $dict)
81
    {
82 1
        $this->__invoke($dict[0]);
83
84 1
        return $this->getItem($name);
85
    }
86
87 13
    protected function configure()
88
    {
89 13
        $configurator = $this->configurator;
90 13
        $this->configurator = null;
91
92 13
        if (is_null($configurator)) {
93
            return;
94
        }
95
96 13
        if (is_array($configurator)) {
97 7
            return $this->loadArray($configurator);
98
        }
99
100 6
        if (file_exists($configurator)) {
101 1
            return $this->loadFile($configurator);
102
        }
103
104 5
        if (is_string($configurator)) {
105 4
            return $this->loadString($configurator);
106
        }
107
108 1
        throw new Argument("Invalid input. Must be a valid file or array");
109
    }
110
    
111 37
    public function getItem($name, $raw = false)
112
    {
113 37
        if ($this->configurator) {
114 9
            $this->configure();
115
        }
116
117 35
        if (!isset($this[$name])) {
118 1
            throw new NotFoundException("Item $name not found");
119
        }
120
121 34
        if ($raw || !is_callable($this[$name])) {
122 28
            return $this[$name];
123
        }
124
125 13
        return $this->lazyLoad($name);
126
    }
127
    
128 2
    public function get($name)
129
    {
130 2
        return $this->getItem($name);
131
    }
132
133 4
    public function loadString($configurator)
134
    {
135 4
        $iniData = parse_ini_string($configurator, true);
136 4
        if (false === $iniData || count($iniData) == 0) {
137 1
            throw new Argument("Invalid configuration string");
138
        }
139
140 3
        return $this->loadArray($iniData);
141
    }
142
143 1
    public function loadFile($configurator)
144
    {
145 1
        $iniData = parse_ini_file($configurator, true);
146 1
        if (false === $iniData) {
147
            throw new Argument("Invalid configuration INI file");
148
        }
149
150 1
        return $this->loadArray($iniData);
151
    }
152
153 32
    protected function state()
154
    {
155
        return array_filter($this->getArrayCopy(), function ($v) {
156 5
            return !is_object($v) || !$v instanceof Instantiator;
157 32
        });
158
    }
159
160 32
    public function loadArray(array $configurator)
161
    {
162 32
        foreach ($this->state() + $configurator as $key => $value) {
163 32
            if ($value instanceof \Closure) {
164 1
                continue;
165
            }
166 32
            $this->parseItem($key, $value);
167
        }
168 32
    }
169
170 17
    public function __get($name)
171
    {
172 17
        return $this->getItem($name);
173
    }
174
175 5
    public function __set($name, $value)
176
    {
177 5
        if (isset($this[$name]) && $this[$name] instanceof Instantiator) {
178 1
            $this[$name]->setInstance($value);
179
        }
180 5
        $this[$name] = $value;
181 5
    }
182
183 19
    protected function keyHasStateInstance($key, &$k)
184
    {
185 19
        return $this->offsetExists($k = current((explode(' ', $key))));
186
    }
187
188 32
    protected function keyHasInstantiator($key)
189
    {
190 32
        return false !== stripos($key, ' ');
191
    }
192
193 32
    protected function parseItem($key, $value)
194
    {
195 32
        $key = trim($key);
196 32
        if ($this->keyHasInstantiator($key)) {
197 19
            if ($this->keyHasStateInstance($key, $k)) {
198 1
                $this->offsetSet($key, $this[$k]);
199
            } else {
200 19
                $this->parseInstantiator($key, $value);
201
            }
202
        } else {
203 16
            $this->parseStandardItem($key, $value);
204
        }
205 32
    }
206
207 9
    protected function parseSubValues(&$value)
208
    {
209 9
        foreach ($value as &$subValue) {
210 9
            $subValue = $this->parseValue($subValue);
211
        }
212
213 9
        return $value;
214
    }
215
216 16
    protected function parseStandardItem($key, &$value)
217
    {
218 16
        if (is_array($value)) {
219 2
            $this->parseSubValues($value);
220
        } else {
221 15
            $value = $this->parseValue($value);
222
        }
223
224 16
        $this->offsetSet($key, $value);
225 16
    }
226
227 19
    protected function removeDuplicatedSpaces($string)
228
    {
229 19
        return preg_replace('/\s+/', ' ', $string);
230
    }
231
232 19
    protected function parseInstantiator($key, $value)
233
    {
234 19
        $key = $this->removeDuplicatedSpaces($key);
235 19
        list($keyName, $keyClass) = explode(' ', $key, 2);
236 19
        if ('instanceof' === $keyName) {
237 1
            $keyName = $keyClass;
238
        }
239 19
        $instantiator = new Instantiator($keyClass);
240
241 19
        if (is_array($value)) {
242 18
            foreach ($value as $property => $pValue) {
243 18
                $instantiator->setParam($property, $this->parseValue($pValue));
244
            }
245
        } else {
246 1
            $instantiator->setParam('__construct', $this->parseValue($value));
247
        }
248
249 19
        $this->offsetSet($keyName, $instantiator);
250 19
    }
251
252 30
    protected function parseValue($value)
253
    {
254 30
        if ($value instanceof Instantiator) {
255
            return $value;
256 30
        } elseif (is_array($value)) {
257 6
            return $this->parseSubValues($value);
258
        } elseif (empty($value)) {
259 5
            return null;
260
        } else {
261 28
            return $this->parseSingleValue($value);
262
        }
263
    }
264 28
    protected function hasCompleteBrackets($value)
265
    {
266 28
        return false !== strpos($value, '[') && false !== strpos($value, ']');
267
    }
268
269 28
    protected function parseSingleValue($value)
270
    {
271 28
        $value = trim($value);
272 28
        if ($this->hasCompleteBrackets($value)) {
273 14
            return $this->parseBrackets($value);
274
        } else {
275 24
            return $this->parseConstants($value);
276
        }
277
    }
278
279 24
    protected function parseConstants($value)
280
    {
281 24
        if (preg_match('/^[\\a-zA-Z_]+([:]{2}[A-Z_]+)?$/', $value) && defined($value)) {
282 3
            return constant($value);
283
        } else {
284 22
            return $value;
285
        }
286
    }
287
288 14
    protected function matchSequence(&$value)
289
    {
290 14
        if (preg_match('/^\[(.*?,.*?)\]$/', $value, $match)) {
291 4
            return (boolean) ($value = $match[1]);
292
        }
293 12
    }
294
295 12
    protected function matchReference(&$value)
296
    {
297 12
        if (preg_match('/^\[([[:alnum:]_\\\\]+)\]$/', $value, $match)) {
298 11
            return (boolean) ($value = $match[1]);
299
        }
300 1
    }
301
302 14
    protected function parseBrackets($value)
303
    {
304 14
        if ($this->matchSequence($value)) {
305 4
            return $this->parseArgumentList($value);
306 12
        } elseif ($this->matchReference($value)) {
307 11
            return $this->getItem($value, true);
308
        } else {
309 1
            return $this->parseVariables($value);
310
        }
311
    }
312
313 1
    protected function parseVariables($value)
314
    {
315 1
        $self = $this;
316
317 1
        return preg_replace_callback(
318 1
            '/\[(\w+)\]/',
319 1
            function ($match) use (&$self) {
320 1
                return $self[$match[1]] ?: '';
321 1
            },
322 1
            $value
323
        );
324
    }
325
326 4
    protected function parseArgumentList($value)
327
    {
328 4
        $subValues = explode(',', $value);
329
330 4
        return $this->parseSubValues($subValues);
331
    }
332
333 13
    protected function lazyLoad($name)
334
    {
335 13
        $callback = $this[$name];
336 13
        if ($callback instanceof Instantiator && $callback->getMode() != Instantiator::MODE_FACTORY) {
337 10
            return $this[$name] = $callback();
338
        }
339
340 3
        return $callback();
341
    }
342
}
343