Completed
Push — master ( e9d1ee...cb5c09 )
by Théo
06:38
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 6
CRAP Score 13.575

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 20
loc 28
ccs 6
cts 20
cp 0.3
rs 8.439
cc 5
eloc 18
nc 5
nop 3
crap 13.575
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
    /**
32
     * @var ResolverInterface
33
     */
34
    private $serviceResolver;
35
36 69
    public function __construct(ResolverInterface $serviceResolver = null)
37
    {
38 69
        $this->serviceResolver = (null === $serviceResolver)? new ServiceResolver(): $serviceResolver;
39 69
    }
40
41
    /**
42
     * Parses service definitions and register them to the container.
43
     *
44
     * @param ContainerBuilder $container
45
     * @param array            $content  YAML file content
46
     * @param string           $fileName file name
47
     *
48
     * @throws InvalidArgumentException
49
     */
50 69
    public function parse(ContainerBuilder $container, $content, $fileName)
51
    {
52 69
        if (!isset($content['services'])) {
53 12
            return;
54
        }
55
56 57
        if (!is_array($content['services'])) {
57 6
            throw new InvalidArgumentException(
58 2
                sprintf(
59 6
                    'The "services" key should contain an array in %s. Check your YAML syntax.',
60
                    $fileName
61 2
                )
62 2
            );
63
        }
64
65 51
        foreach ($content['services'] as $id => $service) {
66 51
            $this->parseDefinition($container, strtolower($id), $service, $fileName);
67 7
        }
68 21
    }
69
70
    /**
71
     * Parses a service definition and register it to the container.
72
     *
73
     * @param ContainerBuilder $container
74
     * @param string           $id
75
     * @param array|string     $service
76
     * @param string           $fileName file name
77
     *
78
     * @throws InvalidArgumentException
79
     */
80 51
    private function parseDefinition(ContainerBuilder $container, $id, $service, $fileName)
81
    {
82 51
        if (is_string($service) && 0 === strpos($service, '@')) {
83 6
            $alias = new Alias($id, substr($service, 1));
84 6
            $container->addAlias($alias);
85
86 6
            return;
87
        }
88
89 48 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 3
            throw new InvalidArgumentException(
91 1
                sprintf(
92 3
                    '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 1
                    gettype($service),
94 1
                    $id,
95
                    $fileName
96 1
                )
97 1
            );
98
        }
99
100 45
        if (isset($service['alias'])) {
101 12
            $aliasName = $service['alias'];
102 12
            if (false === is_string($aliasName)) {
103 3
                throw new InvalidArgumentException(
104 1
                    sprintf(
105 3
                        'Parameter "alias" must be a plain name for service "%s", but found "%s" instead in %s. Check your YAML syntax.',
106 1
                        $id,
107 1
                        gettype($aliasName),
108
                        $fileName
109 1
                    )
110 1
                );
111
            }
112
113 9
            $alias = new Alias($aliasName, $id);
114 9
            $container->addAlias($alias);
115 3
        }
116
117 42
        $class = $this->getClass($id, $service, $fileName);
118 36
        $arguments = (isset($service['arguments']))
119 14
            ? $this->serviceResolver->resolve($service['arguments'])
120 36
            : []
121 12
        ;
122 36
        $tags = $this->getTags($id, $service, $fileName);
123 24
        $autowiringTypes = $this->getAutowiringTypes($id, $service, $fileName);
124
125 18
        $serviceDefinition = new Service($id, $class, $arguments, $autowiringTypes, $tags);
126
127 18
        if (isset($service['factory'])) {
128 3
            $serviceDefinition = $this->parseFactory($serviceDefinition, $service['factory'], $fileName);
129 16
        } elseif (isset($service['decorates'])) {
130 3
            $serviceDefinition = $this->parseDecoration($serviceDefinition, $service, $fileName);
131 1
        }
132
133 18
        $container->addService($serviceDefinition);
134 18
    }
135
136
    /**
137
     * @param string $id
138
     * @param array  $service
139
     * @param string $fileName
140
     *
141
     * @return string
142
     * @throws InvalidArgumentException
143
     */
144 42
    private function getClass($id, $service, $fileName)
