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 ( 359e7a...7762b1 )
by Hong
03:04
created

FactoryTrait::executeMethod()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 12
rs 9.4285
cc 3
eloc 6
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  sting $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
            // resolve external arguments
150
            $this->resolve($args);
151
152
            $def['args'] = $args;
153
        }
154
155
        return $def;
156
    }
157
158
    /**
159
     * Instantiate service object from classname
160
     *
161
     * @param  string $class
162
     * @param  array $args
163
     * @return object
164
     * @throws LogicException if something goes wrong
165
     * @access protected
166
     */
167
    protected function constructObject(/*# string */ $class, array $args)
168
    {
169
        $reflector = new \ReflectionClass($class);
170
        $constructor = $reflector->getConstructor();
171
172
        // not constructor defined
173
        if (is_null($constructor)) {
174
            $obj = $reflector->newInstanceWithoutConstructor();
175
176
        // normal class with constructor
177
        } else {
178
            $args = $this->matchArguments(
179
                $constructor->getParameters(),
180
                $args
181
            );
182
            $obj = $reflector->newInstanceArgs($args);
183
        }
184
185
        return $obj;
186
    }
187
188
    /**
189
     * Execute a (pseudo) callable with arguments
190
     *
191
     * @param  callable|array|object $callable callable or pseudo callable
192
     * @param  array $arguments
193
     * @return mixed
194
     * @throws LogicException if something goes wrong
195
     * @access protected
196
     */
197
    protected function executeCallable($callable, array $arguments = [])
198
    {
199
        // not callable
200
        if (!is_callable($callable)) {
201
            return $callable;
202
        }
203
204
        if (!empty($arguments)) {
205
            $args = $this->matchCallableArguments($callable, $arguments);
206
            return call_user_func_array($callable, $args);
207
        } else {
208
            return call_user_func($callable);
209
        }
210
    }
211
212
    /**
213
     * Rebuild callable base methodName and object
214
     *
215
     * method:
216
     * - ['function', [ arguments...]]
217
     *
218
     * - [ callable, [ arguments ...]]
219
     *
220
     * - ['method', [ arguments ...]]
221
     *   convert to [[$object, 'method'], [ ... ]]
222
     *
223
     * @param  mixed method
224
     * @param  object|null $object to construct callable
225
     * @throws LogicException if something goes wrong
226
     * @access protected
227
     */
228
    protected function executeMethod($method, $object = null)
229
    {
230
        $callable  = $method[0];
231
        $arguments = isset($method[1]) ? $method[1] : [];
232
233
        // rebuild callable from $object
234
        if (null !== $object) {
235
            $callable = [$object, $callable];
236
        }
237
238
        $this->executeCallable($callable, $arguments);
239
    }
240
241
    /**
242
     * Matching callable arguments
243
     *
244
     * @param  callable $callable
245
     * @param  array $arguments
246
     * @return array the matched arguments
247
     * @throws LogicException if something goes wrong
248
     * @access protected
249
     */
250
    protected function matchCallableArguments(
251
        callable $callable,
252
        array $arguments
253
    )/*# : array */ {
254
        // array type
255
        if (is_array($callable)) {
256
            $reflector = new \ReflectionClass($callable[0]);
257
            $method = $reflector->getMethod($callable[1]);
258
259
        } elseif ($this->isInvocable($callable)) {
260
            $reflector = new \ReflectionClass($callable);
261
            $method = $reflector->getMethod('__invoke');
262
263
        // simple function
264
        } else {
265
            $method = new \ReflectionFunction($callable);
266
        }
267
268
        return $this->matchArguments(
269
            $method->getParameters(),
270
            $arguments
271
        );
272
    }
273
274
    /**
275
     * Match provided arguments with a method/function's reflection parameters
276
     *
277
     * @param  \ReflectionParameter[] $reflectionParameters
278
     * @param  array $providedArguments
279
     * @return array the resolved arguments
280
     * @throws LogicException
281
     * @throws NotFoundException
282
     * @access protected
283
     */
284
    protected function matchArguments(
285
        array $reflectionParameters,
286
        array $providedArguments
287
    )/*# : array */ {
288
        // result
289
        $resolvedArguments = [];
290
291
        // go thru each predefined parameter
292
        foreach ($reflectionParameters as $i => $param) {
293
            // arg to match with
294
            $argument = isset($providedArguments[0]) ? $providedArguments[0] : null;
295
296
            // $param is an interface or class ?
0 ignored issues
show
Unused Code Comprehensibility introduced by
36% 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...
297
            $class = $param->getClass();
298
299
            if ($this->isTypeMatched($param, $argument, $class)) {
300
                // type matched
301
                $resolvedArguments[$i] = array_shift($providedArguments);
302
303
            } elseif (null !== $class) {
304
                // not matched, but $param is an interface or class
305
                $resolvedArguments[$i] = $this->getObjectByClass($class->getName());
306
307
            } elseif ($param->isOptional()) {
308
                // $param is optional, $arg is null
309
                break;
310
            } else {
311
                throw new LogicException(
312
                    Message::get(Message::DI_PARAMETER_NOTFOUND, $param->getName()),
313
                    Message::DI_PARAMETER_NOTFOUND
314
                );
315
            }
316
        }
317
318
        // append remained arguments if any
319
        if (!empty($providedArguments)) {
320
            $resolvedArguments = array_merge($resolvedArguments, $providedArguments);
321
        }
322
323
        return $resolvedArguments;
324
    }
