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); |
|
|
|
|
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; |
|
|
|
|
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
|
|
|
|
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.