145
    {
146 42
        if (false === isset($service['class'])) {
147 3
            throw new InvalidArgumentException(
148 1
                sprintf(
149 3
                    'Parameter "class" missing for service "%s" in %s. Check your YAML syntax.',
150 1
                    $id,
151
                    $fileName
152 1
                )
153 1
            );
154
        }
155 39 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 3
            throw new InvalidArgumentException(
157 1
                sprintf(
158 3
                    'Parameter "class" must be a FQCN for service "%s", but found "%s" instead in %s. Check your YAML syntax.',
159 1
                    $id,
160 3
                    gettype($service['class']),
161
                    $fileName
162 1
                )
163 1
            );
164
        }
165
166 36
        return $service['class'];
167
    }
168
169
    /**
170
     * @param string $id
171
     * @param array  $service
172
     * @param string $fileName
173
     *
174
     * @return array
175
     * @throws InvalidArgumentException
176
     */
177 36
    private function getTags($id, $service, $fileName)
178
    {
179 36
        if (false === isset($service['tags'])) {
180 21
            return [];
181
        }
182
183 15
        $tags = [];
184 15 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 3
            throw new InvalidArgumentException(
186 1
                sprintf(
187 3
                    'Parameter "tags" must be an array for service "%s" in %s. Check your YAML syntax.',
188 1
                    $id,
189
                    $fileName
190 1
                )
191 1
            );
192
        }
193
194 12
        foreach ($service['tags'] as $tag) {
195 12
            if (false === is_array($tag)) {
196 3
                throw new InvalidArgumentException(
197 1
                    sprintf(
198 3
                        'A "tags" entry must be an array for service "%s" in %s. Check your YAML syntax.',
199 1
                        $id,
200
                        $fileName
201 1
                    )
202 1
                );
203
            }
204
205 9
            if (false === isset($tag['name'])) {
206 3
                throw new InvalidArgumentException(
207 1
                    sprintf(
208 3
                        'A "tags" entry is missing a "name" key for service "%s" in %s.',
209 1
                        $id,
210
                        $fileName
211 1
                    )
212 1
                );
213
            }
214
215 6
            $name = strtolower($tag['name']);
216 6
            unset($tag['name']);
217
218 6
            foreach ($tag as $attribute => $value) {
219 3
                if (false === is_scalar($value) && null !== $value) {
220 3
                    throw new InvalidArgumentException(
221 1
                        sprintf(
222 3
                            'A "tags" attribute must be of a scalar-type for service "%s", tag "%s", attribute "%s" in %s. Check your YAML syntax.',
223 1
                            $id,
224 1
                            $name,
225 1
                            $attribute,
226
                            $fileName
227 1
                        )
228 1
                    );
229
                }
230 1
            }
231
232 3
            $tags[$name] = $tag;
233 1
        }
234
235 3
        return $tags;
236
    }
237
238
    /**
239
     * @param string $id
240
     * @param array  $service
241
     * @param string $fileName
242
     *
243
     * @return array
244
     * @throws InvalidArgumentException
245
     */
246 24
    private function getAutowiringTypes($id, $service, $fileName)
247
    {
248 24
        if (false === isset($service['autowiringTypes'])) {
249 15
            return [];
250
        }
251
252 9 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 1
                sprintf(
255 3
                    'Parameter "autowiringTypes" must be an array for service "%s" in %s. Check your YAML syntax.',
256 1
                    $id,
257
                    $fileName
258 1
                )
259 1
            );
260
        }
261
262 6
        $autowiringTypes = [];
263 6
        foreach ($service['autowiringTypes'] as $autowiringType) {
264 6
            if (false === is_string($autowiringType)) {
265 3
                throw new InvalidArgumentException(
266 1
                    sprintf(
267 3
                        'A "autowiringType" entry must be a FQCN for service "%s" in %s. Check your YAML syntax.',
268 1
                        $id,
269
                        $fileName
270 1
                    )
271 1
                );
272
            }
273
274 3
            $autowiringTypes[$autowiringType] = true;
275 1
        }
276
277 3
        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 3
    private function parseFactory(ServiceInterface $service, $factory, $fileName)
291
    {
292 3 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 3 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 3
        $factoryClass = $factory[0];
315 3
        $factoryMethod = $factory[1];
316 3 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 3
        $factoryClass = $this->serviceResolver->resolve($factoryClass);
327
328 3 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 3
       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 3
    private function parseDecoration(ServiceInterface $service, $decoration, $fileName)
353
    {
354 3 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 3
        $decorates = $decoration['decorates'];
365
366 3
        $decorationInnerName = (isset($decoration['decoration_inner_name'])) ? $decoration['decoration_inner_name'] : null;
367 3 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 3
        return new Decoration($service, $decorates, $decorationInnerName);
379
    }
380
}
381