Completed
Push — master ( b31906...e560b1 )
by Théo
13:58
created

DefinitionsParser::parseDecoration()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 28
Code Lines 18

Duplication

Lines 20
Ratio 71.43 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 20
loc 28
ccs 0
cts 0
cp 0
rs 8.439
cc 5
eloc 18
nc 5
nop 3
crap 30
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\Decoration;
17
use Fidry\LaravelYaml\DependencyInjection\Definition\DecorationInterface;
18
use Fidry\LaravelYaml\DependencyInjection\Definition\FactoryInterface;
19
use Fidry\LaravelYaml\DependencyInjection\Definition\Factory;
20
use Fidry\LaravelYaml\DependencyInjection\Definition\Service;
21
use Fidry\LaravelYaml\DependencyInjection\Definition\ServiceInterface;
22
use Fidry\LaravelYaml\Exception\FileLoader\InvalidArgumentException;
23
use Fidry\LaravelYaml\FileLoader\Parser\Resolver\ResolverInterface;
24
use Fidry\LaravelYaml\FileLoader\Parser\Resolver\ServiceResolver;
25
26
/**
27
 * @author Théo FIDRY <[email protected]>
28
 */
29
final class DefinitionsParser
30
{
31 63
    /**
32
     * @var ResolverInterface
33 63
     */
34 63
    private $serviceResolver;
35
36
    public function __construct(ResolverInterface $serviceResolver = null)
37
    {
38
        $this->serviceResolver = (null === $serviceResolver)? new ServiceResolver(): $serviceResolver;
39
    }
40
41
    /**
42
     * Parses service definitions and register them to the container.
43
     *
44
     * @param ContainerBuilder $container
45 63
     * @param array            $content  YAML file content
46
     * @param string           $fileName file name
47 63
     *
48 12
     * @throws InvalidArgumentException
49
     */
50
    public function parse(ContainerBuilder $container, $content, $fileName)
51 51
    {
52 6
        if (!isset($content['services'])) {
53 4
            return;
54 6
        }
55
56 4
        if (!is_array($content['services'])) {
57 4
            throw new InvalidArgumentException(
58
                sprintf(
59
                    'The "services" key should contain an array in %s. Check your YAML syntax.',
60 45
                    $fileName
61 45
                )
62 10
            );
63 15
        }
64
65
        foreach ($content['services'] as $id => $service) {
66
            $this->parseDefinition($container, strtolower($id), $service, $fileName);
67
        }
68
    }
69
70
    /**
71
     * Parses a service definition and register it to the container.
72
     *
73
     * @param ContainerBuilder $container
74
     * @param string           $id
75 45
     * @param array|string     $service
76
     * @param string           $fileName file name
77 45
     *
78 6
     * @throws InvalidArgumentException
79 6
     */
80
    private function parseDefinition(ContainerBuilder $container, $id, $service, $fileName)
81 6
    {
82
        if (is_string($service) && 0 === strpos($service, '@')) {
83
            $alias = new Alias($id, substr($service, 1));
84 42
            $container->addAlias($alias);
85 3
86 2
            return;
87 3
        }
88 2
89 2 View Code Duplication
        if (false === is_array($service)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
90
            throw new InvalidArgumentException(
91 2
                sprintf(
92 2
                    'A service definition must be an array or a string starting with "@" but %s found for service "%s" in %s. Check your YAML syntax.',
93
                    gettype($service),
94
                    $id,
95 39
                    $fileName
96 12
                )
97 12
            );
98 3
        }
99 2
100 3
        if (isset($service['alias'])) {
101 2
            $aliasName = $service['alias'];
102 2
            if (false === is_string($aliasName)) {
103
                throw new InvalidArgumentException(
104 2
                    sprintf(
105 2
                        'Parameter "alias" must be a plain name for service "%s", but found "%s" instead in %s. Check your YAML syntax.',
106
                        $id,
107
                        gettype($aliasName),
108 9
                        $fileName
109 9
                    )
110 6
                );
111
            }
112 36
113 30
            $alias = new Alias($aliasName, $id);
114 21
            $container->addAlias($alias);
115 30
        }
116 20
117 30
        $class = $this->getClass($id, $service, $fileName);
118 18
        $arguments = (isset($service['arguments']))
119
            ? $this->serviceResolver->resolve($service['arguments'])
120 12
            : []
121 12
        ;
122 12
        $tags = $this->getTags($id, $service, $fileName);
123
        $autowiringTypes = $this->getAutowiringTypes($id, $service, $fileName);
124
125
        $serviceDefinition = new Service($id, $class, $arguments, $autowiringTypes, $tags);
126
127
        if (isset($service['factory'])) {
128
            $serviceDefinition = $this->parseFactory($serviceDefinition, $service['factory'], $fileName);
129
        } elseif (isset($service['decorates'])) {
130
            $serviceDefinition = $this->parseDecoration($serviceDefinition, $service, $fileName);
131
        }
132 36
133
        $container->addService($serviceDefinition);
134 36
    }
135 3
136 2
    /**
137 3
     * @param string $id
138 2
     * @param array  $service
139
     * @param string $fileName
140 2
     *
141 2
     * @return string
142
     * @throws InvalidArgumentException
143 33
     */
144 3
    private function getClass($id, $service, $fileName)
145 2
    {
146 3
        if (false === isset($service['class'])) {
147 2
            throw new InvalidArgumentException(
148 3
                sprintf(
149
                    'Parameter "class" missing for service "%s" in %s. Check your YAML syntax.',
150 2
                    $id,
151 2
                    $fileName
152
                )
153
            );
154 30
        }
155 View Code Duplication
        if (false === is_string($service['class'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
156
            throw new InvalidArgumentException(
157
                sprintf(
158
                    'Parameter "class" must be a FQCN for service "%s", but found "%s" instead in %s. Check your YAML syntax.',
159
                    $id,
160
                    gettype($service['class']),
161
                    $fileName
162
                )
163
            );
164
        }
165 30
166
        return $service['class'];
167 30
    }
168 15
169
    /**
170
     * @param string $id
171 15
     * @param array  $service
172 15
     * @param string $fileName
173 3
     *
174 2
     * @return array
175 3
     * @throws InvalidArgumentException
176 2
     */
177
    private function getTags($id, $service, $fileName)
178 2
    {
179 2
        if (false === isset($service['tags'])) {
180
            return [];
181
        }
182 12
183 12
        $tags = [];
184 3 View Code Duplication
        if (false === is_array($service['tags'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
185 2
            throw new InvalidArgumentException(
186 3
                sprintf(
187 2
                    'Parameter "tags" must be an array for service "%s" in %s. Check your YAML syntax.',
188
                    $id,
189 2
                    $fileName
190 2
                )
191
            );
192
        }
193 9
194 3
        foreach ($service['tags'] as $tag) {
195 2
            if (false === is_array($tag)) {
196 3
                throw new InvalidArgumentException(
197 2
                    sprintf(
198
                        'A "tags" entry must be an array for service "%s" in %s. Check your YAML syntax.',
199 2
                        $id,
200 2
                        $fileName
201
                    )
202
                );
203 6
            }
204 6
205
            if (false === isset($tag['name'])) {
206 6
                throw new InvalidArgumentException(
207 3
                    sprintf(
208 3
                        'A "tags" entry is missing a "name" key for service "%s" in %s.',
209 2
                        $id,
210 3
                        $fileName
211 2
                    )
212 2
                );
213 2
            }
214
215 2
            $name = strtolower($tag['name']);
216 2
            unset($tag['name']);
217
218 2
            foreach ($tag as $attribute => $value) {
219
                if (false === is_scalar($value) && null !== $value) {
220 3
                    throw new InvalidArgumentException(
221 2
                        sprintf(
222
                            'A "tags" attribute must be of a scalar-type for service "%s", tag "%s", attribute "%s" in %s. Check your YAML syntax.',
223 3
                            $id,
224
                            $name,
225
                            $attribute,
226
                            $fileName
227
                        )
228
                    );
229
                }
230
            }
231
232
            $tags[$name] = $tag;
233
        }
234 18
235
        return $tags;
236 18
    }
237 9
238
    /**
239
     * @param string $id
240 9
     * @param array  $service
241 3
     * @param string $fileName
242 2
     *
243 3
     * @return array
244 2
     * @throws InvalidArgumentException
245
     */
246 2
    private function getAutowiringTypes($id, $service, $fileName)
247 2
    {
248
        if (false === isset($service['autowiringTypes'])) {
249
            return [];
250 6
        }
251 6
252 6 View Code Duplication
        if (false === is_array($service['autowiringTypes'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
253 3
            throw new InvalidArgumentException(
254 2
                sprintf(
255 3
                    'Parameter "autowiringTypes" must be an array for service "%s" in %s. Check your YAML syntax.',
256 2
                    $id,
257
                    $fileName
258 2
                )
259 2
            );
260
        }
261
262 3
        $autowiringTypes = [];
263 2
        foreach ($service['autowiringTypes'] as $autowiringType) {
264
            if (false === is_string($autowiringType)) {
265 3
                throw new InvalidArgumentException(
266
                    sprintf(
267
                        'A "autowiringType" entry must be a FQCN for service "%s" in %s. Check your YAML syntax.',
268
                        $id,
269
                        $fileName
270
                    )
271
                );
272
            }
273
274
            $autowiringTypes[$autowiringType] = true;
275
        }
276
277
        return array_keys($autowiringTypes);
278
    }
279
280
    /**
281
     * Parses a factory service definition and return the factory object.
282
     *
283
     * @param ServiceInterface $service
284
     * @param mixed            $factory
285
     * @param string           $fileName file name
286
     *
287
     * @return FactoryInterface
288
     * @throws InvalidArgumentException
289
     */
290
    private function parseFactory(ServiceInterface $service, $factory, $fileName)
291
    {
292 View Code Duplication
        if (false === is_array($factory)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
293
            throw new InvalidArgumentException(
294
                sprintf(
295
                    'Parameter "factory" must be an array for service "%s", but found "%s" instead in %s. Check your YAML syntax.',
296
                    $service->getName(),
297
                    gettype($factory),
298
                    $fileName
299
                )
300
            );
301
        }
302
303 View Code Duplication
        if (2 !== count($factory)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
304
            throw new InvalidArgumentException(
305
                sprintf(
306
                    'Parameter "factory" expects two parameters for service "%s", but found "%s" instead in %s. Check your YAML syntax.',
307
                    $service->getName(),
308
                    gettype($factory),
309
                    $fileName
310
                )
311
            );
312
        }
313
314
        $factoryClass = $factory[0];
315
        $factoryMethod = $factory[1];
316 View Code Duplication
        if (false === is_string($factoryClass)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
317
            throw new InvalidArgumentException(
318
                sprintf(
319
                    '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.',
320
                    $service->getName(),
321
                    gettype($factoryClass),
322
                    $fileName
323
                )
324
            );
325
        }
326
        $factoryClass = $this->serviceResolver->resolve($factoryClass);
327
328 View Code Duplication
        if (false === is_string($factoryMethod)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
329
            throw new InvalidArgumentException(
330
                sprintf(
331
                    '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.',
332
                    $service->getName(),
333
                    gettype($factoryMethod),
334
                    $fileName
335
                )
336
            );
337
        }
338
339
       return new Factory($service, $factoryClass, $factoryMethod);
340
    }
341
342
    /**
343
     * Parses a factory service definition and return the factory object.
344
     *
345
     * @param ServiceInterface $service
346
     * @param mixed            $decoration
347
     * @param string           $fileName file name
348
     *
349
     * @return DecorationInterface
350
     * @throws InvalidArgumentException
351
     */
352
    private function parseDecoration(ServiceInterface $service, $decoration, $fileName)
353
    {
354 View Code Duplication
        if (false === is_string($decoration['decorates'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
355
            throw new InvalidArgumentException(
356
                sprintf(
357
                    'Parameter "decorates" for service "%s" must be the id of another service, but found "%s" instead in %s. Check your YAML syntax.',
358
                    $service->getName(),
359
                    gettype($decoration['decorates']),
360
                    $fileName
361
                )
362
            );
363
        }
364
        $decorates = $decoration['decorates'];
365
366
        $decorationInnerName = (isset($decoration['decoration_inner_name'])) ? $decoration['decoration_inner_name'] : null;
367 View Code Duplication
        if (null !== $decorationInnerName && false === is_string($decorationInnerName)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
368
            throw new InvalidArgumentException(
369
                sprintf(
370
                    'Parameter "decoration_inner_name" for service "%s" must be a string if is set, but found "%s" instead in %s. Check your YAML syntax.',
371
                    $service->getName(),
372
                    gettype($decorationInnerName),
373
                    $fileName
374
                )
375
            );
376
        }
377
378
        return new Decoration($service, $decorates, $decorationInnerName);
379
    }
380
}
381