Verzeichnisstruktur phpBB-3.3.15
- Veröffentlicht
- 28.08.2024
So funktioniert es
|
Auf das letzte Element klicken. Dies geht jeweils ein Schritt zurück |
Auf das Icon klicken, dies öffnet das Verzeichnis. Nochmal klicken schließt das Verzeichnis. |
|
(Beispiel Datei-Icons)
|
Auf das Icon klicken um den Quellcode anzuzeigen |
YamlFileLoader.php
001 <?php
002
003 /*
004 * This file is part of the Symfony package.
005 *
006 * (c) Fabien Potencier <fabien@symfony.com>
007 *
008 * For the full copyright and license information, please view the LICENSE
009 * file that was distributed with this source code.
010 */
011
012 namespace Symfony\Component\DependencyInjection\Loader;
013
014 use Symfony\Component\DependencyInjection\Alias;
015 use Symfony\Component\DependencyInjection\Argument\ArgumentInterface;
016 use Symfony\Component\DependencyInjection\Argument\BoundArgument;
017 use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
018 use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument;
019 use Symfony\Component\DependencyInjection\ChildDefinition;
020 use Symfony\Component\DependencyInjection\ContainerBuilder;
021 use Symfony\Component\DependencyInjection\ContainerInterface;
022 use Symfony\Component\DependencyInjection\Definition;
023 use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
024 use Symfony\Component\DependencyInjection\Exception\RuntimeException;
025 use Symfony\Component\DependencyInjection\Reference;
026 use Symfony\Component\ExpressionLanguage\Expression;
027 use Symfony\Component\Yaml\Exception\ParseException;
028 use Symfony\Component\Yaml\Parser as YamlParser;
029 use Symfony\Component\Yaml\Tag\TaggedValue;
030 use Symfony\Component\Yaml\Yaml;
031
032 /**
033 * YamlFileLoader loads YAML files service definitions.
034 *
035 * @author Fabien Potencier <fabien@symfony.com>
036 */
037 class YamlFileLoader extends FileLoader
038 {
039 private static $serviceKeywords = [
040 'alias' => 'alias',
041 'parent' => 'parent',
042 'class' => 'class',
043 'shared' => 'shared',
044 'synthetic' => 'synthetic',
045 'lazy' => 'lazy',
046 'public' => 'public',
047 'abstract' => 'abstract',
048 'deprecated' => 'deprecated',
049 'factory' => 'factory',
050 'file' => 'file',
051 'arguments' => 'arguments',
052 'properties' => 'properties',
053 'configurator' => 'configurator',
054 'calls' => 'calls',
055 'tags' => 'tags',
056 'decorates' => 'decorates',
057 'decoration_inner_name' => 'decoration_inner_name',
058 'decoration_priority' => 'decoration_priority',
059 'autowire' => 'autowire',
060 'autowiring_types' => 'autowiring_types',
061 'autoconfigure' => 'autoconfigure',
062 'bind' => 'bind',
063 ];
064
065 private static $prototypeKeywords = [
066 'resource' => 'resource',
067 'namespace' => 'namespace',
068 'exclude' => 'exclude',
069 'parent' => 'parent',
070 'shared' => 'shared',
071 'lazy' => 'lazy',
072 'public' => 'public',
073 'abstract' => 'abstract',
074 'deprecated' => 'deprecated',
075 'factory' => 'factory',
076 'arguments' => 'arguments',
077 'properties' => 'properties',
078 'configurator' => 'configurator',
079 'calls' => 'calls',
080 'tags' => 'tags',
081 'autowire' => 'autowire',
082 'autoconfigure' => 'autoconfigure',
083 'bind' => 'bind',
084 ];
085
086 private static $instanceofKeywords = [
087 'shared' => 'shared',
088 'lazy' => 'lazy',
089 'public' => 'public',
090 'properties' => 'properties',
091 'configurator' => 'configurator',
092 'calls' => 'calls',
093 'tags' => 'tags',
094 'autowire' => 'autowire',
095 ];
096
097 private static $defaultsKeywords = [
098 'public' => 'public',
099 'tags' => 'tags',
100 'autowire' => 'autowire',
101 'autoconfigure' => 'autoconfigure',
102 'bind' => 'bind',
103 ];
104
105 private $yamlParser;
106
107 private $anonymousServicesCount;
108 private $anonymousServicesSuffix;
109
110 /**
111 * {@inheritdoc}
112 */
113 public function load($resource, $type = null)
114 {
115 $path = $this->locator->locate($resource);
116
117 $content = $this->loadFile($path);
118
119 $this->container->fileExists($path);
120
121 // empty file
122 if (null === $content) {
123 return;
124 }
125
126 // imports
127 $this->parseImports($content, $path);
128
129 // parameters
130 if (isset($content['parameters'])) {
131 if (!\is_array($content['parameters'])) {
132 throw new InvalidArgumentException(sprintf('The "parameters" key should contain an array in "%s". Check your YAML syntax.', $path));
133 }
134
135 foreach ($content['parameters'] as $key => $value) {
136 $this->container->setParameter($key, $this->resolveServices($value, $path, true));
137 }
138 }
139
140 // extensions
141 $this->loadFromExtensions($content);
142
143 // services
144 $this->anonymousServicesCount = 0;
145 $this->anonymousServicesSuffix = '~'.ContainerBuilder::hash($path);
146 $this->setCurrentDir(\dirname($path));
147 try {
148 $this->parseDefinitions($content, $path);
149 } finally {
150 $this->instanceof = [];
151 }
152 }
153
154 /**
155 * {@inheritdoc}
156 */
157 public function supports($resource, $type = null)
158 {
159 if (!\is_string($resource)) {
160 return false;
161 }
162
163 if (null === $type && \in_array(pathinfo($resource, \PATHINFO_EXTENSION), ['yaml', 'yml'], true)) {
164 return true;
165 }
166
167 return \in_array($type, ['yaml', 'yml'], true);
168 }
169
170 /**
171 * Parses all imports.
172 *
173 * @param string $file
174 */
175 private function parseImports(array $content, $file)
176 {
177 if (!isset($content['imports'])) {
178 return;
179 }
180
181 if (!\is_array($content['imports'])) {
182 throw new InvalidArgumentException(sprintf('The "imports" key should contain an array in "%s". Check your YAML syntax.', $file));
183 }
184
185 $defaultDirectory = \dirname($file);
186 foreach ($content['imports'] as $import) {
187 if (!\is_array($import)) {
188 $import = ['resource' => $import];
189 }
190 if (!isset($import['resource'])) {
191 throw new InvalidArgumentException(sprintf('An import should provide a resource in "%s". Check your YAML syntax.', $file));
192 }
193
194 $this->setCurrentDir($defaultDirectory);
195 $this->import($import['resource'], isset($import['type']) ? $import['type'] : null, isset($import['ignore_errors']) ? (bool) $import['ignore_errors'] : false, $file);
196 }
197 }
198
199 /**
200 * Parses definitions.
201 *
202 * @param string $file
203 */
204 private function parseDefinitions(array $content, $file)
205 {
206 if (!isset($content['services'])) {
207 return;
208 }
209
210 if (!\is_array($content['services'])) {
211 throw new InvalidArgumentException(sprintf('The "services" key should contain an array in "%s". Check your YAML syntax.', $file));
212 }
213
214 if (\array_key_exists('_instanceof', $content['services'])) {
215 $instanceof = $content['services']['_instanceof'];
216 unset($content['services']['_instanceof']);
217
218 if (!\is_array($instanceof)) {
219 throw new InvalidArgumentException(sprintf('Service "_instanceof" key must be an array, "%s" given in "%s".', \gettype($instanceof), $file));
220 }
221 $this->instanceof = [];
222 $this->isLoadingInstanceof = true;
223 foreach ($instanceof as $id => $service) {
224 if (!$service || !\is_array($service)) {
225 throw new InvalidArgumentException(sprintf('Type definition "%s" must be a non-empty array within "_instanceof" in "%s". Check your YAML syntax.', $id, $file));
226 }
227 if (\is_string($service) && 0 === strpos($service, '@')) {
228 throw new InvalidArgumentException(sprintf('Type definition "%s" cannot be an alias within "_instanceof" in "%s". Check your YAML syntax.', $id, $file));
229 }
230 $this->parseDefinition($id, $service, $file, []);
231 }
232 }
233
234 $this->isLoadingInstanceof = false;
235 $defaults = $this->parseDefaults($content, $file);
236 foreach ($content['services'] as $id => $service) {
237 $this->parseDefinition($id, $service, $file, $defaults);
238 }
239 }
240
241 /**
242 * @param string $file
243 *
244 * @return array
245 *
246 * @throws InvalidArgumentException
247 */
248 private function parseDefaults(array &$content, $file)
249 {
250 if (!\array_key_exists('_defaults', $content['services'])) {
251 return [];
252 }
253 $defaults = $content['services']['_defaults'];
254 unset($content['services']['_defaults']);
255
256 if (!\is_array($defaults)) {
257 throw new InvalidArgumentException(sprintf('Service "_defaults" key must be an array, "%s" given in "%s".', \gettype($defaults), $file));
258 }
259
260 foreach ($defaults as $key => $default) {
261 if (!isset(self::$defaultsKeywords[$key])) {
262 throw new InvalidArgumentException(sprintf('The configuration key "%s" cannot be used to define a default value in "%s". Allowed keys are "%s".', $key, $file, implode('", "', self::$defaultsKeywords)));
263 }
264 }
265
266 if (isset($defaults['tags'])) {
267 if (!\is_array($tags = $defaults['tags'])) {
268 throw new InvalidArgumentException(sprintf('Parameter "tags" in "_defaults" must be an array in "%s". Check your YAML syntax.', $file));
269 }
270
271 foreach ($tags as $tag) {
272 if (!\is_array($tag)) {
273 $tag = ['name' => $tag];
274 }
275
276 if (!isset($tag['name'])) {
277 throw new InvalidArgumentException(sprintf('A "tags" entry in "_defaults" is missing a "name" key in "%s".', $file));
278 }
279 $name = $tag['name'];
280 unset($tag['name']);
281
282 if (!\is_string($name) || '' === $name) {
283 throw new InvalidArgumentException(sprintf('The tag name in "_defaults" must be a non-empty string in "%s".', $file));
284 }
285
286 foreach ($tag as $attribute => $value) {
287 if (!is_scalar($value) && null !== $value) {
288 throw new InvalidArgumentException(sprintf('Tag "%s", attribute "%s" in "_defaults" must be of a scalar-type in "%s". Check your YAML syntax.', $name, $attribute, $file));
289 }
290 }
291 }
292 }
293
294 if (isset($defaults['bind'])) {
295 if (!\is_array($defaults['bind'])) {
296 throw new InvalidArgumentException(sprintf('Parameter "bind" in "_defaults" must be an array in "%s". Check your YAML syntax.', $file));
297 }
298
299 $defaults['bind'] = array_map(function ($v) { return new BoundArgument($v); }, $this->resolveServices($defaults['bind'], $file));
300 }
301
302 return $defaults;
303 }
304
305 /**
306 * @return bool
307 */
308 private function isUsingShortSyntax(array $service)
309 {
310 foreach ($service as $key => $value) {
311 if (\is_string($key) && ('' === $key || '$' !== $key[0])) {
312 return false;
313 }
314 }
315
316 return true;
317 }
318
319 /**
320 * Parses a definition.
321 *
322 * @param string $id
323 * @param array|string $service
324 * @param string $file
325 *
326 * @throws InvalidArgumentException When tags are invalid
327 */
328 private function parseDefinition($id, $service, $file, array $defaults)
329 {
330 if (preg_match('/^_[a-zA-Z0-9_]*$/', $id)) {
331 @trigger_error(sprintf('Service names that start with an underscore are deprecated since Symfony 3.3 and will be reserved in 4.0. Rename the "%s" service or define it in XML instead.', $id), \E_USER_DEPRECATED);
332 }
333 if (\is_string($service) && 0 === strpos($service, '@')) {
334 $this->container->setAlias($id, $alias = new Alias(substr($service, 1)));
335 if (isset($defaults['public'])) {
336 $alias->setPublic($defaults['public']);
337 }
338
339 return;
340 }
341
342 if (\is_array($service) && $this->isUsingShortSyntax($service)) {
343 $service = ['arguments' => $service];
344 }
345
346 if (null === $service) {
347 $service = [];
348 }
349
350 if (!\is_array($service)) {
351 throw new InvalidArgumentException(sprintf('A service definition must be an array or a string starting with "@" but "%s" found for service "%s" in "%s". Check your YAML syntax.', \gettype($service), $id, $file));
352 }
353
354 $this->checkDefinition($id, $service, $file);
355
356 if (isset($service['alias'])) {
357 $this->container->setAlias($id, $alias = new Alias($service['alias']));
358 if (isset($service['public'])) {
359 $alias->setPublic($service['public']);
360 } elseif (isset($defaults['public'])) {
361 $alias->setPublic($defaults['public']);
362 }
363
364 foreach ($service as $key => $value) {
365 if (!\in_array($key, ['alias', 'public'])) {
366 @trigger_error(sprintf('The configuration key "%s" is unsupported for the service "%s" which is defined as an alias in "%s". Allowed configuration keys for service aliases are "alias" and "public". The YamlFileLoader will raise an exception in Symfony 4.0, instead of silently ignoring unsupported attributes.', $key, $id, $file), \E_USER_DEPRECATED);
367 }
368 }
369
370 return;
371 }
372
373 if ($this->isLoadingInstanceof) {
374 $definition = new ChildDefinition('');
375 } elseif (isset($service['parent'])) {
376 if (!empty($this->instanceof)) {
377 throw new InvalidArgumentException(sprintf('The service "%s" cannot use the "parent" option in the same file where "_instanceof" configuration is defined as using both is not supported. Move your child definitions to a separate file.', $id));
378 }
379
380 foreach ($defaults as $k => $v) {
381 if ('tags' === $k) {
382 // since tags are never inherited from parents, there is no confusion
383 // thus we can safely add them as defaults to ChildDefinition
384 continue;
385 }
386 if ('bind' === $k) {
387 throw new InvalidArgumentException(sprintf('Attribute "bind" on service "%s" cannot be inherited from "_defaults" when a "parent" is set. Move your child definitions to a separate file.', $id));
388 }
389 if (!isset($service[$k])) {
390 throw new InvalidArgumentException(sprintf('Attribute "%s" on service "%s" cannot be inherited from "_defaults" when a "parent" is set. Move your child definitions to a separate file or define this attribute explicitly.', $k, $id));
391 }
392 }
393
394 $definition = new ChildDefinition($service['parent']);
395 } else {
396 $definition = new Definition();
397
398 if (isset($defaults['public'])) {
399 $definition->setPublic($defaults['public']);
400 }
401 if (isset($defaults['autowire'])) {
402 $definition->setAutowired($defaults['autowire']);
403 }
404 if (isset($defaults['autoconfigure'])) {
405 $definition->setAutoconfigured($defaults['autoconfigure']);
406 }
407
408 $definition->setChanges([]);
409 }
410
411 if (isset($service['class'])) {
412 $definition->setClass($service['class']);
413 }
414
415 if (isset($service['shared'])) {
416 $definition->setShared($service['shared']);
417 }
418
419 if (isset($service['synthetic'])) {
420 $definition->setSynthetic($service['synthetic']);
421 }
422
423 if (isset($service['lazy'])) {
424 $definition->setLazy($service['lazy']);
425 }
426
427 if (isset($service['public'])) {
428 $definition->setPublic($service['public']);
429 }
430
431 if (isset($service['abstract'])) {
432 $definition->setAbstract($service['abstract']);
433 }
434
435 if (\array_key_exists('deprecated', $service)) {
436 $definition->setDeprecated(true, $service['deprecated']);
437 }
438
439 if (isset($service['factory'])) {
440 $definition->setFactory($this->parseCallable($service['factory'], 'factory', $id, $file));
441 }
442
443 if (isset($service['file'])) {
444 $definition->setFile($service['file']);
445 }
446
447 if (isset($service['arguments'])) {
448 $definition->setArguments($this->resolveServices($service['arguments'], $file));
449 }
450
451 if (isset($service['properties'])) {
452 $definition->setProperties($this->resolveServices($service['properties'], $file));
453 }
454
455 if (isset($service['configurator'])) {
456 $definition->setConfigurator($this->parseCallable($service['configurator'], 'configurator', $id, $file));
457 }
458
459 if (isset($service['calls'])) {
460 if (!\is_array($service['calls'])) {
461 throw new InvalidArgumentException(sprintf('Parameter "calls" must be an array for service "%s" in "%s". Check your YAML syntax.', $id, $file));
462 }
463
464 foreach ($service['calls'] as $call) {
465 if (isset($call['method'])) {
466 $method = $call['method'];
467 $args = isset($call['arguments']) ? $this->resolveServices($call['arguments'], $file) : [];
468 } else {
469 $method = $call[0];
470 $args = isset($call[1]) ? $this->resolveServices($call[1], $file) : [];
471 }
472
473 if (!\is_array($args)) {
474 throw new InvalidArgumentException(sprintf('The second parameter for function call "%s" must be an array of its arguments for service "%s" in "%s". Check your YAML syntax.', $method, $id, $file));
475 }
476 $definition->addMethodCall($method, $args);
477 }
478 }
479
480 $tags = isset($service['tags']) ? $service['tags'] : [];
481 if (!\is_array($tags)) {
482 throw new InvalidArgumentException(sprintf('Parameter "tags" must be an array for service "%s" in "%s". Check your YAML syntax.', $id, $file));
483 }
484
485 if (isset($defaults['tags'])) {
486 $tags = array_merge($tags, $defaults['tags']);
487 }
488
489 foreach ($tags as $tag) {
490 if (!\is_array($tag)) {
491 $tag = ['name' => $tag];
492 }
493
494 if (!isset($tag['name'])) {
495 throw new InvalidArgumentException(sprintf('A "tags" entry is missing a "name" key for service "%s" in "%s".', $id, $file));
496 }
497 $name = $tag['name'];
498 unset($tag['name']);
499
500 if (!\is_string($name) || '' === $name) {
501 throw new InvalidArgumentException(sprintf('The tag name for service "%s" in "%s" must be a non-empty string.', $id, $file));
502 }
503
504 foreach ($tag as $attribute => $value) {
505 if (!is_scalar($value) && null !== $value) {
506 throw new InvalidArgumentException(sprintf('A "tags" attribute must be of a scalar-type for service "%s", tag "%s", attribute "%s" in "%s". Check your YAML syntax.', $id, $name, $attribute, $file));
507 }
508 }
509
510 $definition->addTag($name, $tag);
511 }
512
513 if (isset($service['decorates'])) {
514 if ('' !== $service['decorates'] && '@' === $service['decorates'][0]) {
515 throw new InvalidArgumentException(sprintf('The value of the "decorates" option for the "%s" service must be the id of the service without the "@" prefix (replace "%s" with "%s").', $id, $service['decorates'], substr($service['decorates'], 1)));
516 }
517
518 $renameId = isset($service['decoration_inner_name']) ? $service['decoration_inner_name'] : null;
519 $priority = isset($service['decoration_priority']) ? $service['decoration_priority'] : 0;
520 $definition->setDecoratedService($service['decorates'], $renameId, $priority);
521 }
522
523 if (isset($service['autowire'])) {
524 $definition->setAutowired($service['autowire']);
525 }
526
527 if (isset($service['autowiring_types'])) {
528 if (\is_string($service['autowiring_types'])) {
529 $definition->addAutowiringType($service['autowiring_types']);
530 } else {
531 if (!\is_array($service['autowiring_types'])) {
532 throw new InvalidArgumentException(sprintf('Parameter "autowiring_types" must be a string or an array for service "%s" in "%s". Check your YAML syntax.', $id, $file));
533 }
534
535 foreach ($service['autowiring_types'] as $autowiringType) {
536 if (!\is_string($autowiringType)) {
537 throw new InvalidArgumentException(sprintf('A "autowiring_types" attribute must be of type string for service "%s" in "%s". Check your YAML syntax.', $id, $file));
538 }
539
540 $definition->addAutowiringType($autowiringType);
541 }
542 }
543 }
544
545 if (isset($defaults['bind']) || isset($service['bind'])) {
546 // deep clone, to avoid multiple process of the same instance in the passes
547 $bindings = isset($defaults['bind']) ? unserialize(serialize($defaults['bind'])) : [];
548
549 if (isset($service['bind'])) {
550 if (!\is_array($service['bind'])) {
551 throw new InvalidArgumentException(sprintf('Parameter "bind" must be an array for service "%s" in "%s". Check your YAML syntax.', $id, $file));
552 }
553
554 $bindings = array_merge($bindings, $this->resolveServices($service['bind'], $file));
555 }
556
557 $definition->setBindings($bindings);
558 }
559
560 if (isset($service['autoconfigure'])) {
561 if (!$definition instanceof ChildDefinition) {
562 $definition->setAutoconfigured($service['autoconfigure']);
563 } elseif ($service['autoconfigure']) {
564 throw new InvalidArgumentException(sprintf('The service "%s" cannot have a "parent" and also have "autoconfigure". Try setting "autoconfigure: false" for the service.', $id));
565 }
566 }
567
568 if (\array_key_exists('namespace', $service) && !\array_key_exists('resource', $service)) {
569 throw new InvalidArgumentException(sprintf('A "resource" attribute must be set when the "namespace" attribute is set for service "%s" in "%s". Check your YAML syntax.', $id, $file));
570 }
571
572 if (\array_key_exists('resource', $service)) {
573 if (!\is_string($service['resource'])) {
574 throw new InvalidArgumentException(sprintf('A "resource" attribute must be of type string for service "%s" in "%s". Check your YAML syntax.', $id, $file));
575 }
576 $exclude = isset($service['exclude']) ? $service['exclude'] : null;
577 $namespace = isset($service['namespace']) ? $service['namespace'] : $id;
578 $this->registerClasses($definition, $namespace, $service['resource'], $exclude);
579 } else {
580 $this->setDefinition($id, $definition);
581 }
582 }
583
584 /**
585 * Parses a callable.
586 *
587 * @param string|array $callable A callable
588 * @param string $parameter A parameter (e.g. 'factory' or 'configurator')
589 * @param string $id A service identifier
590 * @param string $file A parsed file
591 *
592 * @throws InvalidArgumentException When errors occur
593 *
594 * @return string|array A parsed callable
595 */
596 private function parseCallable($callable, $parameter, $id, $file)
597 {
598 if (\is_string($callable)) {
599 if ('' !== $callable && '@' === $callable[0]) {
600 throw new InvalidArgumentException(sprintf('The value of the "%s" option for the "%s" service must be the id of the service without the "@" prefix (replace "%s" with "%s").', $parameter, $id, $callable, substr($callable, 1)));
601 }
602
603 if (false !== strpos($callable, ':') && false === strpos($callable, '::')) {
604 $parts = explode(':', $callable);
605
606 return [$this->resolveServices('@'.$parts[0], $file), $parts[1]];
607 }
608
609 return $callable;
610 }
611
612 if (\is_array($callable)) {
613 if (isset($callable[0]) && isset($callable[1])) {
614 return [$this->resolveServices($callable[0], $file), $callable[1]];
615 }
616
617 if ('factory' === $parameter && isset($callable[1]) && null === $callable[0]) {
618 return $callable;
619 }
620
621 throw new InvalidArgumentException(sprintf('Parameter "%s" must contain an array with two elements for service "%s" in "%s". Check your YAML syntax.', $parameter, $id, $file));
622 }
623
624 throw new InvalidArgumentException(sprintf('Parameter "%s" must be a string or an array for service "%s" in "%s". Check your YAML syntax.', $parameter, $id, $file));
625 }
626
627 /**
628 * Loads a YAML file.
629 *
630 * @param string $file
631 *
632 * @return array The file content
633 *
634 * @throws InvalidArgumentException when the given file is not a local file or when it does not exist
635 */
636 protected function loadFile($file)
637 {
638 if (!class_exists('Symfony\Component\Yaml\Parser')) {
639 throw new RuntimeException('Unable to load YAML config files as the Symfony Yaml Component is not installed.');
640 }
641
642 if (!stream_is_local($file)) {
643 throw new InvalidArgumentException(sprintf('This is not a local file "%s".', $file));
644 }
645
646 if (!file_exists($file)) {
647 throw new InvalidArgumentException(sprintf('The file "%s" does not exist.', $file));
648 }
649
650 if (null === $this->yamlParser) {
651 $this->yamlParser = new YamlParser();
652 }
653
654 $prevErrorHandler = set_error_handler(function ($level, $message, $script, $line) use ($file, &$prevErrorHandler) {
655 $message = \E_USER_DEPRECATED === $level ? preg_replace('/ on line \d+/', ' in "'.$file.'"$0', $message) : $message;
656
657 return $prevErrorHandler ? $prevErrorHandler($level, $message, $script, $line) : false;
658 });
659
660 try {
661 $configuration = $this->yamlParser->parseFile($file, Yaml::PARSE_CONSTANT | Yaml::PARSE_CUSTOM_TAGS);
662 } catch (ParseException $e) {
663 throw new InvalidArgumentException(sprintf('The file "%s" does not contain valid YAML: ', $file).$e->getMessage(), 0, $e);
664 } finally {
665 restore_error_handler();
666 }
667
668 return $this->validate($configuration, $file);
669 }
670
671 /**
672 * Validates a YAML file.
673 *
674 * @param mixed $content
675 * @param string $file
676 *
677 * @return array
678 *
679 * @throws InvalidArgumentException When service file is not valid
680 */
681 private function validate($content, $file)
682 {
683 if (null === $content) {
684 return $content;
685 }
686
687 if (!\is_array($content)) {
688 throw new InvalidArgumentException(sprintf('The service file "%s" is not valid. It should contain an array. Check your YAML syntax.', $file));
689 }
690
691 foreach ($content as $namespace => $data) {
692 if (\in_array($namespace, ['imports', 'parameters', 'services'])) {
693 continue;
694 }
695
696 if (!$this->container->hasExtension($namespace)) {
697 $extensionNamespaces = array_filter(array_map(function ($ext) { return $ext->getAlias(); }, $this->container->getExtensions()));
698 throw new InvalidArgumentException(sprintf('There is no extension able to load the configuration for "%s" (in "%s"). Looked for namespace "%s", found "%s".', $namespace, $file, $namespace, $extensionNamespaces ? sprintf('"%s"', implode('", "', $extensionNamespaces)) : 'none'));
699 }
700 }
701
702 return $content;
703 }
704
705 /**
706 * Resolves services.
707 *
708 * @param mixed $value
709 * @param string $file
710 * @param bool $isParameter
711 *
712 * @return array|string|Reference|ArgumentInterface
713 */
714 private function resolveServices($value, $file, $isParameter = false)
715 {
716 if ($value instanceof TaggedValue) {
717 $argument = $value->getValue();
718 if ('iterator' === $value->getTag()) {
719 if (!\is_array($argument)) {
720 throw new InvalidArgumentException(sprintf('"!iterator" tag only accepts sequences in "%s".', $file));
721 }
722 $argument = $this->resolveServices($argument, $file, $isParameter);
723 try {
724 return new IteratorArgument($argument);
725 } catch (InvalidArgumentException $e) {
726 throw new InvalidArgumentException(sprintf('"!iterator" tag only accepts arrays of "@service" references in "%s".', $file));
727 }
728 }
729 if ('tagged' === $value->getTag()) {
730 if (!\is_string($argument) || !$argument) {
731 throw new InvalidArgumentException(sprintf('"!tagged" tag only accepts non empty string in "%s".', $file));
732 }
733
734 return new TaggedIteratorArgument($argument);
735 }
736 if ('service' === $value->getTag()) {
737 if ($isParameter) {
738 throw new InvalidArgumentException(sprintf('Using an anonymous service in a parameter is not allowed in "%s".', $file));
739 }
740
741 $isLoadingInstanceof = $this->isLoadingInstanceof;
742 $this->isLoadingInstanceof = false;
743 $instanceof = $this->instanceof;
744 $this->instanceof = [];
745
746 $id = sprintf('%d_%s', ++$this->anonymousServicesCount, preg_replace('/^.*\\\\/', '', isset($argument['class']) ? $argument['class'] : '').$this->anonymousServicesSuffix);
747 $this->parseDefinition($id, $argument, $file, []);
748
749 if (!$this->container->hasDefinition($id)) {
750 throw new InvalidArgumentException(sprintf('Creating an alias using the tag "!service" is not allowed in "%s".', $file));
751 }
752
753 $this->container->getDefinition($id)->setPublic(false);
754
755 $this->isLoadingInstanceof = $isLoadingInstanceof;
756 $this->instanceof = $instanceof;
757
758 return new Reference($id);
759 }
760
761 throw new InvalidArgumentException(sprintf('Unsupported tag "!%s".', $value->getTag()));
762 }
763
764 if (\is_array($value)) {
765 foreach ($value as $k => $v) {
766 $value[$k] = $this->resolveServices($v, $file, $isParameter);
767 }
768 } elseif (\is_string($value) && 0 === strpos($value, '@=')) {
769 if (!class_exists(Expression::class)) {
770 throw new \LogicException(sprintf('The "@=" expression syntax cannot be used without the ExpressionLanguage component. Try running "composer require symfony/expression-language".'));
771 }
772
773 return new Expression(substr($value, 2));
774 } elseif (\is_string($value) && 0 === strpos($value, '@')) {
775 if (0 === strpos($value, '@@')) {
776 $value = substr($value, 1);
777 $invalidBehavior = null;
778 } elseif (0 === strpos($value, '@!')) {
779 $value = substr($value, 2);
780 $invalidBehavior = ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE;
781 } elseif (0 === strpos($value, '@?')) {
782 $value = substr($value, 2);
783 $invalidBehavior = ContainerInterface::IGNORE_ON_INVALID_REFERENCE;
784 } else {
785 $value = substr($value, 1);
786 $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE;
787 }
788
789 if ('=' === substr($value, -1)) {
790 @trigger_error(sprintf('The "=" suffix that used to disable strict references in Symfony 2.x is deprecated since Symfony 3.3 and will be unsupported in 4.0. Remove it in "%s".', $value), \E_USER_DEPRECATED);
791 $value = substr($value, 0, -1);
792 }
793
794 if (null !== $invalidBehavior) {
795 $value = new Reference($value, $invalidBehavior);
796 }
797 }
798
799 return $value;
800 }
801
802 /**
803 * Loads from Extensions.
804 */
805 private function loadFromExtensions(array $content)
806 {
807 foreach ($content as $namespace => $values) {
808 if (\in_array($namespace, ['imports', 'parameters', 'services'])) {
809 continue;
810 }
811
812 if (!\is_array($values) && null !== $values) {
813 $values = [];
814 }
815
816 $this->container->loadFromExtension($namespace, $values);
817 }
818 }
819
820 /**
821 * Checks the keywords used to define a service.
822 *
823 * @param string $id The service name
824 * @param array $definition The service definition to check
825 * @param string $file The loaded YAML file
826 */
827 private function checkDefinition($id, array $definition, $file)
828 {
829 if ($throw = $this->isLoadingInstanceof) {
830 $keywords = self::$instanceofKeywords;
831 } elseif ($throw = (isset($definition['resource']) || isset($definition['namespace']))) {
832 $keywords = self::$prototypeKeywords;
833 } else {
834 $keywords = self::$serviceKeywords;
835 }
836
837 foreach ($definition as $key => $value) {
838 if (!isset($keywords[$key])) {
839 if ($throw) {
840 throw new InvalidArgumentException(sprintf('The configuration key "%s" is unsupported for definition "%s" in "%s". Allowed configuration keys are "%s".', $key, $id, $file, implode('", "', $keywords)));
841 }
842
843 @trigger_error(sprintf('The configuration key "%s" is unsupported for service definition "%s" in "%s". Allowed configuration keys are "%s". The YamlFileLoader object will raise an exception instead in Symfony 4.0 when detecting an unsupported service configuration key.', $key, $id, $file, implode('", "', $keywords)), \E_USER_DEPRECATED);
844 }
845 }
846 }
847 }
848