feature #31587 [Routing][Config] Allow patterns of resources to be excluded from config loading (tristanbes)
This PR was merged into the 4.4 branch.
Discussion
----------
[Routing][Config] Allow patterns of resources to be excluded from config loading
| Q | A
| ------------- | ---
| Branch? | master
| Bug fix? | no
| New feature? | yes
| BC breaks? | no
| Deprecations? | yes
| Tests pass? | yes
| Fixed tickets | #31516
| License | MIT
| Doc PR | not yet
The PR will fix the following RFC: #31516
Like resource loading for services, this PR offers a way to exclude patterns of resources like:
```yml
// config/routes/annotations.yaml
controllers:
resource: ../../src/Controller/*
type: annotation
exclude: '../src/Controller/{DebugEmailController}.php'
```
All the annotation routes inside `Controller/` will be loaded in this example except all the one present inside the `Controller/DebugEmailController.php`
Commits
-------
332ff8811c
[Routing][Config] Allow patterns of resources to be excluded from config loading
This commit is contained in:
commit
13dd18c8a6
|
@ -19,6 +19,11 @@ Debug
|
|||
* Deprecated the `FlattenException` class, use the one from the `ErrorRenderer` component instead
|
||||
* Deprecated the component in favor of the `ErrorHandler` component
|
||||
|
||||
Config
|
||||
------
|
||||
|
||||
* Deprecated overriding the `FilerLoader::import()` method without declaring the optional `$exclude` argument
|
||||
|
||||
DependencyInjection
|
||||
-------------------
|
||||
|
||||
|
|
|
@ -27,6 +27,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 `FilerLoader::import()` method has a new `$exclude` argument.
|
||||
|
||||
Console
|
||||
-------
|
||||
|
@ -390,6 +391,7 @@ Routing
|
|||
with the new serialization methods in PHP 7.4.
|
||||
* Removed `ServiceRouterLoader` and `ObjectRouteLoader`.
|
||||
* Service route loaders must be tagged with `routing.route_loader`.
|
||||
* The `RoutingConfigurator::import()` method has a new optional `$exclude` argument.
|
||||
|
||||
Security
|
||||
--------
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
CHANGELOG
|
||||
=========
|
||||
|
||||
4.4.0
|
||||
-----
|
||||
|
||||
* added a way to exclude patterns of resources from being imported by the `import()` method
|
||||
|
||||
4.3.0
|
||||
-----
|
||||
|
||||
|
|
|
@ -59,10 +59,11 @@ abstract class FileLoader extends Loader
|
|||
/**
|
||||
* Imports a resource.
|
||||
*
|
||||
* @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 string|null $sourceResource The original resource importing the new resource
|
||||
* @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 string|null $sourceResource The original resource importing the new resource
|
||||
* @param string|string[]|null $exclude Glob patterns to exclude from the import
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
|
@ -70,12 +71,25 @@ abstract class FileLoader extends Loader
|
|||
* @throws FileLoaderImportCircularReferenceException
|
||||
* @throws FileLocatorFileNotFoundException
|
||||
*/
|
||||
public function import($resource, $type = null, $ignoreErrors = false, $sourceResource = null)
|
||||
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) {
|
||||
@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;
|
||||
|
||||
if (\is_string($resource) && \strlen($resource) !== $i = strcspn($resource, '*?{[')) {
|
||||
$excluded = [];
|
||||
foreach ((array) $exclude as $pattern) {
|
||||
foreach ($this->glob($pattern, true, $_, false, true) as $path => $info) {
|
||||
// normalize Windows slashes
|
||||
$excluded[str_replace('\\', '/', $path)] = true;
|
||||
}
|
||||
}
|
||||
|
||||
$ret = [];
|
||||
$isSubpath = 0 !== $i && false !== strpos(substr($resource, 0, $i), '/');
|
||||
foreach ($this->glob($resource, false, $_, $ignoreErrors || !$isSubpath) as $path => $info) {
|
||||
foreach ($this->glob($resource, false, $_, $ignoreErrors || !$isSubpath, false, $excluded) as $path => $info) {
|
||||
if (null !== $res = $this->doImport($path, $type, $ignoreErrors, $sourceResource)) {
|
||||
$ret[] = $res;
|
||||
}
|
||||
|
|
|
@ -92,6 +92,14 @@ class FileLoaderTest extends TestCase
|
|||
|
||||
$this->assertSame(__FILE__, strtr($loader->import('FileLoaderTest.*'), '/', \DIRECTORY_SEPARATOR));
|
||||
}
|
||||
|
||||
public function testImportWithExclude()
|
||||
{
|
||||
$loader = new TestFileLoader(new FileLocator(__DIR__.'/../Fixtures'));
|
||||
$loadedFiles = $loader->import('Include/*', null, false, null, __DIR__.'/../Fixtures/Include/{ExcludeFile.txt}');
|
||||
$this->assertCount(2, $loadedFiles);
|
||||
$this->assertNotContains('ExcludeFile.txt', $loadedFiles);
|
||||
}
|
||||
}
|
||||
|
||||
class TestFileLoader extends FileLoader
|
||||
|
|
|
@ -38,7 +38,7 @@ class GlobFileLoaderTest extends TestCase
|
|||
|
||||
class GlobFileLoaderWithoutImport extends GlobFileLoader
|
||||
{
|
||||
public function import($resource, $type = null, $ignoreErrors = false, $sourceResource = null)
|
||||
public function import($resource, $type = null, $ignoreErrors = false, $sourceResource = null, $exclude = null)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ CHANGELOG
|
|||
|
||||
* Deprecated `ServiceRouterLoader` in favor of `ContainerLoader`.
|
||||
* Deprecated `ObjectRouteLoader` in favor of `ObjectLoader`.
|
||||
* Added a way to exclude patterns of resources from being imported by the `import()` method
|
||||
|
||||
4.3.0
|
||||
-----
|
||||
|
|
|
@ -33,11 +33,14 @@ class RoutingConfigurator
|
|||
$this->file = $file;
|
||||
}
|
||||
|
||||
final public function import($resource, string $type = null, bool $ignoreErrors = false): ImportConfigurator
|
||||
/**
|
||||
* @param string|string[]|null $exclude Glob patterns to exclude from the import
|
||||
*/
|
||||
final public function import($resource, string $type = null, bool $ignoreErrors = false, $exclude = null): ImportConfigurator
|
||||
{
|
||||
$this->loader->setCurrentDir(\dirname($this->path));
|
||||
$imported = $this->loader->import($resource, $type, $ignoreErrors, $this->file) ?: [];
|
||||
|
||||
$imported = $this->loader->import($resource, $type, $ignoreErrors, $this->file, $exclude) ?: [];
|
||||
if (!\is_array($imported)) {
|
||||
return new ImportConfigurator($this->collection, $imported);
|
||||
}
|
||||
|
|
|
@ -163,10 +163,24 @@ class XmlFileLoader extends FileLoader
|
|||
throw new \InvalidArgumentException(sprintf('The <route> element in file "%s" must not have both a "prefix" attribute and <prefix> child nodes.', $path));
|
||||
}
|
||||
|
||||
$exclude = [];
|
||||
foreach ($node->childNodes as $child) {
|
||||
if ($child instanceof \DOMElement && $child->localName === $exclude && self::NAMESPACE_URI === $child->namespaceURI) {
|
||||
$exclude[] = $child->nodeValue;
|
||||
}
|
||||
}
|
||||
|
||||
if ($node->hasAttribute('exclude')) {
|
||||
if ($exclude) {
|
||||
throw new \InvalidArgumentException('You cannot use both the attribute "exclude" and <exclude> tags at the same time.');
|
||||
}
|
||||
$exclude = [$node->getAttribute('exclude')];
|
||||
}
|
||||
|
||||
$this->setCurrentDir(\dirname($path));
|
||||
|
||||
/** @var RouteCollection[] $imported */
|
||||
$imported = $this->import($resource, ('' !== $type ? $type : null), false, $file) ?: [];
|
||||
$imported = $this->import($resource, ('' !== $type ? $type : null), false, $file, $exclude) ?: [];
|
||||
|
||||
if (!\is_array($imported)) {
|
||||
$imported = [$imported];
|
||||
|
|
|
@ -28,7 +28,7 @@ use Symfony\Component\Yaml\Yaml;
|
|||
class YamlFileLoader extends FileLoader
|
||||
{
|
||||
private static $availableKeys = [
|
||||
'resource', 'type', 'prefix', 'path', 'host', 'schemes', 'methods', 'defaults', 'requirements', 'options', 'condition', 'controller', 'name_prefix', 'trailing_slash_on_root', 'locale', 'format', 'utf8',
|
||||
'resource', 'type', 'prefix', 'path', 'host', 'schemes', 'methods', 'defaults', 'requirements', 'options', 'condition', 'controller', 'name_prefix', 'trailing_slash_on_root', 'locale', 'format', 'utf8', 'exclude',
|
||||
];
|
||||
private $yamlParser;
|
||||
|
||||
|
@ -169,6 +169,7 @@ class YamlFileLoader extends FileLoader
|
|||
$schemes = isset($config['schemes']) ? $config['schemes'] : null;
|
||||
$methods = isset($config['methods']) ? $config['methods'] : null;
|
||||
$trailingSlashOnRoot = $config['trailing_slash_on_root'] ?? true;
|
||||
$exclude = $config['exclude'] ?? null;
|
||||
|
||||
if (isset($config['controller'])) {
|
||||
$defaults['_controller'] = $config['controller'];
|
||||
|
@ -185,7 +186,7 @@ class YamlFileLoader extends FileLoader
|
|||
|
||||
$this->setCurrentDir(\dirname($path));
|
||||
|
||||
$imported = $this->import($config['resource'], $type, false, $file) ?: [];
|
||||
$imported = $this->import($config['resource'], $type, false, $file, $exclude) ?: [];
|
||||
|
||||
if (!\is_array($imported)) {
|
||||
$imported = [$imported];
|
||||
|
|
|
@ -61,9 +61,11 @@
|
|||
<xsd:sequence maxOccurs="unbounded" minOccurs="0">
|
||||
<xsd:group ref="configs" minOccurs="0" maxOccurs="unbounded" />
|
||||
<xsd:element name="prefix" type="localized-path" minOccurs="0" maxOccurs="unbounded" />
|
||||
<xsd:element name="exclude" type="xsd:string" minOccurs="0" maxOccurs="unbounded" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="resource" type="xsd:string" use="required" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="exclude" type="xsd:string" />
|
||||
<xsd:attribute name="prefix" type="xsd:string" />
|
||||
<xsd:attribute name="name-prefix" type="xsd:string" />
|
||||
<xsd:attribute name="host" type="xsd:string" />
|
||||
|
|
|
@ -38,7 +38,7 @@ class GlobFileLoaderTest extends TestCase
|
|||
|
||||
class GlobFileLoaderWithoutImport extends GlobFileLoader
|
||||
{
|
||||
public function import($resource, $type = null, $ignoreErrors = false, $sourceResource = null)
|
||||
public function import($resource, $type = null, $ignoreErrors = false, $sourceResource = null, $exclude = null)
|
||||
{
|
||||
return new RouteCollection();
|
||||
}
|
||||
|
|
Reference in New Issue