Completed
Push — master ( c04ba9...19acfd )
by Martin
04:21
created

NeonFileLoader::processArguments()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 19
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 19
rs 9.4285
cc 3
eloc 11
nc 2
nop 4
1
<?php
2
3
/*
4
 * This is part of the symfonette/neon-integration package.
5
 *
6
 * (c) Martin Hasoň <[email protected]>
7
 * (c) Webuni s.r.o. <[email protected]>
8
 *
9
 * For the full copyright and license information, please view the LICENSE
10
 * file that was distributed with this source code.
11
 */
12
13
namespace Symfonette\NeonIntegration\DependencyInjection;
14
15
use Nette\Neon\Entity;
16
use Nette\Neon\Neon;
17
use Symfony\Component\Config\Resource\FileResource;
18
use Symfony\Component\DependencyInjection\Alias;
19
use Symfony\Component\DependencyInjection\ContainerInterface;
20
use Symfony\Component\DependencyInjection\Definition;
21
use Symfony\Component\DependencyInjection\DefinitionDecorator;
22
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
23
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
24
use Symfony\Component\DependencyInjection\Extension\ExtensionInterface;
25
use Symfony\Component\DependencyInjection\Loader\FileLoader;
26
use Symfony\Component\DependencyInjection\Reference;
27
use Symfony\Component\ExpressionLanguage\Expression;
28
29
class NeonFileLoader extends FileLoader
30
{
31
    private static $keywords = [
32
        'alias' => 'alias',
33
        'parent' => 'parent',
34
        'class' => 'class',
35
        'shared' => 'shared',
36
        'synthetic' => 'synthetic',
37
        'lazy' => 'lazy',
38
        'public' => 'public',
39
        'abstract' => 'abstract',
40
        'deprecated' => 'deprecated',
41
        'factory' => 'factory',
42
        'file' => 'file',
43
        'arguments' => 'arguments',
44
        'properties' => 'properties',
45
        'configurator' => 'configurator',
46
        'calls' => 'calls',
47
        'tags' => 'tags',
48
        'decorates' => 'decorates',
49
        'decoration_inner_name' => 'decoration_inner_name',
50
        'decoration_priority' => 'decoration_priority',
51
        'autowire' => 'autowire',
52
        'autowiring_types' => 'autowiring_types',
53
        'setup' => 'setup', // nette
54
    ];
55
56
    private $anonymousServicesCount;
57
58
    /**
59
     * {@inheritdoc}
60
     */
61
    public function supports($resource, $type = null)
62
    {
63
        return is_string($resource) && 'neon' === pathinfo($resource, PATHINFO_EXTENSION) && (!$type || 'neon' === $type);
0 ignored issues
show
Bug Best Practice introduced by
The expression $type of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
64
    }
65
66
    /**
67
     * {@inheritdoc}
68
     */
69
    public function load($resource, $type = null)
70
    {
71
        $path = $this->locator->locate($resource);
72
73
        $content = $this->loadFile($path);
74
75
        $this->container->addResource(new FileResource($path));
1 ignored issue
show
Bug introduced by
It seems like $path defined by $this->locator->locate($resource) on line 71 can also be of type array; however, Symfony\Component\Config...Resource::__construct() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
76
77
        if (null === $content) {
78
            return;
79
        }
80
81
        $this->parseImports($content, $path);
82
83
        if (isset($content['parameters'])) {
84
            if (!is_array($content['parameters'])) {
85
                throw new InvalidArgumentException(sprintf('The "parameters" key should contain an array in %s. Check your NEON syntax.', $resource));
86
            }
87
88
            foreach ($content['parameters'] as $key => $value) {
89
                $this->container->setParameter($key, $this->resolveServices($value, $path));
90
            }
91
        }
92
93
        $this->loadFromExtensions($content);
94
95
        $this->parseDefinitions($content, $resource);
96
    }
97
98
    private function loadFile($file)
99
    {
100
        if (!class_exists('Nette\Neon\Neon')) {
101
            throw new RuntimeException('Unable to load NEON config files as the Nette Neon Component is not installed.');
102
        }
103
104
        if (!stream_is_local($file)) {
105
            throw new InvalidArgumentException(sprintf('This is not a local file "%s".', $file));
106
        }
107
108
        if (!file_exists($file)) {
109
            throw new InvalidArgumentException(sprintf('The service file "%s" is not valid.', $file));
110
        }
111
112
        try {
113
            $configuration = Neon::decode(file_get_contents($file));
114
        } catch (\Exception $e) {
115
            throw new InvalidArgumentException(sprintf('The file "%s" does not contain valid NEON.', $file), 0, $e);
116
        }
117
118
        return $this->validateFile($configuration, $file);
119
    }
120
121
    private function validateFile($content, $file)
122
    {
123
        if (null === $content) {
124
            return $content;
125
        }
126
127
        if (!is_array($content)) {
128
            throw new InvalidArgumentException(sprintf('The service file "%s" is not valid. It should contain an array. Check your NEON syntax.', $file));
129
        }
130
131
        foreach ($content as $namespace => $data) {
132
            if (in_array($namespace, ['imports', 'parameters', 'services'])) {
133
                continue;
134
            }
135
136
            if (!$this->container->hasExtension($namespace)) {
137
                $extensionNamespaces = array_filter(array_map(
138
                    function (ExtensionInterface $ext) {
139
                        return $ext->getAlias();
140
                    },
141
                    $this->container->getExtensions()
142
                ));
143
144
                throw new InvalidArgumentException(sprintf(
145
                    'There is no extension able to load the configuration for "%s" (in %s). Looked for namespace "%s", found %s',
146
                    $namespace,
147
                    $file,
148
                    $namespace,
149
                    $extensionNamespaces ? sprintf('"%s"', implode('", "', $extensionNamespaces)) : 'none'
150
                ));
151
            }
152
        }
153
154
        return $content;
155
    }
156
157
    private function parseImports($content, $file)
158
    {
159
        // nette
160
        if (!isset($content['imports']) && !isset($content['includes'])) {
161
            return;
162
        }
163
164
        // nette
165
        if (isset($content['imports']) && isset($content['includes'])) {
166
            throw new InvalidArgumentException('The "imports" and "includes" keys cannot be used together. Checkyour NEON syntax.', $file);
167
        }
168
169 View Code Duplication
        if (isset($content['imports']) && !is_array($content['imports'])) {
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...
170
            throw new InvalidArgumentException(sprintf('The "imports" key should contain an array in %s. Check your NEON syntax.', $file));
171
        }
172
173
        // nette
174 View Code Duplication
        if (isset($content['includes']) && !is_array($content['includes'])) {
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...
175
            throw new InvalidArgumentException(sprintf('The "includes" key should contain an array in %s. Check your NEON syntax.', $file));
176
        }
177
178
        // nette
179
        $content = array_merge(['imports' => [], 'includes' => []], $content);
180
181
        foreach ($content['imports'] as $import) {
182
            if (!is_array($import)) {
183
                throw new InvalidArgumentException(sprintf('The values in the "imports" key should be arrays in %s. Check your NEON syntax.', $file));
184
            }
185
186
            $this->setCurrentDir(dirname($file));
187
            $this->import($import['resource'], null, isset($import['ignore_errors']) ? (bool) $import['ignore_errors'] : false, $file);
188
        }
189
190
        // nette
191
        foreach ($content['includes'] as $include) {
192
            $this->setCurrentDir(dirname($file));
193
            $this->import($include, null, false, $file);
194
        }
195
    }
196
197
    private function parseDefinitions($content, $file)
198
    {
199
        if (!isset($content['services'])) {
200
            return;
201
        }
202
203
        if (!is_array($content['services'])) {
204
            throw new InvalidArgumentException(sprintf('The "services" key should contain an array in %s. Check your YAML syntax.', $file));
205
        }
206
207
        $this->anonymousServicesCount = 0;
208
        foreach ($content['services'] as $id => $service) {
209
            if (is_int($id)) {
210
                $id = $this->generateAnonymousServiceId($file);
211
            }
212
            $this->parseDefinition($id, $service, $file);
213
        }
214
    }
215
216
    private function parseDefinition($id, $service, $file)
217
    {
218
        if ($this->processService($id, $service, $file)) {
219
            return;
220
        }
221
222
        $this->checkDefinition($id, $service, $file);
223
224
        if ($this->processAlias($id, $service, $file)) {
225
            return;
226
        }
227
228
        $definition = isset($service['parent']) ? new DefinitionDecorator($service['parent']) : new Definition();
229
230
        $this->processClass($id, $service, $definition, $file);
231
232
        if (isset($service['shared'])) {
233
            $definition->setShared($service['shared']);
234
        }
235
236
        if (isset($service['synthetic'])) {
237
            $definition->setSynthetic($service['synthetic']);
238
        }
239
240
        if (isset($service['lazy'])) {
241
            $definition->setLazy($service['lazy']);
242
        }
243
244
        if (isset($service['public'])) {
245
            $definition->setPublic($service['public']);
246
        }
247
248
        if (isset($service['abstract'])) {
249
            $definition->setAbstract($service['abstract']);
250
        }
251
252
        if (array_key_exists('deprecated', $service)) {
253
            $definition->setDeprecated(true, $service['deprecated']);
254
        }
255
256
        $this->processFactory($id, $service, $definition, $file);
257
258
        if (isset($service['file'])) {
259
            $definition->setFile($service['file']);
260
        }
261
262
        $this->processArguments($id, $service, $definition, $file);
263
264
        // nette
265
        $this->processSetup($service);
266
267
        if (isset($service['properties'])) {
268
            $definition->setProperties($this->resolveServices($service['properties'], $file));
269
        }
270
271
        if (isset($service['configurator'])) {
272
            if (is_string($service['configurator'])) {
273
                $definition->setConfigurator($service['configurator']);
274
            } else {
275
                $definition->setConfigurator([$this->resolveServices($service['configurator'][0], $file), $service['configurator'][1]]);
276
            }
277
        }
278
279
        $this->processCalls($id, $service, $definition, $file);
280
        $this->processTags($id, $service, $definition, $file);
281
282
        if (isset($service['decorates'])) {
283
            $renameId = isset($service['decoration_inner_name']) ? $service['decoration_inner_name'] : null;
284
            $priority = isset($service['decoration_priority']) ? $service['decoration_priority'] : 0;
285
            $definition->setDecoratedService($service['decorates'], $renameId, $priority);
286
        }
287
288
        $this->processAutowire($id, $service, $definition, $file);
289
        $this->processAutowiringTypes($id, $service, $definition, $file);
290
291
        $this->container->setDefinition($id, $definition);
292
    }
293
294
    private function processService(&$id, &$service, $file)
295
    {
296
        // nette
297
        if ($service instanceof Entity) {
298
            $value = $service->value;
299
            $service = ['arguments' => $service->attributes];
300
            if (false === strpos($value, ':')) {
301
                $service['class'] = $value;
302
            } else {
303
                $service['factory'] = $value;
304
            }
305
        }
306
307
        // nette
308
        if (preg_match('#^(\S+)\s+<\s+(\S+)\z#', $id, $matches)) {
309 View Code Duplication
            if (isset($service['parent']) && $matches[2] !== $service['parent']) {
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...
310
                throw new InvalidArgumentException(sprintf('Two parent services "%s" and "%s" are defined for service "%s" in "%s". Check your NEON syntax.', $service['parent'], $matches[2], $matches[1], $file));
311
            }
312
313
            $id = $matches[1];
314
            $parent = $matches[2];
315
        }
316
317
        // nette
318
        if (is_string($service) && false !== strpos($service, ':')) {
319
            $service = ['factory' => $this->parseFactory($service, $file)];
320
        } elseif (is_string($service) && 0 === strpos($service, '@')) {
321
            $this->container->setAlias($id, substr($service, 1));
322
323
            return true;
324
        } elseif (is_string($service)) {
325
            $service = ['class' => $service];
326
        }
327
328
        // nette
329
        if (isset($parent)) {
330
            $service['parent'] = $parent;
331
        }
332
    }
333
334
    private function processAlias($id, array &$service, $file)
335
    {
336
        if (!isset($service['alias'])) {
337
            return;
338
        }
339
340
        $public = !array_key_exists('public', $service) || (bool) $service['public'];
341
        $this->container->setAlias($id, new Alias($service['alias'], $public));
342
343
        foreach ($service as $key => $value) {
344
            if (!in_array($key, ['alias', 'public'])) {
345
                throw new InvalidArgumentException(sprintf('The configuration key "%s" is unsupported for alias definition "%s" in "%s". Allowed configuration keys are "alias" and "public".', $key, $id, $file));
346
            }
347
        }
348
349
        return true;
350
    }
351
352 View Code Duplication
    private function processClass($id, &$service, Definition $definition, $file)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
353
    {
354
        if (!isset($service['class'])) {
355
            return;
356
        }
357
358
        $class = $service['class'];
359
360
        // nette
361
        if ($class instanceof Entity) {
362
            if (isset($service['arguments']) && !empty($class->attributes)) {
363
                throw new InvalidArgumentException(sprintf('Duplicated definition of arguments for service "%s" in "%s". Check you NEON syntax.', $id, $file));
364
            }
365
366
            $service['arguments'] = $class->attributes;
367
            $class = $class->value;
368
        }
369
370
        $definition->setClass($class);
371
    }
372
373 View Code Duplication
    private function processFactory($id, array &$service, Definition $definition, $file)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
374
    {
375
        if (!isset($service['factory'])) {
376
            return;
377
        }
378
379
        $factory = $service['factory'];
380
381
        //nette
382
        if ($factory instanceof Entity) {
383
            if (isset($service['arguments']) && !empty($factory->attributes)) {
384
                throw new InvalidArgumentException(sprintf('Duplicated definition of arguments for service "%s" in "%s". Check you NEON syntax.', $id, $file));
385
            }
386
387
            $service['arguments'] = $factory->attributes;
388
            $factory = $factory->value;
389
        }
390
391
        $definition->setFactory($this->parseFactory($factory, $file));
392
    }
393
394
    private function processArguments($id, array &$service, Definition $definition, $file)
0 ignored issues
show
Unused Code introduced by
The parameter $id is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
395
    {
396
        if (!isset($service['arguments'])) {
397
            return;
398
        }
399
400
        $autowired = false;
401
        array_walk($service['arguments'], function (&$value) use (&$autowired) {
402
            if ('...' === $value) {
403
                $value = '';
404
                $autowired = true;
405
            }
406
407
            return $value;
408
        });
409
410
        $definition->setAutowired($autowired);
411
        $definition->setArguments($this->resolveServices($service['arguments'], $file));
412
    }
413
414
    private function processSetup(array &$service)
415
    {
416
        if (!isset($service['setup'])) {
417
            return;
418
        }
419
420
        foreach ($service['setup'] as $setup) {
421
            if ($setup instanceof Entity) {
422
                $name = $setup->value;
423
                $args = $setup->attributes;
424
            } elseif (is_array($setup)) {
425
                $name = $setup[0];
426
                $args = isset($setup[1]) ? $setup[1] : [];
427
            } else {
428
                $name = $setup;
429
                $args = [];
430
            }
431
432
            if ('$' === $name[0]) {
433
                $service['properties'][substr($name, 1)] = $args;
434
            } else {
435
                $service['calls'][] = [$name, $args];
436
            }
437
        }
438
    }
439
440
    private function processCalls($id, array &$service, Definition $definition, $file)
441
    {
442
        if (!isset($service['calls'])) {
443
            return;
444
        }
445
446
        if (!is_array($service['calls'])) {
447
            throw new InvalidArgumentException(sprintf('Parameter "calls" must be an array for service "%s" in %s. Check your NEON syntax.', $id, $file));
448
        }
449
450
        foreach ($service['calls'] as $call) {
451
            if ($call instanceof Entity) { // nette
452
                $method = $call->value;
453
                $args = $this->resolveServices($call->attributes, $file);
454
            } elseif (isset($call['method'])) {
455
                $method = $call['method'];
456
                $args = isset($call['arguments']) ? $this->resolveServices($call['arguments'], $file) : [];
457
            } elseif (is_array($call)) {
458
                $method = $call[0];
459
                $args = isset($call[1]) ? $this->resolveServices($call[1], $file) : [];
460
            } else { // nette
461
                $method = $call;
462
                $args = [];
463
            }
464
465
            $definition->addMethodCall($method, $args);
466
        }
467
    }
468
469
    private function processTags($id, array &$service, Definition $definition, $file)
470
    {
471
        if (!isset($service['tags'])) {
472
            return;
473
        }
474
475
        if (!is_array($service['tags'])) {
476
            throw new InvalidArgumentException(sprintf('Parameter "tags" must be an array for service "%s" in %s. Check your NEON syntax.', $id, $file));
477
        }
478
479
        foreach ($service['tags'] as $tag) {
480
            if ($tag instanceof Entity) {
481
                $tag = ['name' => $tag->value] + $tag->attributes;
482
            } elseif (is_string($tag)) {
483
                $tag = ['name' => $tag];
484
            }
485
486
            if (!is_array($tag)) {
487
                throw new InvalidArgumentException(sprintf('A "tags" entry must be an array for service "%s" in %s. Check your NEON syntax.', $id, $file));
488
            }
489
490
            if (!isset($tag['name'])) {
491
                throw new InvalidArgumentException(sprintf('A "tags" entry is missing a "name" key for service "%s" in %s.', $id, $file));
492
            }
493
494
            if (!is_string($tag['name']) || '' === $tag['name']) {
495
                throw new InvalidArgumentException(sprintf('The tag name for service "%s" in %s must be a non-empty string.', $id, $file));
496
            }
497
498
            $name = $tag['name'];
499
            unset($tag['name']);
500
501
            foreach ($tag as $attribute => $value) {
502
                if (!is_scalar($value) && null !== $value) {
503
                    throw new InvalidArgumentException(sprintf('A "tags" attribute must be of a scalar-type for service "%s", tag "%s", attribute "%s" in %s. Check your NEON syntax.', $id, $name, $attribute, $file));
504
                }
505
            }
506
507
            $definition->addTag($name, $tag);
508
        }
509
    }
510
511
    private function processAutowire($id, array &$service, Definition $definition, $file)
512
    {
513
        // nette
514 View Code Duplication
        if (isset($service['autowired'])) {
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...
515
            if (isset($service['autowire']) && $service['autowire'] !== $service['autowired']) {
516
                throw new InvalidArgumentException(sprintf('Contradictory definition of autowiring for service "%s" in "%s". Check you NEON syntax.', $id, $file));
517
            }
518
519
            $service['autowire'] = $service['autowired'];
520
        }
521
522
        if (isset($service['autowire'])) {
523
            // nette
524
            if ($definition->isAutowired() && !$service['autowire']) {
525
                throw new InvalidArgumentException(sprintf('Contradictory definition of autowiring for service "%s" in "%s". Check you NEON syntax.', $id, $file));
526
            }
527
528
            $definition->setAutowired($service['autowire']);
529
        }
530
    }
531
532
    private function processAutowiringTypes($id, array &$service, Definition $definition, $file)
533
    {
534
        if (!isset($service['autowiring_types'])) {
535
            return;
536
        }
537
538
        if (is_string($service['autowiring_types'])) {
539
            $definition->addAutowiringType($service['autowiring_types']);
540
        } else {
541
            if (!is_array($service['autowiring_types'])) {
542
                throw new InvalidArgumentException(sprintf('Parameter "autowiring_types" must be a string or an array for service "%s" in %s. Check your NEON syntax.', $id, $file));
543
            }
544
545
            foreach ($service['autowiring_types'] as $autowiringType) {
546
                if (!is_string($autowiringType)) {
547
                    throw new InvalidArgumentException(sprintf('A "autowiring_types" attribute must be of type string for service "%s" in %s. Check your NEON syntax.', $id, $file));
548
                }
549
550
                $definition->addAutowiringType($autowiringType);
551
            }
552
        }
553
    }
554
555
    private function parseFactory($factory, $file)
556
    {
557
        if (is_string($factory)) {
558
            if (strpos($factory, '::') !== false) {
559
                $parts = explode('::', $factory, 2);
560
561
                return ['@' === $parts[0][0] ? $this->resolveServices($parts[0], $file) : $parts[0], $parts[1]];
562
            } elseif (strpos($factory, ':') !== false) {
563
                $parts = explode(':', $factory, 2);
564
565
                return [$this->resolveServices(('@' === $parts[0][0] ?: '@').$parts[0], $file), $parts[1]];
566
            } else {
567
                return $factory;
568
            }
569
        } else {
570
            return [$this->resolveServices($factory[0], $file), $factory[1]];
571
        }
572
    }
573
574
    private function resolveServices($value, $file)
575
    {
576
        // nette
577
        if ($value instanceof Entity) {
578
            if ('expression' === $value->value || 'expr' === $value->value) {
579
                return new Expression(reset($value->attributes));
580
            } elseif (0 === strpos($value->value, '@')) {
581
                $value = $value->value;
582
            } else {
583
                $id = $this->generateAnonymousServiceId($file);
584
                $this->parseDefinition($id, $value, $file);
585
                $value = new Reference($id);
586
            }
587
        }
588
589
        if (is_array($value)) {
590
            $value = array_map(function ($value) use ($file) {
591
                return $this->resolveServices($value, $file);
592
            }, $value);
593
        } elseif (is_string($value) &&  0 === strpos($value, '@=')) {
594
            return new Expression(substr($value, 2));
595
        } elseif (is_string($value) &&  0 === strpos($value, '@')) {
596
            if (0 === strpos($value, '@@')) {
597
                $value = substr($value, 1);
598
                $invalidBehavior = null;
599
            } elseif (0 === strpos($value, '@?')) {
600
                $value = substr($value, 2);
601
                $invalidBehavior = ContainerInterface::IGNORE_ON_INVALID_REFERENCE;
602
            } else {
603
                $value = substr($value, 1);
604
                $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE;
605
            }
606
607
            if ('=' === substr($value, -1)) {
608
                $value = substr($value, 0, -1);
609
            }
610
611
            if (null !== $invalidBehavior) {
612
                $value = new Reference($value, $invalidBehavior);
613
            }
614
        }
615
616
        return $value;
617
    }
618
619
    private function loadFromExtensions($content)
620
    {
621
        foreach ($content as $namespace => $values) {
622
            // nette
623
            if (in_array($namespace, ['imports', 'includes', 'parameters', 'services'])) {
624
                continue;
625
            }
626
627
            if (!is_array($values)) {
628
                $values = [];
629
            }
630
631
            $this->container->loadFromExtension($namespace, $values);
632
        }
633
    }
634
635
    private function generateAnonymousServiceId($file)
636
    {
637
        return sprintf('%s_%d', hash('sha256', $file), ++$this->anonymousServicesCount);
638
    }
639
640
    private function checkDefinition($id, array $definition, $file)
641
    {
642
        if (!is_array($definition)) {
643
            throw new InvalidArgumentException(sprintf('A service definition must be an array or a string starting with "@" or a NEON entity but %s found for service "%s" in %s. Check your NEON syntax.', gettype($definition), $id, $file));
644
        }
645
646
        foreach ($definition as $key => $value) {
647
            if (!isset(self::$keywords[$key])) {
648
                throw new InvalidArgumentException(sprintf('The configuration key "%s" is unsupported for service definition "%s" in "%s". Allowed configuration keys are "%s".', $key, $id, $file, implode('", "', self::$keywords)));
649
            }
650
        }
651
    }
652
}
653