GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — master ( 7762b1...7ac25d )
by Hong
03:37
created

FactoryTrait::getDefinition()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 20
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
c 3
b 0
f 0
dl 0
loc 20
rs 9.2
cc 4
eloc 10
nc 4
nop 2
1
<?php
2
/**
3
 * Phossa Project
4
 *
5
 * PHP version 5.4
6
 *
7
 * @category  Library
8
 * @package   Phossa2\Di
9
 * @copyright Copyright (c) 2016 phossa.com
10
 * @license   http://mit-license.org/ MIT License
11
 * @link      http://www.phossa.com/
12
 */
13
/*# declare(strict_types=1); */
14
15
namespace Phossa2\Di\Traits;
16
17
use Phossa2\Di\Message\Message;
18
use Phossa2\Di\Exception\LogicException;
19
use Phossa2\Di\Exception\NotFoundException;
20
21
/**
22
 * FactoryTrait
23
 *
24
 * Create service instance here
25
 *
26
 * @package Phossa2\Di
27
 * @author  Hong Zhang <[email protected]>
28
 * @version 2.0.0
29
 * @since   2.0.0 added
30
 */
31
trait FactoryTrait
32
{
33
    use ScopeTrait;
34
35
    /**
36
     * for loop detection
37
     *
38
     * @var    array
39
     * @access protected
40
     */
41
    protected $loop = [];
42
43
    /**
44
     * Full scope info
45
     *
46
     * @param  string $id
47
     * @return array
48
     * @access protected
49
     */
50
    protected function fullScopeInfo(/*# string */ $id)/*# : array */
51
    {
52
        list($rawId, $scope) = $this->scopedInfo($id);
53
54
        // special treatment if $scope is a '#service_id'
55
        if (isset($this->loop[$scope])) {
56
            $scope .= '_' . $this->loop[$scope];
57
        }
58
59
        return [$rawId, $this->scopedId($rawId, $scope), $scope];
60
    }
61
62
    /**
63
     * Create the instance with loop detection
64
     *
65
     * @param  string $rawId
66
     * @param  array $args arguments for the constructor if any
67
     * @return object
68
     * @throws LogicException if instantiation goes wrong or loop detected
69
     * @access protected
70
     */
71
    protected function createInstance(/*# string */ $rawId, array $args)
72
    {
73
        static $counter = 0;
74
75
        // conver 'service_id' to '#service_id'
76
        $serviceId = $this->getServiceId($rawId);
77
78
        // loop detected
79
        if (isset($this->loop[$serviceId])) {
80
            throw new LogicException(
81
                Message::get(Message::DI_LOOP_DETECTED, $rawId),
82
                Message::DI_LOOP_DETECTED
83
            );
84
        }
85
86
        // set loop marker
87
        $this->loop[$serviceId] = ++$counter;
88
89
        // create the service instance
90
        $obj = $this->createFromId($rawId, $args);
91
92
        // remove current marker
93
        unset($this->loop[$serviceId]);
94
95
        return $obj;
96
    }
97
98
    /**
99
     * Create object base on the raw id
100
     *
101
     * @param  string $rawId
102
     * @param  array $arguments
103
     * @return object
104
     * @throws LogicException if instantiation goes wrong
105
     * @access protected
106
     */
107
    protected function createFromId(/*# string */ $rawId, array $arguments)
108
    {
109
        // get definition
110
        $def = $this->getDefinition($rawId, $arguments);
111
112
        if (is_string($def['class'])) {
113
            // classname
114
            $obj = $this->constructObject($def['class'], $def['args']);
115
116
        } else {
117
            // object or callable etc.
118
            $obj = $this->executeCallable($def['class'], $def['args']);
119
        }
120
121
        // after creation
122
        $this->afterCreation($obj, $def);
123
124
        return $obj;
125
    }
126
127
    /**
128
     * Get service definition
129
     *
130
     * @param  string $rawId
131
     * @param  array $args
132
     * @return array
133
     * @access protected
134
     */
135
    protected function getDefinition(
136
        /*# string */ $rawId,
137
        array $args
138
    )/*# : array */ {
139
        // get the definition
140
        $def = $this->getResolver()->getService($rawId);
141
142
        // fix class
143
        if (!is_array($def)) {
144
            $def = ['class' => $def];
145
        }
146
147
        // fix arguments
148
        if (!empty($args) || !isset($def['args'])) {
149
            $this->resolve($args);
150
            $def['args'] = $args;
151
        }
152
153
        return $def;
154
    }
155
156
    /**
157
     * Instantiate service object from classname
158
     *
159
     * @param  string $class
160
     * @param  array $args
161
     * @return object
162
     * @throws LogicException if something goes wrong
163
     * @access protected
164
     */
165
    protected function constructObject(/*# string */ $class, array $args)
166
    {
167
        $reflector = new \ReflectionClass($class);
168
        $constructor = $reflector->getConstructor();
169
170
        // not constructor defined
171
        if (is_null($constructor)) {
172
            $obj = $reflector->newInstanceWithoutConstructor();
173
174
        // normal class with constructor
175
        } else {
176
            $args = $this->matchArguments(
177
                $constructor->getParameters(),
178
                $args
179
            );
180
            $obj = $reflector->newInstanceArgs($args);
181
        }
182
183
        return $obj;
184
    }
185
186
    /**
187
     * Execute a (pseudo) callable with arguments
188
     *
189
     * @param  callable|array|object $callable callable or pseudo callable
190
     * @param  array $arguments
191
     * @return mixed
192
     * @throws LogicException if something goes wrong
193
     * @access protected
194
     */
195
    protected function executeCallable($callable, array $arguments = [])
196
    {
197
        // not callable
198
        if (!is_callable($callable)) {
199
            return $callable;
200
        }
201
202
        if (!empty($arguments)) {
203
            $args = $this->matchCallableArguments($callable, $arguments);
204
            return call_user_func_array($callable, $args);
205
        } else {
206
            return call_user_func($callable);
207
        }
208
    }
209
210
    /**
211
     * Rebuild callable base methodName and object
212
     *
213
     * method:
214
     * - ['function', [ arguments...]]
215
     *
216
     * - [ callable, [ arguments ...]]
217
     *
218
     * - ['method', [ arguments ...]]
219
     *   convert to [[$object, 'method'], [ ... ]]
220
     *
221
     * @param  mixed method
222
     * @param  object|null $object to construct callable
223
     * @throws LogicException if something goes wrong
224
     * @access protected
225
     */
226
    protected function executeMethod($method, $object = null)
227
    {
228
        $callable  = $method[0];
229
        $arguments = isset($method[1]) ? $method[1] : [];
230
231
        // rebuild callable from $object
232
        if (null !== $object) {
233
            $callable = [$object, $callable];
234
        }
235
236
        $this->executeCallable($callable, $arguments);
237
    }
238
239
    /**
240
     * Matching callable arguments
241
     *
242
     * @param  callable $callable
243
     * @param  array $arguments
244
     * @return array the matched arguments
245
     * @throws LogicException if something goes wrong
246
     * @access protected
247
     */
248
    protected function matchCallableArguments(
249
        callable $callable,
250
        array $arguments
251
    )/*# : array */ {
252
        // array type
253
        if (is_array($callable)) {
254
            $reflector = new \ReflectionClass($callable[0]);
255
            $method = $reflector->getMethod($callable[1]);
256
257
        } elseif ($this->isInvocable($callable)) {
258
            $reflector = new \ReflectionClass($callable);
259
            $method = $reflector->getMethod('__invoke');
260
261
        // simple function
262
        } else {
263
            $method = new \ReflectionFunction($callable);
264
        }
265
266
        return $this->matchArguments(
267
            $method->getParameters(),
268
            $arguments
269
        );
270
    }
271
272
    /**
273
     * Match provided arguments with a method/function's reflection parameters
274
     *
275
     * @param  \ReflectionParameter[] $reflectionParameters
276
     * @param  array $providedArguments
277
     * @return array the resolved arguments
278
     * @throws LogicException
279
     * @throws NotFoundException
280
     * @access protected
281
     */
282
    protected function matchArguments(
283
        array $reflectionParameters,
284
        array $providedArguments
285
    )/*# : array */ {
286
        // result
287
        $resolvedArguments = [];
288
289
        // go thru each predefined parameter
290
        foreach ($reflectionParameters as $i => $param) {
291
            // arg to match with
292
            $argument = isset($providedArguments[0]) ? $providedArguments[0] : null;
293
294
            // an interface or class ?
0 ignored issues
show
Unused Code Comprehensibility introduced by
40% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
295
            $class = $param->getClass();
296
297
            if ($this->isTypeMatched($param, $argument, $class)) {
298
                // type matched
299
                $resolvedArguments[$i] = array_shift($providedArguments);
300
301
            } elseif (null !== $class) {
302
                // not matched, but $param is an interface or class
303
                $resolvedArguments[$i] = $this->getObjectByClass($class->getName());
304
305
            } elseif ($param->isOptional()) {
306
                // $param is optional, $arg is null
307
                break;
308
            } else {
309
                throw new LogicException(
310
                    Message::get(Message::DI_PARAMETER_NOTFOUND, $param->getName()),
311
                    Message::DI_PARAMETER_NOTFOUND
312
                );
313
            }
314
        }
315
316
        // append remained arguments if any
317
        if (!empty($providedArguments)) {
318
            $resolvedArguments = array_merge($resolvedArguments, $providedArguments);
319
        }
320
321
        return $resolvedArguments;
322
    }
323
324
    /**
325
     * Is $parameter same type as the $argument ?
326
     *
327
     * @param  \ReflectionParameter $parameter
328
     * @param  mixed $argument
329
     * @param  null|string $class
330
     * @return bool
331
     * @throws LogicException if type missmatch
332
     * @access protected
333
     */
334
    protected function isTypeMatched(
335
        \ReflectionParameter $parameter,
336
        $argument,
337
        $class
338
    )/*# : bool */ {
339
        if (null === $argument) {
340
            return false;
341
        } elseif (null !== $class) {
342
            return is_a($argument, $parameter->getClass()->getName());
343
        } else {
344
            return true;
345
        }
346
    }
347
348
    /**
349
     * Get an object base on provided classname or interface name
350
     *
351
     * @param  string $classname class or interface name
352
     * @return object
353
     * @throws \Exception if something goes wrong
354
     * @access protected
355
     */
356
    protected function getObjectByClass(/*# string */ $classname)
357
    {
358
        // mapping exists
359
        if ($this->getResolver()->hasMapping($classname)) {
360
            $classname = $this->getResolver()->getMapping($classname);
361
            if (is_object($classname)) {
362
                return $classname;
363
            }
364
        }
365
        return $this->get($classname);
366
    }
367
368
    /**
369
     * Is $var an object with '__invoke()' defined
370
     *
371
     * @param  mixed $var
372
     * @return bool
373
     * @access protected
374
     */
375
    protected function isInvocable($var)/*# : bool */
376
    {
377
        return is_object($var) && method_exists($var, '__invoke');
378
    }
379
380
    /**
381
     * Things to do after object created.
382
     *
383
     * @param  object $object
384
     * @param  array $definition service definition for $object
385
     * @access protected
386
     */
387
    protected function afterCreation($object, array $definition)
388
    {
389
        // execute methods from this object
390
        if (isset($definition['methods'])) {
391
            foreach ($definition['methods'] as $method) {
392
                $this->executeMethod($method, $object);
393
            }
394
        }
395
396
        // execute common methods in 'di.common' for all created objects
397
        if ($this->getResolver()->has('', 'common')) {
398
            $methods = $this->mergeNodeInfo($this->getResolver()->get('', 'common'));
399
            $this->executeTester($object, $methods);
400
        }
401
    }
402
403
    /**
404
     * Execute [tester, todo] pairs, both use $object as argument
405
     *
406
     * signatures
407
     * - tester: function($object, $container) { return $object instance of XXXX; }
408
     * - todoer: function($object, $container) { }
409
     *
410
     * @param  object $object
411
     * @param  array $methods
412
     * @access protected
413
     */
414
    protected function executeTester($object, array $methods)
415
    {
416
        foreach ($methods as $method) {
417
            if ($method[0]($object, $this)) {
418
                $method[1]($object, $this);
419
            }
420
        }
421
    }
422
423
    /**
424
     * Merge data in the node, normally merge methods
425
     *
426
     * @param  array $nodeData
427
     * @return array
428
     * @access protected
429
     */
430
    protected function mergeNodeInfo(array $nodeData)/*# : array */
431
    {
432
        // no merge
433
        if (isset($nodeData[0])) {
434
            return $nodeData;
435
        }
436
437
        // in sections
438
        $result = [];
439
        foreach ($nodeData as $data) {
440
            $result = array_merge($result, $data);
441
        }
442
        return $result;
443
    }
444
445
    /**
446
     * Append '#' to rawId, representing a service object id
447
     *
448
     * @param  string $rawId
449
     * @return string
450
     * @access protected
451
     */
452
    protected function getServiceId(/*# string */ $rawId)/*# : string */
453
    {
454
        return '#' . $rawId;
455
    }
456
457
    /**
458
     * @param string $id Identifier of the entry to look for.
459
     * @return mixed Entry.
460
     */
461
    abstract public function get($id);
462
}
463