Completed
Push — master ( 4abd02...ac9184 )
by Hong
57s
created

FactoryTrait   A

Complexity

Total Complexity 19

Size/Duplication

Total Lines 127
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 1

Importance

Changes 0
Metric Value
wmc 19
lcom 1
cbo 1
dl 0
loc 127
rs 10
c 0
b 0
f 0

6 Methods

Rating   Name   Duplication   Size   Complexity  
A fabricate() 0 17 2
A fixDefinition() 0 12 4
A constructObject() 0 15 3
A executeCallable() 0 10 3
A afterConstruct() 0 9 3
A fixMethod() 0 12 4
1
<?php
2
3
/**
4
 * Phoole (PHP7.2+)
5
 *
6
 * @category  Library
7
 * @package   Phoole\Di
8
 * @copyright Copyright (c) 2019 Hong Zhang
9
 */
10
declare(strict_types=1);
11
12
namespace Phoole\Di\Util;
13
14
use Phoole\Di\Exception\LogicException;
15
16
/**
17
 * FactoryTrait
18
 *
19
 * @package Phoole\Di
20
 */
21
trait FactoryTrait
22
{
23
    /**
24
     * fabricate the object using the definition
25
     *
26
     * @param  object|array $definition
27
     * @throws LogicException if something goes wrong
28
     * @return object
29
     */
30
    protected function fabricate($definition): object
31
    {
32
        // fix definition
33
        $def = $this->fixDefinition($definition);
0 ignored issues
show
Bug introduced by
It seems like $definition defined by parameter $definition on line 30 can also be of type object; however, Phoole\Di\Util\FactoryTrait::fixDefinition() does only seem to accept string|array, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
34
35
        // construct it
36
        if (is_string($def['class'])) {
37
            $obj = $this->constructObject($def['class'], $def['args']);
38
        } else {
39
            $obj = $this->executeCallable($def['class'], $def['args']);
40
        }
41
        
42
        // aftermath
43
        $this->afterConstruct($obj, $def);
44
45
        return $obj;
46
    }
47
    
48
    /**
49
     * fix object definition
50
     *
51
     * @param  string|array $definition
52
     * @return array
53
     */
54
    protected function fixDefinition($definition): array
55
    {
56
        if (!is_array($definition) || !isset($definition['class'])) {
57
            $definition = ['class' => $definition];
58
        }
59
60
        if (!isset($definition['args'])) {
61
            $definition['args'] = [];
62
        }
63
64
        return (array) $definition;
65
    }
66
67
    /**
68
     * Instantiate service object
69
     *
70
     * @param  string $class        class name
71
     * @param  array $arguments     constructor arguments
72
     * @throws LogicException       if something goes wrong
73
     * @return object
74
     */
75
    protected function constructObject(string $class, array $arguments): object
76
    {
77
        $reflector = new \ReflectionClass($class);
78
        $constructor = $reflector->getConstructor();
79
80
        try {
81
            if (is_null($constructor)) {
82
                return $reflector->newInstanceWithoutConstructor();
83
            } else {
84
                return $reflector->newInstanceArgs($arguments);
85
            }
86
        } catch (\Throwable $e) {
87
            throw new LogicException($e->getMessage());
88
        }
89
    }
90
91
    /**
92
     * execute callable
93
     *
94
     * @param  callable|object $callable      callable
95
     * @param  array $arguments     constructor arguments
96
     * @throws LogicException       if something goes wrong
97
     * @return mixed
98
     */
99
    protected function executeCallable($callable, array $arguments)
100
    {
101
        if (is_callable($callable)) {
102
            return call_user_func_array($callable, $arguments);
103
        } elseif (is_object($callable)) {
104
            return $callable;
105
        } else {
106
            throw new LogicException((string) $callable . " not a callable");
107
        }
108
    }
109
110
    /**
111
     * processing service aftermath
112
     *
113
     * @param  object $object
114
     * @param  array  $definition
115
     * @return void
116
     */
117
    protected function afterConstruct(object $object, array $definition): void
118
    {
119
        if (isset($definition['after'])) {
120
            foreach ($definition['after'] as $line) {
121
                list($callable, $arguments) = $this->fixMethod($object, (array) $line);
122
                $this->executeCallable($callable, $arguments);
123
            }
124
        }
125
    }
126
127
    /**
128
     * fix the 'after' part of definition
129
     *
130
     * @param  object $object
131
     * @param  array  $line
132
     * @throws LogicException   if goes wrong
133
     * @return array  [Callable, arguments]
134
     */
135
    protected function fixMethod(object $object, array $line): array
136
    {
137
        $callable = null;
0 ignored issues
show
Unused Code introduced by
$callable is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
138
        if (is_string($line[0]) && method_exists($object, $line[0])) {
139
            $callable = [$object, $line[0]];
140
        } elseif (is_callable($line[0])) {
141
            $callable = $line[0];
142
        } else {
143
            throw new LogicException("Bad method definition: $line");
144
        }
145
        return [$callable, (array) ($line[1] ?? [])];
146
    }
147
}
148