Skip to content

Commit

Permalink
[Config] [DependencyInjection] [Routing] Added option `ignore_not_fou…
Browse files Browse the repository at this point in the history
…nd` for imported config files
  • Loading branch information
pulzarraider committed Apr 28, 2019
1 parent 77f642e commit 5ab59f2
Show file tree
Hide file tree
Showing 23 changed files with 182 additions and 21 deletions.
1 change: 1 addition & 0 deletions UPGRADE-5.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ Config
* Removed `FileLoaderLoadException`, use `LoaderLoadException` instead.
* Using environment variables with `cannotBeEmpty()` if the value is validated with `validate()` will throw an exception.
* Removed the `root()` method in `TreeBuilder`, pass the root node information to the constructor instead
* The 3rd `boolean` argument `$ignoreErrors` in `import` method in `FileLoader` was changed to `int` `$errorLevel`. Use `LoaderInterface::ERROR_LEVEL_IGNORE_ALL` instead of `true`. Use `LoaderInterface::ERROR_LEVEL_ALL` instead of `false`.

Console
-------
Expand Down
5 changes: 5 additions & 0 deletions src/Symfony/Component/Config/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
CHANGELOG
=========

4.4.0
-----
* added option `ignore_not_found` for imported config files
* deprecated 3rd `boolean` argument `$ignoreErrors` and changed to `int` `$errorLevel` in `import` method in `FileLoader`.

4.3.0
-----