325
326
    /**
327
     * Is $parameter same type as the $argument ?
328
     *
329
     * @param  \ReflectionParameter $parameter
0 ignored issues
show
Documentation introduced by
There is no parameter named $parameter. Did you maybe mean $parameters?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
330
     * @param  mixed $argument
331
     * @param  null|string $class
332
     * @return bool
333
     * @throws LogicException if type missmatch
334
     * @access protected
335
     */
336
    protected function isTypeMatched(
337
        \ReflectionParameter $parameters,
338
        $argument,
339
        $class
340
    )/*# : bool */ {
341
        if (null === $argument) {
342
            return false;
343
        } elseif (null !== $class) {
344
            return is_a($argument, $parameters->getClass()->getName());
345
        } else {
346
            return true;
347
        }
348
    }
349
350
    /**
351
     * Get an object base on provided classname or interface name
352
     *
353
     * @param  string $classname class or interface name
354
     * @return object
355
     * @throws \Exception if something goes wrong
356
     * @access protected
357
     */
358
    protected function getObjectByClass(/*# string */ $classname)
359
    {
360
        // mapping exists
361
        if ($this->getResolver()->hasMapping($classname)) {
362
            $classname = $this->getResolver()->getMapping($classname);
363
            if (is_object($classname)) {
364
                return $classname;
365
            }
366
        }
367
        return $this->get($classname);
0 ignored issues
show
Bug introduced by
It seems like get() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
368
    }
369
370
    /**
371
     * Is $var an object with '__invoke()' defined
372
     *
373
     * @param  mixed $var
374
     * @return bool
375
     * @access protected
376
     */
377
    protected function isInvocable($var)/*# : bool */
378
    {
379
        return is_object($var) && method_exists($var, '__invoke');
380
    }
381
382
    /**
383
     * Things to do after object created.
384
     *
385
     * @param  object $object
386
     * @param  array $definition service definition for $object
387
     * @access protected
388
     */
389
    protected function afterCreation($object, array $definition)
390
    {
391
        // execute methods from this object
392
        if (isset($definition['methods'])) {
393
            foreach ($definition['methods'] as $method) {
394
                $this->executeMethod($method, $object);
395
            }
396
        }
397
398
        // execute common methods in 'di.common' for all created objects
399
        if ($this->getResolver()->has('', 'common')) {
400
            $methods = $this->mergeNodeInfo($this->getResolver()->get('', 'common'));
401
            $this->executeTester($object, $methods);
402
        }
403
    }
404
405
    /**
406
     * Execute [tester, todo] pairs, both use $object as argument
407
     *
408
     * signatures
409
     *
410
     * - tester: function($object) { return $object instance of XXXX; }
411
     * - todoer: function($object, $container) { }
412
     *
413
     * @param  object $object
414
     * @param  array $methods
415
     * @access protected
416
     */
417
    protected function executeTester($object, array $methods)
418
    {
419
        foreach ($methods as $method) {
420
            // tester: $method[0]
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% 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...
421
            if ($method[0]($object)) {
422
                // todoer: $method[1]
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% 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...
423
                $method[1]($object, $this);
424
            }
425
        }
426
    }
427
428
    /**
429
     * Merge data in the node, normally merge methods
430
     *
431
     * @param  array $nodeData
432
     * @return array
433
     * @access protected
434
     */
435
    protected function mergeNodeInfo(array $nodeData)/*# : array */
436
    {
437
        // no merge
438
        if (isset($nodeData[0])) {
439
            return $nodeData;
440
        }
441
442
        // in sections
443
        $result = [];
444
        foreach ($nodeData as $data) {
445
            $result = array_merge($result, $data);
446
        }
447
        return $result;
448
    }
449
450
    /**
451
     * Append '#' to rawId, representing a service object id
452
     *
453
     * @param  string $rawId
454
     * @return string
455
     * @access protected
456
     */
457
    protected function getServiceId(/*# string */ $rawId)/*# : string */
458
    {
459
        return '#' . $rawId;
460
    }
461
}
462