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

DefinitionsParser   B

Complexity

Total Complexity 42

Size/Duplication

Total Lines 352
Duplicated Lines 27.84 %

Coupling/Cohesion

Components 1
Dependencies 8

Test Coverage

Coverage 100%

Importance

Changes 5
Bugs 2 Features 3
Metric Value
wmc 42
c 5
b 2
f 3
lcom 1
cbo 8
dl 98
loc 352
ccs 133
cts 133
cp 1
rs 8.295

8 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 2
A parse() 0 19 4
B parseDefinition() 10 55 9
B getClass() 10 24 3
C getTags() 9 60 9
B getAutowiringTypes() 9 33 5
B parseFactory() 40 51 5
B parseDecoration() 20 28 5

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like DefinitionsParser often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use DefinitionsParser, and based on these observations, apply Extract Interface, too.

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