Expand Down
33 changes: 24 additions & 9 deletions src/Symfony/Component/Config/Loader/FileLoader.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ public function getLocator()
*
* @param mixed $resource A Resource
* @param string|null $type The resource type or null if unknown
* @param bool $ignoreErrors Whether to ignore import errors or not
* @param int $errorLevel Whether to ignore import errors or not
* @param string|null $sourceResource The original resource importing the new resource
*
* @return mixed
Expand All @@ -70,13 +70,23 @@ public function getLocator()
* @throws FileLoaderImportCircularReferenceException
* @throws FileLocatorFileNotFoundException
*/
public function import($resource, $type = null, $ignoreErrors = false, $sourceResource = null)
public function import($resource, $type = null, $errorLevel = LoaderInterface::ERROR_LEVEL_ALL, $sourceResource = null)
{
if (\is_bool($errorLevel)) {
@trigger_error('The boolean value you are using for the $errorLevel argument of the '.__CLASS__.'::import method is deprecated since Symfony 4.4 and will not be supported anymore in 5.0. Use the constants defined in the FileLoader instead.', E_USER_DEPRECATED);

if (true === $errorLevel) {
$errorLevel = LoaderInterface::ERROR_LEVEL_IGNORE_ALL;
} elseif (false === $errorLevel) {
$errorLevel = LoaderInterface::ERROR_LEVEL_ALL;
}
}

if (\is_string($resource) && \strlen($resource) !== $i = strcspn($resource, '*?{[')) {
$ret = [];
$isSubpath = 0 !== $i && false !== strpos(substr($resource, 0, $i), '/');
foreach ($this->glob($resource, false, $_, $ignoreErrors || !$isSubpath) as $path => $info) {
if (null !== $res = $this->doImport($path, $type, $ignoreErrors, $sourceResource)) {
foreach ($this->glob($resource, false, $_, $errorLevel || !$isSubpath) as $path => $info) {
if (null !== $res = $this->doImport($path, $type, $errorLevel, $sourceResource)) {
$ret[] = $res;
}
$isSubpath = true;
Expand All @@ -87,13 +97,13 @@ public function import($resource, $type = null, $ignoreErrors = false, $sourceRe
}
}

return $this->doImport($resource, $type, $ignoreErrors, $sourceResource);
return $this->doImport($resource, $type, $errorLevel, $sourceResource);
}

/**
* @internal
*/
protected function glob(string $pattern, bool $recursive, &$resource = null, bool $ignoreErrors = false, bool $forExclusion = false, array $excluded = [])
protected function glob(string $pattern, bool $recursive, &$resource = null, int $errorLevel = LoaderInterface::ERROR_LEVEL_ALL, bool $forExclusion = false, array $excluded = [])
{
if (\strlen($pattern) === $i = strcspn($pattern, '*?{[')) {
$prefix = $pattern;
Expand All @@ -109,7 +119,7 @@ protected function glob(string $pattern, bool $recursive, &$resource = null, boo
try {
$prefix = $this->locator->locate($prefix, $this->currentDir, true);
} catch (FileLocatorFileNotFoundException $e) {
if (!$ignoreErrors) {
if ($errorLevel & LoaderInterface::ERROR_LEVEL_FILE_NOT_FOUND) {
throw $e;
}

Expand All @@ -125,7 +135,7 @@ protected function glob(string $pattern, bool $recursive, &$resource = null, boo
yield from $resource;
}

private function doImport($resource, $type = null, bool $ignoreErrors = false, $sourceResource = null)
private function doImport($resource, $type = null, int $errorLevel = LoaderInterface::ERROR_LEVEL_ALL, $sourceResource = null)
{
try {
$loader = $this->resolve($resource, $type);
Expand Down Expand Up @@ -157,7 +167,12 @@ private function doImport($resource, $type = null, bool $ignoreErrors = false, $
} catch (FileLoaderImportCircularReferenceException $e) {
throw $e;
} catch (\Exception $e) {
if (!$ignoreErrors) {
if (LoaderInterface::ERROR_LEVEL_IGNORE_ALL !== $errorLevel) {
if (0 === ($errorLevel & LoaderInterface::ERROR_LEVEL_FILE_NOT_FOUND) && ($e instanceof FileLocatorFileNotFoundException)) {
// ignore exception if file was not found
return;
}

// prevent embedded imports from nesting multiple exceptions
if ($e instanceof LoaderLoadException) {
throw $e;
Expand Down
21 changes: 21 additions & 0 deletions src/Symfony/Component/Config/Loader/LoaderInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,27 @@
*/
interface LoaderInterface
{
/**
* Ignore all errors.
*/
const ERROR_LEVEL_IGNORE_ALL = 0;

/**
* Basic error level.
*/
const ERROR_LEVEL_BASIC = 1;

/**
* Occurs if file was not found.
*/
const ERROR_LEVEL_FILE_NOT_FOUND = 2;

/**
* Throws exceptions if any error occurs.
* The value of this constant will change in the future because it contains the sum of all error levels.
*/
const ERROR_LEVEL_ALL = 3;

/**
* Loads a resource.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

namespace Symfony\Component\DependencyInjection\Loader\Configurator;

use Symfony\Component\Config\Loader\LoaderInterface;
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument;
use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument;
Expand Down Expand Up @@ -60,10 +61,10 @@ 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, int $errorLevel = LoaderInterface::ERROR_LEVEL_ALL)
{
$this->loader->setCurrentDir(\dirname($this->path));
$this->loader->import($resource, $type, $ignoreErrors, $this->file);
$this->loader->import($resource, $type, $errorLevel, $this->file);
}

final public function parameters(): ParametersConfigurator
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@

namespace Symfony\Component\DependencyInjection\Loader;

use Symfony\Component\Config\Loader\LoaderInterface;

/**
* DirectoryLoader is a recursive loader to go through directories.
*
Expand All @@ -35,7 +37,7 @@ public function load($file, $type = null)

$this->setCurrentDir($path);

$this->import($dir, null, false, $path);
$this->import($dir, null, LoaderInterface::ERROR_LEVEL_ALL, $path);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

namespace Symfony\Component\DependencyInjection\Loader;

use Symfony\Component\Config\Loader\LoaderInterface;
use Symfony\Component\Config\Util\XmlUtils;
use Symfony\Component\DependencyInjection\Alias;
use Symfony\Component\DependencyInjection\Argument\BoundArgument;
Expand Down Expand Up @@ -116,7 +117,15 @@ private function parseImports(\DOMDocument $xml, $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);

$errorLevel = LoaderInterface::ERROR_LEVEL_ALL;
if (true === (bool) XmlUtils::phpize($import->getAttribute('ignore-errors'))) {
$errorLevel = LoaderInterface::ERROR_LEVEL_IGNORE_ALL;
} elseif (true === (bool) XmlUtils::phpize($import->getAttribute('ignore-not-found'))) {
$errorLevel = $errorLevel & ~LoaderInterface::ERROR_LEVEL_FILE_NOT_FOUND;
}

$this->import($import->getAttribute('resource'), XmlUtils::phpize($import->getAttribute('type')) ?: null, $errorLevel, $file);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

namespace Symfony\Component\DependencyInjection\Loader;

use Symfony\Component\Config\Loader\LoaderInterface;
use Symfony\Component\DependencyInjection\Alias;
use Symfony\Component\DependencyInjection\Argument\ArgumentInterface;
use Symfony\Component\DependencyInjection\Argument\BoundArgument;
Expand Down Expand Up @@ -189,7 +190,15 @@ 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);

$errorLevel = LoaderInterface::ERROR_LEVEL_ALL;
if (isset($import['ignore_errors']) && true === (bool) $import['ignore_errors']) {
$errorLevel = LoaderInterface::ERROR_LEVEL_IGNORE_ALL;
} elseif (isset($import['ignore_not_found']) && true === (bool) $import['ignore_not_found']) {
$errorLevel = $errorLevel & ~LoaderInterface::ERROR_LEVEL_FILE_NOT_FOUND;
}

$this->import($import['resource'], isset($import['type']) ? $import['type'] : null, $errorLevel, $file);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@
</xsd:annotation>
<xsd:attribute name="resource" type="xsd:string" use="required" />
<xsd:attribute name="ignore-errors" type="boolean" />
<xsd:attribute name="ignore-not-found" type="boolean" />
<xsd:attribute name="type" type="xsd:string" />
</xsd:complexType>

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?xml version="1.0" ?>

<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services https://symfony.com/schema/dic/services/services-1.0.xsd">
<imports>
<import resource="foo_fake.xml" ignore-not-found="true" />
</imports>
</container>
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?xml version="1.0" ?>

<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services https://symfony.com/schema/dic/services/services-1.0.xsd">
<imports>
<import resource="nonvalid.xml" ignore-not-found="true" />
</imports>
</container>
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?xml version="1.0" ?>

<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services https://symfony.com/schema/dic/services/services-1.0.xsd">
<imports>
<import resource="foo_fake.xml" />
</imports>
</container>
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
imports:
- { resource: foo_fake.yml, ignore_not_found: true }
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
imports:
- { resource: nonvalid2.yml, ignore_not_found: true }
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
imports:
- { resource: foo_fake.yml }
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

use PHPUnit\Framework\TestCase;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\Config\Loader\LoaderInterface;
use Symfony\Component\Config\Resource\GlobResource;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Loader\GlobFileLoader;
Expand All @@ -38,7 +39,7 @@ public function testLoadAddsTheGlobResourceToTheContainer()

class GlobFileLoaderWithoutImport extends GlobFileLoader
{
public function import($resource, $type = null, $ignoreErrors = false, $sourceResource = null)
public function import($resource, $type = null, $errorLevel = LoaderInterface::ERROR_LEVEL_ALL, $sourceResource = null)
{
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,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_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()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,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_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()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

namespace Symfony\Component\Routing\Loader\Configurator;

use Symfony\Component\Config\Loader\LoaderInterface;
use Symfony\Component\Routing\Loader\PhpFileLoader;
use Symfony\Component\Routing\RouteCollection;

Expand All @@ -36,10 +37,10 @@ public function __construct(RouteCollection $collection, PhpFileLoader $loader,
/**
* @return ImportConfigurator
*/
final public function import($resource, $type = null, $ignoreErrors = false)
final public function import($resource, $type = null, $errorLevel = LoaderInterface::ERROR_LEVEL_ALL)
{
$this->loader->setCurrentDir(\dirname($this->path));
$imported = $this->loader->import($resource, $type, $ignoreErrors, $this->file);
$imported = $this->loader->import($resource, $type, $errorLevel, $this->file);
if (!\is_array($imported)) {
return new ImportConfigurator($this->collection, $imported);
}
Expand Down
3 changes: 2 additions & 1 deletion src/Symfony/Component/Routing/Loader/DirectoryLoader.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
namespace Symfony\Component\Routing\Loader;

use Symfony\Component\Config\Loader\FileLoader;
use Symfony\Component\Config\Loader\LoaderInterface;
use Symfony\Component\Config\Resource\DirectoryResource;
use Symfony\Component\Routing\RouteCollection;

Expand All @@ -38,7 +39,7 @@ public function load($file, $type = null)
$subType = 'directory';
}

$subCollection = $this->import($subPath, $subType, false, $path);
$subCollection = $this->import($subPath, $subType, LoaderInterface::ERROR_LEVEL_ALL, $path);
$collection->addCollection($subCollection);
}
}
Expand Down
Loading

0 comments on commit 5ab59f2

Please sign in to comment.