diff --git a/src/Symfony/Component/Config/Loader/FileLoader.php b/src/Symfony/Component/Config/Loader/FileLoader.php index 0ab6b648abef..828ac672124f 100644 --- a/src/Symfony/Component/Config/Loader/FileLoader.php +++ b/src/Symfony/Component/Config/Loader/FileLoader.php @@ -73,7 +73,7 @@ public function getLocator() */ public function import($resource, $type = null, $ignoreErrors = false, $sourceResource = null/*, $exclude = null*/) { - if (\func_num_args() < 5 && __CLASS__ !== \get_class($this) && __CLASS__ !== (new \ReflectionMethod($this, __FUNCTION__))->getDeclaringClass()->getName() && !$this instanceof \PHPUnit\Framework\MockObject\MockObject && !$this instanceof \Prophecy\Prophecy\ProphecySubjectInterface) { + if (\func_num_args() < 5 && __CLASS__ !== \get_class($this) && 0 !== strpos(\get_class($this), 'Symfony\Component\\') && __CLASS__ !== (new \ReflectionMethod($this, __FUNCTION__))->getDeclaringClass()->getName() && !$this instanceof \PHPUnit\Framework\MockObject\MockObject && !$this instanceof \Prophecy\Prophecy\ProphecySubjectInterface) { @trigger_error(sprintf('The "%s()" method will have a new "$exclude = null" argument in version 5.0, not defining it is deprecated since Symfony 4.4.', __METHOD__), E_USER_DEPRECATED); } $exclude = \func_num_args() >= 5 ? func_get_arg(4) : null; diff --git a/src/Symfony/Component/DependencyInjection/Loader/Configurator/ContainerConfigurator.php b/src/Symfony/Component/DependencyInjection/Loader/Configurator/ContainerConfigurator.php index 2fd74c0e3310..b9cb8b604908 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/Configurator/ContainerConfigurator.php +++ b/src/Symfony/Component/DependencyInjection/Loader/Configurator/ContainerConfigurator.php @@ -60,7 +60,7 @@ final public function extension(string $namespace, array $config) $this->container->loadFromExtension($namespace, static::processValue($config)); } - final public function import(string $resource, string $type = null, bool $ignoreErrors = false) + final public function import(string $resource, string $type = null, $ignoreErrors = false) { $this->loader->setCurrentDir(\dirname($this->path)); $this->loader->import($resource, $type, $ignoreErrors, $this->file); diff --git a/src/Symfony/Component/DependencyInjection/Loader/FileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/FileLoader.php index ceb0f0113e5b..c5af5ea7431b 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/FileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/FileLoader.php @@ -11,8 +11,11 @@ namespace Symfony\Component\DependencyInjection\Loader; +use Symfony\Component\Config\Exception\FileLocatorFileNotFoundException; +use Symfony\Component\Config\Exception\LoaderLoadException; use Symfony\Component\Config\FileLocatorInterface; use Symfony\Component\Config\Loader\FileLoader as BaseFileLoader; +use Symfony\Component\Config\Loader\Loader; use Symfony\Component\Config\Resource\GlobResource; use Symfony\Component\DependencyInjection\ChildDefinition; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -41,6 +44,42 @@ public function __construct(ContainerBuilder $container, FileLocatorInterface $l parent::__construct($locator); } + /** + * {@inheritdoc} + * + * @param bool|string $ignoreErrors Whether errors should be ignored; pass "not_found" to ignore only when the loaded resource is not found + * @param string|string[]|null $exclude Glob patterns to exclude from the import + */ + public function import($resource, $type = null, $ignoreErrors = false, $sourceResource = null/*, $exclude = null*/) + { + $args = \func_get_args(); + + if ($ignoreNotFound = 'not_found' === $ignoreErrors) { + $args[2] = false; + } elseif (!\is_bool($ignoreErrors)) { + @trigger_error(sprintf('Invalid argument $ignoreErrors provided to %s::import(): boolean or "not_found" expected, %s given.', \get_class($this), \gettype($ignoreErrors)), E_USER_DEPRECATED); + $args[2] = (bool) $ignoreErrors; + } + + try { + parent::import(...$args); + } catch (LoaderLoadException $e) { + if (!$ignoreNotFound || !($prev = $e->getPrevious()) instanceof FileLocatorFileNotFoundException) { + throw $e; + } + + foreach ($prev->getTrace() as $frame) { + if ('import' === ($frame['function'] ?? null) && is_a($frame['class'] ?? '', Loader::class, true)) { + break; + } + } + + if ($args !== $frame['args']) { + throw $e; + } + } + } + /** * Registers a set of classes as services using PSR-4 for discovery. * diff --git a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php index de770432b660..5733eb11ca37 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php @@ -105,7 +105,7 @@ private function parseImports(\DOMDocument $xml, string $file) $defaultDirectory = \dirname($file); foreach ($imports as $import) { $this->setCurrentDir($defaultDirectory); - $this->import($import->getAttribute('resource'), XmlUtils::phpize($import->getAttribute('type')) ?: null, (bool) XmlUtils::phpize($import->getAttribute('ignore-errors')), $file); + $this->import($import->getAttribute('resource'), XmlUtils::phpize($import->getAttribute('type')) ?: null, XmlUtils::phpize($import->getAttribute('ignore-errors')) ?: false, $file); } } diff --git a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php index 3290461c9f74..18a44b980689 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php @@ -191,7 +191,7 @@ private function parseImports(array $content, string $file) } $this->setCurrentDir($defaultDirectory); - $this->import($import['resource'], isset($import['type']) ? $import['type'] : null, isset($import['ignore_errors']) ? (bool) $import['ignore_errors'] : false, $file); + $this->import($import['resource'], $import['type'] ?? null, $import['ignore_errors'] ?? false, $file); } } diff --git a/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd b/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd index 4a71088a1c47..4f85f41035d8 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd +++ b/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd @@ -78,7 +78,7 @@ ]]> - + @@ -273,6 +273,12 @@ + + + + + + diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services4_bad_import_file_not_found.xml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services4_bad_import_file_not_found.xml new file mode 100644 index 000000000000..f3c6814b714b --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services4_bad_import_file_not_found.xml @@ -0,0 +1,9 @@ + + + + + + + diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services4_bad_import_nonvalid.xml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services4_bad_import_nonvalid.xml new file mode 100644 index 000000000000..9d026135cdcd --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services4_bad_import_nonvalid.xml @@ -0,0 +1,9 @@ + + + + + + + diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services4_bad_import_with_errors.xml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services4_bad_import_with_errors.xml new file mode 100644 index 000000000000..8644f8bacbb5 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services4_bad_import_with_errors.xml @@ -0,0 +1,9 @@ + + + + + + + diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services4_bad_import_file_not_found.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services4_bad_import_file_not_found.yml new file mode 100644 index 000000000000..d94e53c5a8e3 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services4_bad_import_file_not_found.yml @@ -0,0 +1,2 @@ +imports: + - { resource: foo_fake.yml, ignore_errors: not_found } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services4_bad_import_nonvalid.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services4_bad_import_nonvalid.yml new file mode 100644 index 000000000000..3ad1c2b63076 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services4_bad_import_nonvalid.yml @@ -0,0 +1,2 @@ +imports: + - { resource: nonvalid2.yml, ignore_errors: not_found } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services4_bad_import_with_errors.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services4_bad_import_with_errors.yml new file mode 100644 index 000000000000..42bca4ca0674 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services4_bad_import_with_errors.yml @@ -0,0 +1,2 @@ +imports: + - { resource: foo_fake.yml } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php index b5a0296dad3b..2c5386572a43 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php @@ -183,6 +183,37 @@ public function testLoadImports() // Bad import throws no exception due to ignore_errors value. $loader->load('services4_bad_import.xml'); + + // Bad import with nonexistent file throws no exception due to ignore_errors: not_found value. + $loader->load('services4_bad_import_file_not_found.xml'); + + try { + $loader->load('services4_bad_import_with_errors.xml'); + $this->fail('->load() throws a LoaderLoadException if the imported xml file configuration does not exist'); + } catch (\Exception $e) { + $this->assertInstanceOf('Symfony\\Component\\Config\\Exception\\LoaderLoadException', $e, '->load() throws a LoaderLoadException if the imported xml file configuration does not exist'); + $this->assertRegExp(sprintf('#^The file "%1$s" does not exist \(in: .+\) in %1$s \(which is being imported from ".+%2$s"\)\.$#', 'foo_fake\.xml', 'services4_bad_import_with_errors\.xml'), $e->getMessage(), '->load() throws a LoaderLoadException if the imported xml file configuration does not exist'); + + $e = $e->getPrevious(); + $this->assertInstanceOf('Symfony\\Component\\Config\\Exception\\FileLocatorFileNotFoundException', $e, '->load() throws a FileLocatorFileNotFoundException if the imported xml file configuration does not exist'); + $this->assertRegExp(sprintf('#^The file "%s" does not exist \(in: .+\)\.$#', 'foo_fake\.xml'), $e->getMessage(), '->load() throws a FileLocatorFileNotFoundException if the imported xml file configuration does not exist'); + } + + try { + $loader->load('services4_bad_import_nonvalid.xml'); + $this->fail('->load() throws an LoaderLoadException if the imported configuration does not validate the XSD'); + } catch (\Exception $e) { + $this->assertInstanceOf('Symfony\\Component\\Config\\Exception\\LoaderLoadException', $e, '->load() throws a LoaderLoadException if the imported configuration does not validate the XSD'); + $this->assertRegExp(sprintf('#^Unable to parse file ".+%s": .+.$#', 'nonvalid\.xml'), $e->getMessage(), '->load() throws a LoaderLoadException if the imported configuration does not validate the XSD'); + + $e = $e->getPrevious(); + $this->assertInstanceOf('Symfony\\Component\\DependencyInjection\\Exception\\InvalidArgumentException', $e, '->load() throws an InvalidArgumentException if the configuration does not validate the XSD'); + $this->assertRegExp(sprintf('#^Unable to parse file ".+%s": .+.$#', 'nonvalid\.xml'), $e->getMessage(), '->load() throws an InvalidArgumentException if the configuration does not validate the XSD'); + + $e = $e->getPrevious(); + $this->assertInstanceOf('Symfony\\Component\\Config\\Util\\Exception\\XmlParsingException', $e, '->load() throws a XmlParsingException if the configuration does not validate the XSD'); + $this->assertStringStartsWith('[ERROR 1845] Element \'nonvalid\': No matching global declaration available for the validation root. (in', $e->getMessage(), '->load() throws a XmlParsingException if the loaded file does not validate the XSD'); + } } public function testLoadAnonymousServices() diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php index 4e3a2bb38b36..2010e36ababb 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php @@ -135,6 +135,33 @@ public function testLoadImports() // Bad import throws no exception due to ignore_errors value. $loader->load('services4_bad_import.yml'); + + // Bad import with nonexistent file throws no exception due to ignore_errors: not_found value. + $loader->load('services4_bad_import_file_not_found.yml'); + + try { + $loader->load('services4_bad_import_with_errors.yml'); + $this->fail('->load() throws a LoaderLoadException if the imported yaml file does not exist'); + } catch (\Exception $e) { + $this->assertInstanceOf('Symfony\\Component\\Config\\Exception\\LoaderLoadException', $e, '->load() throws a LoaderLoadException if the imported yaml file does not exist'); + $this->assertRegExp(sprintf('#^The file "%1$s" does not exist \(in: .+\) in %1$s \(which is being imported from ".+%2$s"\)\.$#', 'foo_fake\.yml', 'services4_bad_import_with_errors\.yml'), $e->getMessage(), '->load() throws a LoaderLoadException if the imported yaml file does not exist'); + + $e = $e->getPrevious(); + $this->assertInstanceOf('Symfony\\Component\\Config\\Exception\\FileLocatorFileNotFoundException', $e, '->load() throws a FileLocatorFileNotFoundException if the imported yaml file does not exist'); + $this->assertRegExp(sprintf('#^The file "%s" does not exist \(in: .+\)\.$#', 'foo_fake\.yml'), $e->getMessage(), '->load() throws a FileLocatorFileNotFoundException if the imported yaml file does not exist'); + } + + try { + $loader->load('services4_bad_import_nonvalid.yml'); + $this->fail('->load() throws a LoaderLoadException if the tag in the imported yaml file is not valid'); + } catch (\Exception $e) { + $this->assertInstanceOf('Symfony\\Component\\Config\\Exception\\LoaderLoadException', $e, '->load() throws a LoaderLoadException if the tag in the imported yaml file is not valid'); + $this->assertRegExp(sprintf('#^The service file ".+%1$s" is not valid\. It should contain an array\. Check your YAML syntax in .+%1$s \(which is being imported from ".+%2$s"\)\.$#', 'nonvalid2\.yml', 'services4_bad_import_nonvalid.yml'), $e->getMessage(), '->load() throws a LoaderLoadException if the tag in the imported yaml file is not valid'); + + $e = $e->getPrevious(); + $this->assertInstanceOf('Symfony\\Component\\DependencyInjection\\Exception\\InvalidArgumentException', $e, '->load() throws an InvalidArgumentException if the tag in the imported yaml file is not valid'); + $this->assertRegExp(sprintf('#^The service file ".+%s" is not valid\. It should contain an array\. Check your YAML syntax\.$#', 'nonvalid2\.yml'), $e->getMessage(), '->load() throws an InvalidArgumentException if the tag in the imported yaml file is not valid'); + } } public function testLoadServices()