1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
declare (strict_types = 1); |
4
|
|
|
|
5
|
|
|
namespace TheCodingMachine\Yaco\ServiceProvider; |
6
|
|
|
|
7
|
|
|
use Interop\Container\Definition\DefinitionInterface; |
8
|
|
|
use Interop\Container\ServiceProviderInterface; |
9
|
|
|
use TheCodingMachine\ServiceProvider\Registry; |
10
|
|
|
use TheCodingMachine\Yaco\Compiler; |
11
|
|
|
use TheCodingMachine\Yaco\CompilerException; |
12
|
|
|
use TheCodingMachine\Yaco\Definition\AliasDefinition; |
13
|
|
|
use TheCodingMachine\Yaco\Definition\DumpableInterface; |
14
|
|
|
use TheCodingMachine\Yaco\Definition\FactoryCallDefinition; |
15
|
|
|
use TheCodingMachine\Yaco\Definition\Reference; |
16
|
|
|
use TheCodingMachine\Yaco\DefinitionConverterInterface; |
17
|
|
|
|
18
|
|
|
class ServiceProviderLoader |
19
|
|
|
{ |
20
|
|
|
/** |
21
|
|
|
* @var Compiler |
22
|
|
|
*/ |
23
|
|
|
private $compiler; |
24
|
|
|
|
25
|
|
|
/** |
26
|
|
|
* @var DefinitionConverterInterface |
27
|
|
|
*/ |
28
|
|
|
private $converter; |
29
|
|
|
|
30
|
|
|
/** |
31
|
|
|
* @param Compiler $compiler |
32
|
|
|
*/ |
33
|
|
|
public function __construct(Compiler $compiler, DefinitionConverterInterface $converter) |
34
|
|
|
{ |
35
|
|
|
$this->compiler = $compiler; |
36
|
|
|
$this->converter = $converter; |
37
|
|
|
} |
38
|
|
|
|
39
|
|
|
/** |
40
|
|
|
* Loads the registry into the container. |
41
|
|
|
* |
42
|
|
|
* @param Registry $registry |
43
|
|
|
*/ |
44
|
|
|
public function loadFromRegistry(Registry $registry) |
45
|
|
|
{ |
46
|
|
|
foreach ($registry as $key => $serviceProvider) { |
47
|
|
|
$this->loadServiceProvider($serviceProvider, $key); |
48
|
|
|
} |
49
|
|
|
} |
50
|
|
|
|
51
|
|
|
/** |
52
|
|
|
* @param ServiceProviderInterface $serviceProvider |
53
|
|
|
* @param int $serviceProviderKey |
54
|
|
|
*/ |
55
|
|
|
private function loadServiceProvider(ServiceProviderInterface $serviceProvider, $serviceProviderKey) |
56
|
|
|
{ |
57
|
|
|
$serviceFactories = $serviceProvider->getFactories(); |
58
|
|
|
|
59
|
|
|
foreach ($serviceFactories as $serviceName => $callable) { |
60
|
|
|
$this->registerService($serviceName, $serviceProviderKey, $callable); |
61
|
|
|
} |
62
|
|
|
|
63
|
|
|
$serviceExtensions = $serviceProvider->getExtensions(); |
64
|
|
|
|
65
|
|
|
foreach ($serviceExtensions as $serviceName => $callable) { |
66
|
|
|
$this->extendService($serviceName, $serviceProviderKey, $callable); |
67
|
|
|
} |
68
|
|
|
} |
69
|
|
|
|
70
|
|
|
/** |
71
|
|
|
* @param string $serviceName |
72
|
|
|
* @param int $serviceProviderKey |
73
|
|
|
* @param callable $callable |
74
|
|
|
* |
75
|
|
|
*/ |
76
|
|
|
private function registerService(string $serviceName, int $serviceProviderKey, callable $callable) |
77
|
|
|
{ |
78
|
|
|
$definition = $this->getCreateServiceDefinitionFromCallable($serviceName, $serviceName, $serviceProviderKey, $callable, new ContainerDefinition()); |
79
|
|
|
|
80
|
|
|
$this->compiler->addDumpableDefinition($definition); |
81
|
|
|
} |
82
|
|
|
|
83
|
|
|
/** |
84
|
|
|
* @param string $serviceName |
85
|
|
|
* @param int $serviceProviderKey |
86
|
|
|
* @param callable $callable |
87
|
|
|
* |
88
|
|
|
* @throws CompilerException |
89
|
|
|
*/ |
90
|
|
|
private function extendService(string $serviceName, int $serviceProviderKey, callable $callable) |
91
|
|
|
{ |
92
|
|
|
// TODO: check if $callable as a nullable previous argument! |
93
|
|
|
|
94
|
|
|
if (!$this->compiler->has($serviceName)) { |
|
|
|
|
95
|
|
|
// TODO: if $callable as NOT a nullable previous argument, throw an exception. |
96
|
|
|
} |
97
|
|
|
|
98
|
|
|
// The new service will be created under the name 'xxx_decorated_y' |
99
|
|
|
// The old service will be moved to the name 'xxx_decorated_y.inner' |
100
|
|
|
// This old service will be accessible through a callback represented by 'xxx_decorated_y.callbackwrapper' |
101
|
|
|
// The $servicename becomes an alias pointing to 'xxx_decorated_y' |
102
|
|
|
|
103
|
|
|
$previousDefinition = $this->compiler->getDumpableDefinition($serviceName); |
104
|
|
|
/*while ($previousDefinition instanceof Reference) { |
|
|
|
|
105
|
|
|
$previousDefinition = $this->compiler->getDumpableDefinition($previousDefinition->getAlias()); |
106
|
|
|
}*/ |
107
|
|
|
|
108
|
|
|
while ($previousDefinition instanceof AliasDefinition) { |
109
|
|
|
$previousDefinition = $this->compiler->getDumpableDefinition($previousDefinition->getAlias()); |
110
|
|
|
} |
111
|
|
|
|
112
|
|
|
$oldServiceName = $serviceName; |
113
|
|
|
$decoratedServiceName = $this->getDecoratedServiceName($serviceName); |
114
|
|
|
//$innerName = $decoratedServiceName.'.inner'; |
|
|
|
|
115
|
|
|
//$callbackWrapperName = $decoratedServiceName.'.callbackwrapper'; |
|
|
|
|
116
|
|
|
|
117
|
|
|
// TODO: it would be way easier if we could simply rename a definition!!! |
118
|
|
|
if ($previousDefinition instanceof FactoryCallDefinition) { |
119
|
|
|
$innerDefinition = new FactoryCallDefinition(null /*$innerName*/, $previousDefinition->getFactory(), $previousDefinition->getMethodName(), $previousDefinition->getMethodArguments()); |
|
|
|
|
120
|
|
|
} elseif ($previousDefinition instanceof CreateServiceFromRegistryDefinition || $previousDefinition instanceof ExtendServiceFromRegistryDefinition) { |
121
|
|
|
$innerDefinition = $previousDefinition; |
122
|
|
|
} else { |
123
|
|
|
// @codeCoverageIgnoreStart |
124
|
|
|
throw new CompilerException('Unable to rename definition from class '.get_class($previousDefinition)); |
125
|
|
|
// @codeCoverageIgnoreEnd |
126
|
|
|
} |
127
|
|
|
|
128
|
|
|
$definition = $this->getExtendServiceDefinitionFromCallable($decoratedServiceName, $serviceName, $serviceProviderKey, $callable, new ContainerDefinition(), $innerDefinition); |
129
|
|
|
|
130
|
|
|
$this->compiler->addDumpableDefinition($definition); |
131
|
|
|
//$this->compiler->addDumpableDefinition($innerDefinition); |
|
|
|
|
132
|
|
|
//$this->compiler->addDumpableDefinition($callbackWrapperDefinition); |
|
|
|
|
133
|
|
|
$this->compiler->addDumpableDefinition(new AliasDefinition($oldServiceName, $decoratedServiceName)); |
134
|
|
|
} |
135
|
|
|
|
136
|
|
|
/** |
137
|
|
|
* @param $serviceName |
138
|
|
|
* @param int $serviceProviderKey |
139
|
|
|
* @param callable $callable |
140
|
|
|
* @param ContainerDefinition $containerDefinition |
141
|
|
|
* |
142
|
|
|
* @return DumpableInterface |
143
|
|
|
*/ |
144
|
|
|
private function getCreateServiceDefinitionFromCallable($decoratedServiceName, $serviceName, $serviceProviderKey, callable $callable, ContainerDefinition $containerDefinition): DumpableInterface |
145
|
|
|
{ |
146
|
|
|
// FIXME: we must split this method in 2. One for the factories and one for the extensions! |
147
|
|
|
|
148
|
|
|
if ($callable instanceof DefinitionInterface) { |
149
|
|
|
return $this->converter->convert($decoratedServiceName, $callable); |
150
|
|
|
} |
151
|
|
View Code Duplication |
if (is_array($callable) && is_string($callable[0])) { |
|
|
|
|
152
|
|
|
return new FactoryCallDefinition($decoratedServiceName, $callable[0], $callable[1], [$containerDefinition]); |
|
|
|
|
153
|
|
|
} elseif (is_string($callable) && strpos($callable, '::') !== false) { |
154
|
|
|
$pos = strpos($callable, '::'); |
155
|
|
|
$className = substr($callable, 0, $pos); |
156
|
|
|
$methodName = substr($callable, $pos + 2); |
157
|
|
|
|
158
|
|
|
return new FactoryCallDefinition($decoratedServiceName, $className, $methodName, [$containerDefinition]); |
|
|
|
|
159
|
|
|
} |
160
|
|
|
|
161
|
|
|
// This is an object or a callback... we need to call the getServices method of the service provider at runtime. |
162
|
|
|
return new CreateServiceFromRegistryDefinition($decoratedServiceName, $serviceName, $serviceProviderKey); |
163
|
|
|
} |
164
|
|
|
|
165
|
|
|
/** |
166
|
|
|
* @param $serviceName |
167
|
|
|
* @param int $serviceProviderKey |
168
|
|
|
* @param callable $callable |
169
|
|
|
* @param ContainerDefinition $containerDefinition |
170
|
|
|
* @param CallbackWrapperDefinition|null $previousDefinition |
171
|
|
|
* |
172
|
|
|
* @return DumpableInterface |
173
|
|
|
*/ |
174
|
|
|
private function getExtendServiceDefinitionFromCallable($decoratedServiceName, $serviceName, $serviceProviderKey, callable $callable, ContainerDefinition $containerDefinition, DumpableInterface $previousDefinition = null) |
175
|
|
|
{ |
176
|
|
|
// FIXME: we must split this method in 2. One for the factories and one for the extensions! |
177
|
|
|
|
178
|
|
|
if ($callable instanceof DefinitionInterface) { |
179
|
|
|
return $this->converter->convert($decoratedServiceName, $callable); |
180
|
|
|
} |
181
|
|
View Code Duplication |
if (is_array($callable) && is_string($callable[0])) { |
|
|
|
|
182
|
|
|
return new FactoryCallDefinition($decoratedServiceName, $callable[0], $callable[1], [$containerDefinition, $previousDefinition]); |
|
|
|
|
183
|
|
|
} elseif (is_string($callable) && strpos($callable, '::') !== false) { |
184
|
|
|
$pos = strpos($callable, '::'); |
185
|
|
|
$className = substr($callable, 0, $pos); |
186
|
|
|
$methodName = substr($callable, $pos + 2); |
187
|
|
|
|
188
|
|
|
return new FactoryCallDefinition($decoratedServiceName, $className, $methodName, [$containerDefinition, $previousDefinition]); |
|
|
|
|
189
|
|
|
} |
190
|
|
|
|
191
|
|
|
// This is an object or a callback... we need to call the getServices method of the service provider at runtime. |
192
|
|
|
return new ExtendServiceFromRegistryDefinition($decoratedServiceName, $serviceName, $serviceProviderKey, $previousDefinition); |
193
|
|
|
} |
194
|
|
|
|
195
|
|
|
/** |
196
|
|
|
* @param string $serviceName |
197
|
|
|
* |
198
|
|
|
* @return string |
199
|
|
|
*/ |
200
|
|
|
private function getDecoratedServiceName($serviceName) |
201
|
|
|
{ |
202
|
|
|
$counter = 1; |
203
|
|
|
while ($this->compiler->has($serviceName.'_decorated_'.$counter)) { |
204
|
|
|
++$counter; |
205
|
|
|
} |
206
|
|
|
|
207
|
|
|
return $serviceName.'_decorated_'.$counter; |
208
|
|
|
} |
209
|
|
|
} |
210
|
|
|
|
This check looks for the bodies of
if
statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.These
if
bodies can be removed. If you have an empty if but statements in theelse
branch, consider inverting the condition.could be turned into
This is much more concise to read.