1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/* |
4
|
|
|
* This file is part of the LaravelYaml package. |
5
|
|
|
* |
6
|
|
|
* (c) Théo FIDRY <[email protected]> |
7
|
|
|
* |
8
|
|
|
* For the full copyright and license information, please view the LICENSE |
9
|
|
|
* file that was distributed with this source code. |
10
|
|
|
*/ |
11
|
|
|
|
12
|
|
|
namespace Fidry\LaravelYaml\FileLoader\Parser\Yaml; |
13
|
|
|
|
14
|
|
|
use Fidry\LaravelYaml\DependencyInjection\Builder\ContainerBuilder; |
15
|
|
|
use Fidry\LaravelYaml\DependencyInjection\Definition\Alias; |
16
|
|
|
use Fidry\LaravelYaml\DependencyInjection\Definition\FactoryInterface; |
17
|
|
|
use Fidry\LaravelYaml\DependencyInjection\Definition\FactoryService; |
18
|
|
|
use Fidry\LaravelYaml\DependencyInjection\Definition\Service; |
19
|
|
|
use Fidry\LaravelYaml\DependencyInjection\Definition\ServiceInterface; |
20
|
|
|
use Fidry\LaravelYaml\Exception\FileLoader\InvalidArgumentException; |
21
|
|
|
use Fidry\LaravelYaml\FileLoader\Parser\Resolver\ResolverInterface; |
22
|
|
|
use Fidry\LaravelYaml\FileLoader\Parser\Resolver\ServiceResolver; |
23
|
|
|
|
24
|
|
|
/** |
25
|
|
|
* @author Théo FIDRY <[email protected]> |
26
|
|
|
*/ |
27
|
|
|
final class DefinitionsParser |
28
|
|
|
{ |
29
|
|
|
/** |
30
|
|
|
* @var ResolverInterface |
31
|
63 |
|
*/ |
32
|
|
|
private $serviceResolver; |
33
|
63 |
|
|
34
|
63 |
|
public function __construct(ResolverInterface $serviceResolver = null) |
35
|
|
|
{ |
36
|
|
|
$this->serviceResolver = (null === $serviceResolver)? new ServiceResolver(): $serviceResolver; |
37
|
|
|
} |
38
|
|
|
|
39
|
|
|
/** |
40
|
|
|
* Parses service definitions and register them to the container. |
41
|
|
|
* |
42
|
|
|
* @param ContainerBuilder $container |
43
|
|
|
* @param array $content YAML file content |
44
|
|
|
* @param string $fileName file name |
45
|
63 |
|
* |
46
|
|
|
* @throws InvalidArgumentException |
47
|
63 |
|
*/ |
48
|
12 |
|
public function parse(ContainerBuilder $container, $content, $fileName) |
49
|
|
|
{ |
50
|
|
|
if (!isset($content['services'])) { |
51
|
51 |
|
return; |
52
|
6 |
|
} |
53
|
4 |
|
|
54
|
6 |
|
if (!is_array($content['services'])) { |
55
|
|
|
throw new InvalidArgumentException( |
56
|
4 |
|
sprintf( |
57
|
4 |
|
'The "services" key should contain an array in %s. Check your YAML syntax.', |
58
|
|
|
$fileName |
59
|
|
|
) |
60
|
45 |
|
); |
61
|
45 |
|
} |
62
|
10 |
|
|
63
|
15 |
|
foreach ($content['services'] as $id => $service) { |
64
|
|
|
$this->parseDefinition($container, strtolower($id), $service, $fileName); |
65
|
|
|
} |
66
|
|
|
} |
67
|
|
|
|
68
|
|
|
/** |
69
|
|
|
* Parses a service definition and register it to the container. |
70
|
|
|
* |
71
|
|
|
* @param ContainerBuilder $container |
72
|
|
|
* @param string $id |
73
|
|
|
* @param array|string $service |
74
|
|
|
* @param string $fileName file name |
75
|
45 |
|
* |
76
|
|
|
* @throws InvalidArgumentException |
77
|
45 |
|
*/ |
78
|
6 |
|
private function parseDefinition(ContainerBuilder $container, $id, $service, $fileName) |
79
|
6 |
|
{ |
80
|
|
|
if (is_string($service) && 0 === strpos($service, '@')) { |
81
|
6 |
|
$alias = new Alias($id, substr($service, 1)); |
82
|
|
|
$container->addAlias($alias); |
83
|
|
|
|
84
|
42 |
|
return; |
85
|
3 |
|
} |
86
|
2 |
|
|
87
|
3 |
View Code Duplication |
if (false === is_array($service)) { |
|
|
|
|
88
|
2 |
|
throw new InvalidArgumentException( |
89
|
2 |
|
sprintf( |
90
|
|
|
'A service definition must be an array or a string starting with "@" but %s found for service "%s" in %s. Check your YAML syntax.', |
91
|
2 |
|
gettype($service), |
92
|
2 |
|
$id, |
93
|
|
|
$fileName |
94
|
|
|
) |
95
|
39 |
|
); |
96
|
12 |
|
} |
97
|
12 |
|
|
98
|
3 |
|
if (isset($service['alias'])) { |
99
|
2 |
|
$aliasName = $service['alias']; |
100
|
3 |
|
if (false === is_string($aliasName)) { |
101
|
2 |
|
throw new InvalidArgumentException( |
102
|
2 |
|
sprintf( |
103
|
|
|
'Parameter "alias" must be a plain name for service "%s", but found "%s" instead in %s. Check your YAML syntax.', |
104
|
2 |
|
$id, |
105
|
2 |
|
gettype($aliasName), |
106
|
|
|
$fileName |
107
|
|
|
) |
108
|
9 |
|
); |
109
|
9 |
|
} |
110
|
6 |
|
|
111
|
|
|
$alias = new Alias($aliasName, $id); |
112
|
36 |
|
$container->addAlias($alias); |
113
|
30 |
|
} |
114
|
21 |
|
|
115
|
30 |
|
$class = $this->getClass($id, $service, $fileName); |
116
|
20 |
|
$arguments = (isset($service['arguments'])) |
117
|
30 |
|
? $this->serviceResolver->resolve($service['arguments']) |
118
|
18 |
|
: [] |
119
|
|
|
; |
120
|
12 |
|
$tags = $this->getTags($id, $service, $fileName); |
121
|
12 |
|
$autowiringTypes = $this->getAutowiringTypes($id, $service, $fileName); |
122
|
12 |
|
|
123
|
|
|
$serviceDefinition = new Service($id, $class, $arguments, $autowiringTypes, $tags); |
124
|
|
|
|
125
|
|
|
if (isset($service['factory'])) { |
126
|
|
|
$serviceDefinition = $this->parseFactory($serviceDefinition, $service['factory'], $fileName); |
127
|
|
|
} |
128
|
|
|
|
129
|
|
|
$container->addService($serviceDefinition); |
130
|
|
|
} |
131
|
|
|
|
132
|
36 |
|
/** |
133
|
|
|
* @param string $id |
134
|
36 |
|
* @param array $service |
135
|
3 |
|
* @param string $fileName |
136
|
2 |
|
* |
137
|
3 |
|
* @return string |
138
|
2 |
|
* @throws InvalidArgumentException |
139
|
|
|
*/ |
140
|
2 |
|
private function getClass($id, $service, $fileName) |
141
|
2 |
|
{ |
142
|
|
|
if (false === isset($service['class'])) { |
143
|
33 |
|
throw new InvalidArgumentException( |
144
|
3 |
|
sprintf( |
145
|
2 |
|
'Parameter "class" missing for service "%s" in %s. Check your YAML syntax.', |
146
|
3 |
|
$id, |
147
|
2 |
|
$fileName |
148
|
3 |
|
) |
149
|
|
|
); |
150
|
2 |
|
} |
151
|
2 |
View Code Duplication |
if (false === is_string($service['class'])) { |
|
|
|
|
152
|
|
|
throw new InvalidArgumentException( |
153
|
|
|
sprintf( |
154
|
30 |
|
'Parameter "class" must be a FQCN for service "%s", but found "%s" instead in %s. Check your YAML syntax.', |
155
|
|
|
$id, |
156
|
|
|
gettype($service['class']), |
157
|
|
|
$fileName |
158
|
|
|
) |
159
|
|
|
); |
160
|
|
|
} |
161
|
|
|
|
162
|
|
|
return $service['class']; |
163
|
|
|
} |
164
|
|
|
|
165
|
30 |
|
/** |
166
|
|
|
* @param string $id |
167
|
30 |
|
* @param array $service |
168
|
15 |
|
* @param string $fileName |
169
|
|
|
* |
170
|
|
|
* @return array |
171
|
15 |
|
* @throws InvalidArgumentException |
172
|
15 |
|
*/ |
173
|
3 |
|
private function getTags($id, $service, $fileName) |
174
|
2 |
|
{ |
175
|
3 |
|
if (false === isset($service['tags'])) { |
176
|
2 |
|
return []; |
177
|
|
|
} |
178
|
2 |
|
|
179
|
2 |
|
$tags = []; |
180
|
|
View Code Duplication |
if (false === is_array($service['tags'])) { |
|
|
|
|
181
|
|
|
throw new InvalidArgumentException( |
182
|
12 |
|
sprintf( |
183
|
12 |
|
'Parameter "tags" must be an array for service "%s" in %s. Check your YAML syntax.', |
184
|
3 |
|
$id, |
185
|
2 |
|
$fileName |
186
|
3 |
|
) |
187
|
2 |
|
); |
188
|
|
|
} |
189
|
2 |
|
|
190
|
2 |
|
foreach ($service['tags'] as $tag) { |
191
|
|
|
if (false === is_array($tag)) { |
192
|
|
|
throw new InvalidArgumentException( |
193
|
9 |
|
sprintf( |
194
|
3 |
|
'A "tags" entry must be an array for service "%s" in %s. Check your YAML syntax.', |
195
|
2 |
|
$id, |
196
|
3 |
|
$fileName |
197
|
2 |
|
) |
198
|
|
|
); |
199
|
2 |
|
} |
200
|
2 |
|
|
201
|
|
|
if (false === isset($tag['name'])) { |
202
|
|
|
throw new InvalidArgumentException( |
203
|
6 |
|
sprintf( |
204
|
6 |
|
'A "tags" entry is missing a "name" key for service "%s" in %s.', |
205
|
|
|
$id, |
206
|
6 |
|
$fileName |
207
|
3 |
|
) |
208
|
3 |
|
); |
209
|
2 |
|
} |
210
|
3 |
|
|
211
|
2 |
|
$name = strtolower($tag['name']); |
212
|
2 |
|
unset($tag['name']); |
213
|
2 |
|
|
214
|
|
|
foreach ($tag as $attribute => $value) { |
215
|
2 |
|
if (false === is_scalar($value) && null !== $value) { |
216
|
2 |
|
throw new InvalidArgumentException( |
217
|
|
|
sprintf( |
218
|
2 |
|
'A "tags" attribute must be of a scalar-type for service "%s", tag "%s", attribute "%s" in %s. Check your YAML syntax.', |
219
|
|
|
$id, |
220
|
3 |
|
$name, |
221
|
2 |
|
$attribute, |
222
|
|
|
$fileName |
223
|
3 |
|
) |
224
|
|
|
); |
225
|
|
|
} |
226
|
|
|
} |
227
|
|
|
|
228
|
|
|
$tags[$name] = $tag; |
229
|
|
|
} |
230
|
|
|
|
231
|
|
|
return $tags; |
232
|
|
|
} |
233
|
|
|
|
234
|
18 |
|
/** |
235
|
|
|
* @param string $id |
236
|
18 |
|
* @param array $service |
237
|
9 |
|
* @param string $fileName |
238
|
|
|
* |
239
|
|
|
* @return array |
240
|
9 |
|
* @throws InvalidArgumentException |
241
|
3 |
|
*/ |
242
|
2 |
|
private function getAutowiringTypes($id, $service, $fileName) |
243
|
3 |
|
{ |
244
|
2 |
|
if (false === isset($service['autowiringTypes'])) { |
245
|
|
|
return []; |
246
|
2 |
|
} |
247
|
2 |
|
|
248
|
|
View Code Duplication |
if (false === is_array($service['autowiringTypes'])) { |
|
|
|
|
249
|
|
|
throw new InvalidArgumentException( |
250
|
6 |
|
sprintf( |
251
|
6 |
|
'Parameter "autowiringTypes" must be an array for service "%s" in %s. Check your YAML syntax.', |
252
|
6 |
|
$id, |
253
|
3 |
|
$fileName |
254
|
2 |
|
) |
255
|
3 |
|
); |
256
|
2 |
|
} |
257
|
|
|
|
258
|
2 |
|
$autowiringTypes = []; |
259
|
2 |
|
foreach ($service['autowiringTypes'] as $autowiringType) { |
260
|
|
|
if (false === is_string($autowiringType)) { |
261
|
|
|
throw new InvalidArgumentException( |
262
|
3 |
|
sprintf( |
263
|
2 |
|
'A "autowiringType" entry must be a FQCN for service "%s" in %s. Check your YAML syntax.', |
264
|
|
|
$id, |
265
|
3 |
|
$fileName |
266
|
|
|
) |
267
|
|
|
); |
268
|
|
|
} |
269
|
|
|
|
270
|
|
|
$autowiringTypes[$autowiringType] = true; |
271
|
|
|
} |
272
|
|
|
|
273
|
|
|
return array_keys($autowiringTypes); |
274
|
|
|
} |
275
|
|
|
|
276
|
|
|
/** |
277
|
|
|
* Parses a factory service definition and return the factory object. |
278
|
|
|
* |
279
|
|
|
* @param ServiceInterface $service |
280
|
|
|
* @param mixed $factory |
281
|
|
|
* @param string $fileName file name |
282
|
|
|
* |
283
|
|
|
* @return FactoryInterface |
284
|
|
|
* @throws InvalidArgumentException |
285
|
|
|
*/ |
286
|
|
|
private function parseFactory( ServiceInterface $service, $factory, $fileName) |
287
|
|
|
{ |
288
|
|
View Code Duplication |
if (false === is_array($factory)) { |
|
|
|
|
289
|
|
|
throw new InvalidArgumentException( |
290
|
|
|
sprintf( |
291
|
|
|
'Parameter "factory" must be an array for service "%s", but found "%s" instead in %s. Check your YAML syntax.', |
292
|
|
|
$service->getName(), |
293
|
|
|
gettype($factory), |
294
|
|
|
$fileName |
295
|
|
|
) |
296
|
|
|
); |
297
|
|
|
} |
298
|
|
|
|
299
|
|
View Code Duplication |
if (2 !== count($factory)) { |
|
|
|
|
300
|
|
|
throw new InvalidArgumentException( |
301
|
|
|
sprintf( |
302
|
|
|
'Parameter "factory" expects two parameters for service "%s", but found "%s" instead in %s. Check your YAML syntax.', |
303
|
|
|
$service->getName(), |
304
|
|
|
gettype($factory), |
305
|
|
|
$fileName |
306
|
|
|
) |
307
|
|
|
); |
308
|
|
|
} |
309
|
|
|
|
310
|
|
|
$factoryClass = $factory[0]; |
311
|
|
|
$factoryMethod = $factory[1]; |
312
|
|
View Code Duplication |
if (false === is_string($factoryClass)) { |
|
|
|
|
313
|
|
|
throw new InvalidArgumentException( |
314
|
|
|
sprintf( |
315
|
|
|
'The first parameter of "factory" for service "%s" must be a class name or a reference to a service, but found "%s" instead in %s. Check your YAML syntax.', |
316
|
|
|
$service->getName(), |
317
|
|
|
gettype($factoryClass), |
318
|
|
|
$fileName |
319
|
|
|
) |
320
|
|
|
); |
321
|
|
|
} |
322
|
|
|
$factoryClass = $this->serviceResolver->resolve($factoryClass); |
323
|
|
|
|
324
|
|
View Code Duplication |
if (false === is_string($factoryMethod)) { |
|
|
|
|
325
|
|
|
throw new InvalidArgumentException( |
326
|
|
|
sprintf( |
327
|
|
|
'The second parameter of "factory" for service "%s" must be a class name or a reference to a service, but found "%s" instead in %s. Check your YAML syntax.', |
328
|
|
|
$service->getName(), |
329
|
|
|
gettype($factoryMethod), |
330
|
|
|
$fileName |
331
|
|
|
) |
332
|
|
|
); |
333
|
|
|
} |
334
|
|
|
|
335
|
|
|
return new FactoryService($service, $factoryClass, $factoryMethod); |
336
|
|
|
} |
337
|
|
|
} |
338
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.