[DependencyInjection] Use glob pattern to load config files #21270

Merged
merged 1 commit into from Feb 14, 2017
@pierredup
Contributor
pierredup commented Jan 13, 2017 edited
Q A
Branch? master
Bug fix? no
New feature? yes
BC breaks? no
Deprecations? no
Tests pass? yes
Fixed tickets #21173
License MIT
Doc PR -

This relates to #21173, but I'm not sure if it completely fixes the issue.

This allows to use a glob pattern to load config files, which makes the following possible:

# config.yml
imports:
    - { resource: "*.yml" }
    - { resource: "folder/*.yml" }
    - { resource: "/etc/myapp/*.{yml,xml}" }

It can also be used in a container extension, if a bundle uses a lot of configs:

$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config'));
$loader->load('*.yml');
$loader->load('routing/*');
@@ -42,11 +42,15 @@ public function locate($name, $currentPath = null, $first = true)
}
if ($this->isAbsolutePath($name)) {
- if (!file_exists($name)) {
+ if (array() === $files = glob($name, GLOB_BRACE)) {
@nicolas-grekas
nicolas-grekas Jan 14, 2017 Member

missing check for GLOB_BRACE, because it's is not always defined (see php.net/glob )

@pierredup
pierredup Jan 14, 2017 Contributor

Check added

+ };
+
+ foreach ($paths as $path) {
+ if (array() !== $files = glob($path.DIRECTORY_SEPARATOR.$name)) {
@nicolas-grekas
nicolas-grekas Jan 14, 2017 Member

no BLOG_BRACE here?

@pierredup
pierredup Jan 14, 2017 Contributor

GLOB_BRACE added with a defined check

+ if (array() !== $files = glob($path.DIRECTORY_SEPARATOR.$name)) {
+ array_walk($files, $load);
+ } else {
+ $load($path.DIRECTORY_SEPARATOR.$name);
@nicolas-grekas
nicolas-grekas Jan 14, 2017 Member

Does this actually do something? Looks like "glob" failed to find any file, so why would "file_exists" (in $load) return something else that "false"? If I'm right, then I'd prefer this array_walk + $load to be replaced by a simple foreach (with no array() !== ... check). But I may miss something, that's why I'm asking :)

@nicolas-grekas
nicolas-grekas Jan 14, 2017 Member

(if it's legit, we should be sure it has a test case btw)

@pierredup
pierredup Jan 14, 2017 Contributor

I agree that this doesn't do anything.
Originally I wanted to preserve any existing functionality, but the glob should cover all current use cases.

throw new FileLocatorFileNotFoundException(sprintf('The file "%s" does not exist.', $name));
}
- return $name;
+ if ($first) {
@GuilhemN
GuilhemN Jan 14, 2017 Contributor

Wdyt about adding || 1 === count($files) to keep the return the same as now?

@pierredup
pierredup Jan 15, 2017 Contributor

Actually I think we should keep the original behavior as it was (just invert the file_exists check), then add the glob as an extra step.
Otherwise if you do something like /etc/myapp/*.yml, you would expect an array, so it might be weird to get a string if the glob only matches a single file

@pierredup
pierredup Jan 15, 2017 Contributor

Changes made

- if (true === $first) {
- return $file;
- }
+ foreach (glob($path.DIRECTORY_SEPARATOR.$name, defined('GLOB_BRACE') ? GLOB_BRACE : 0) as $file) {
@GuilhemN
GuilhemN Jan 14, 2017 Contributor

array_merge?

@pierredup
pierredup Jan 15, 2017 Contributor

Done

throw new FileLocatorFileNotFoundException(sprintf('The file "%s" does not exist.', $name));
}
- return $name;
+ if (true === $first) {
@ro0NL
ro0NL Jan 15, 2017 Contributor

Could be qualified a bugfix for 2.7 i guess, $first seems to be ignored previously πŸ˜•

@pierredup
pierredup Jan 15, 2017 Contributor

This would always have returned only one result, so the check for $first wasn't necessary. But the glob could return more than one result, so the the check needed to be added

throw new FileLocatorFileNotFoundException(sprintf('The file "%s" does not exist.', $name));
}
- return $name;
+ if (true === $first) {
+ return array_shift($files);
@ro0NL
ro0NL Jan 15, 2017 Contributor

I think return reset($files) is a faster operation..

@pierredup
pierredup Jan 15, 2017 Contributor

array_shift is used more commonly across the rest of the code base when doing a return. I don't think it makes much of a difference either way

- }
- $filepaths[] = $file;
- }
+ $filepaths = array_merge($filepaths, glob($path.DIRECTORY_SEPARATOR.$name, defined('GLOB_BRACE') ? GLOB_BRACE : 0));
@ro0NL
ro0NL Jan 15, 2017 Contributor

I'd prefer a quick return if $first=true and the first file is found.

@pierredup
pierredup Jan 15, 2017 Contributor

Early return added

+ $this->import($resource, isset($import['type']) ? $import['type'] : null, isset($import['ignore_errors']) ? (bool) $import['ignore_errors'] : false, $file);
+ }
+ } else {
+ $this->import($import['resource'], isset($import['type']) ? $import['type'] : null, isset($import['ignore_errors']) ? (bool) $import['ignore_errors'] : false, $file);
@nicolas-grekas
nicolas-grekas Jan 15, 2017 edited Member

same question as FileLocator: is this "else" dead code or live code (same on the XmlFileLoader?)

@pierredup
pierredup Jan 15, 2017 Contributor

I need to rethink this part a bit, because without this, you don't get the exception if a file doesn't exist

+
+ if (array() !== $files = glob($resourceFile, defined('GLOB_BRACE') ? GLOB_BRACE : 0)) {
+ foreach ($files as $resource) {
+ $this->import($resource, XmlUtils::phpize($import->getAttribute('type')) ?: null, (bool) XmlUtils::phpize($import->getAttribute('ignore-errors')), $file);
@sstok
sstok Jan 15, 2017 Contributor

The value of 'type' and 'ignore-errors' will not change within this loop, so it's better to resolve them before the loop, and then reuse the value.

@pierredup
pierredup Jan 15, 2017 Contributor

Variables added for the two values

@@ -106,7 +106,21 @@ 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);
+
+ $resourceFile = $import->getAttribute('resource');
@nicolas-grekas
nicolas-grekas Jan 15, 2017 Member

Two notes here:

  • tests still pass if I revert this change on XmlFileLoader on my setup
  • I think this logic should be handled at the FileLoader level: I don't see why both Xml&Yaml loaders should care about that. They should be untouched to me.
@@ -34,4 +34,15 @@ public function __construct(ContainerBuilder $container, FileLocatorInterface $l
parent::__construct($locator);
}
+
+ protected function isAbsolutePath($file)
@nicolas-grekas
nicolas-grekas Jan 15, 2017 Member

this looks like leaking logic from the locator, looks suspicious to me

@nicolas-grekas
Member

While working on using glob for another use case in #21289, I found some similarities with this PR.
Here is what I did there. It looks like this PR could do almost exactly the same:

  • let the Config component untouched
  • move the glob call to FileLoader in the DI component

Before calling glob, the "resource" should be parsed for glob-specific characters. The constant part of the "resource" should be the part that is given the locator. Then, FileLoader could use glob on the result, with the glob tail appended.

In case this is not clear enough, this logic is implemented here:
https://github.com/symfony/symfony/pull/21289/files#diff-ad1ed76aba6a80df5a48dfa4585adcf3R61

@nicolas-grekas nicolas-grekas added this to the 3.3 milestone Jan 16, 2017
+ foreach ($files as $file) {
+ if (is_dir($file)) {
+ $this->container->addResource(new DirectoryResource($file, '/^$/'));
+ parent::import($file, 'directory', $ignoreErrors, $sourceResource);
@pierredup
pierredup Jan 17, 2017 Contributor

Directories should be handle by the DirectoryLoader class, otherwise we get an exception here.
Maybe we can deprecate the DirectoryLoader class in favor of using a glob?

@pierredup
Contributor

@nicolas-grekas I have made the suggested changes, please have a look if that is what you had in mind.

For me the downside of keeping this in the DI component and out of the Config component, is that you loose the ability to load multiple configs in a container extension, E.G

$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config'));
$loader->load('*.yml');
+ foreach ($files as $file) {
+ if (is_dir($file)) {
+ $this->container->addResource(new DirectoryResource($file, '/^$/'));
+ parent::import($file, 'directory', $ignoreErrors, $sourceResource);
@pierredup
pierredup Jan 17, 2017 Contributor

Directories should be handle by the DirectoryLoader class, otherwise we get an exception here.
Maybe we can deprecate the DirectoryLoader class in favor of using a glob?

@nicolas-grekas
nicolas-grekas Jan 31, 2017 Member

I think it's ok as is, no need to deprecate yet, that wouldn't provide much benefit, while forcing everyone to upgrade, isn't it?

@pierredup
Contributor

The other issue with not changing the FileLocator::locatemethod, is that you now always need to have a directory prefix. You can't just load, for example, something like *.yml ordev_*.yml, and use whatever the paths is set in the locator

@pierredup
Contributor

The following patch fixes the above mentioned issues, wdyt about applying this changes?

 src/Symfony/Component/Config/FileLocator.php                   | 10 ++++++----
 .../Component/DependencyInjection/Loader/FileLoader.php        |  4 +++-
 2 files changed, 9 insertions(+), 5 deletions(-)

diff --git a/src/Symfony/Component/Config/FileLocator.php b/src/Symfony/Component/Config/FileLocator.php
index 6b686390bc..09288b4bde 100644
--- a/src/Symfony/Component/Config/FileLocator.php
+++ b/src/Symfony/Component/Config/FileLocator.php
@@ -59,11 +59,13 @@ class FileLocator implements FileLocatorInterface
         $filepaths = array();

         foreach ($paths as $path) {
-            if (@file_exists($file = $path.DIRECTORY_SEPARATOR.$name)) {
-                if (true === $first) {
-                    return $file;
+            foreach(glob($path.DIRECTORY_SEPARATOR.$name, defined('GLOB_BRACE') ? GLOB_BRACE : 0) as $file) {
+                if (@file_exists($file)) {
+                    if (true === $first) {
+                        return $file;
+                    }
+                    $filepaths[] = $file;
                 }
-                $filepaths[] = $file;
             }
         }

diff --git a/src/Symfony/Component/DependencyInjection/Loader/FileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/FileLoader.php
index 46e89ab1e9..983cfbc5db 100644
--- a/src/Symfony/Component/DependencyInjection/Loader/FileLoader.php
+++ b/src/Symfony/Component/DependencyInjection/Loader/FileLoader.php
@@ -54,7 +54,9 @@ abstract class FileLoader extends BaseFileLoader
      */
     public function import($resource, $type = null, $ignoreErrors = false, $sourceResource = null)
     {
-        if (strlen($resource) === $i = strcspn($resource, '*?{[')) {
+        $i = strcspn($resource, '*?{[');
+
+        if (strlen($resource) === $i || 0 === $i || false === strpos($resource, '/')) {
             $directoryPrefix = $resource;
             $directoryGlob = '';
         } else {
@nicolas-grekas

The current code looks good to me (with small comment).
I don't think FileLocator needs to be changed. Making it handle glob breaks it seeking logic to me. Better use the locator for the prefix, the glob inside the located result.

@@ -14,6 +14,9 @@
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Config\Loader\FileLoader as BaseFileLoader;
use Symfony\Component\Config\FileLocatorInterface;
+use Symfony\Component\Config\Exception\FileLoaderLoadException;
@nicolas-grekas
nicolas-grekas Jan 31, 2017 Member

should be moved before FileLoader (we add new entries in alpha order, but keep existing ones asis)

+
+ foreach ($files as $file) {
+ if (is_dir($file)) {
+ $this->container->addResource(new DirectoryResource($file, '/^$/'));
@nicolas-grekas
nicolas-grekas Jan 31, 2017 Member

This misses tracking new/removed files in $directoryPrefix.
I think it's better to track at the prefix level as done at https://github.com/symfony/symfony/pull/21289/files#diff-ad1ed76aba6a80df5a48dfa4585adcf3R74:
$this->container->addResource(new DirectoryResource($directoryPrefix, '/^$/'));

+ foreach ($files as $file) {
+ if (is_dir($file)) {
+ $this->container->addResource(new DirectoryResource($file, '/^$/'));
+ parent::import($file, 'directory', $ignoreErrors, $sourceResource);
@nicolas-grekas
nicolas-grekas Jan 31, 2017 Member

I think it's ok as is, no need to deprecate yet, that wouldn't provide much benefit, while forcing everyone to upgrade, isn't it?

+ $this->container->addResource(new DirectoryResource($file, '/^$/'));
+ parent::import($file, 'directory', $ignoreErrors, $sourceResource);
+ } else {
+ $this->container->addResource(new FileResource($file));
@nicolas-grekas
nicolas-grekas Jan 31, 2017 Member

resource tracking shouldn't be required here, import already does it (by calling load)

@nicolas-grekas
nicolas-grekas Feb 3, 2017 Member

Now that we have #21408, resource tracking should be done using the new $container->fileExists() method, both here and above instead of Directory/FileResource.

@nicolas-grekas nicolas-grekas added to In Progress in Lower entry bar Feb 3, 2017
@nicolas-grekas
Member

Status: needs work

@pierredup
Contributor

@nicolas-grekas Changes made. I still think it's important to load config without the need of a directory prefix, but I'll work separately on that. If I can get something to work, I'll open a new PR

@pierredup
Contributor

Status: Needs Review

@nicolas-grekas
Member

Sure, when there is no prefix, we should just use ./

@pierredup
Contributor

That might not be intuitive for users, so it should probably then just be documented

@nicolas-grekas
Member
nicolas-grekas commented Feb 8, 2017 edited

here is a fix - a separate test case would be better indeed

diff --git a/src/Symfony/Component/DependencyInjection/Loader/FileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/FileLoader.php
index 555831b..0f642dc 100644
--- a/src/Symfony/Component/DependencyInjection/Loader/FileLoader.php
+++ b/src/Symfony/Component/DependencyInjection/Loader/FileLoader.php
@@ -55,6 +55,9 @@ abstract class FileLoader extends BaseFileLoader
         if (strlen($resource) === $i = strcspn($resource, '*?{[')) {
             $directoryPrefix = $resource;
             $directoryGlob = '';
+        } elseif (0 === $i) {
+            $directoryPrefix = '.';
+            $directoryGlob = '/'.$resource;
         } else {
             $directoryPrefix = dirname(substr($resource, 0, 1 + $i));
             $directoryGlob = substr($resource, strlen($directoryPrefix));
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/FileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/FileLoaderTest.php
index ad875ba..8853eb6 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Loader/FileLoaderTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/FileLoaderTest.php
@@ -43,7 +43,7 @@ class FileLoaderTest extends \PHPUnit_Framework_TestCase
         ));
 
         $loader->setResolver($resolver);
-        $loader->import('Fixtures/{xml,yaml}/services2.{yml,xml}');
+        $loader->import('{F}ixtures/{xml,yaml}/services2.{yml,xml}');
 
         $actual = $container->getParameterBag()->all();
         $expected = array(
@@ -24,6 +25,17 @@
{
protected $container;
+ private $currentDir;
@nicolas-grekas
nicolas-grekas Feb 8, 2017 Member

can be made protected, and setCurrentDir be removed IMO (also required in my other PR)

+ throw $e;
+ }
+
+ throw new FileLoaderLoadException($resource, $sourceResource);
@nicolas-grekas
nicolas-grekas Feb 8, 2017 Member

throw new FileLoaderLoadException($resource, $sourceResource, null, $e);

+ foreach ($files as $file) {
+ parent::import($file, is_dir($file) ? 'directory' : $type, $ignoreErrors, $sourceResource);
+ }
+ } catch (\Exception $e) {
@nicolas-grekas
nicolas-grekas Feb 8, 2017 edited Member

is this catch block required? looks like not to me

@fabpot
Member
fabpot commented Feb 12, 2017

Coding standard should be fixed (cf. fabbot)

@nicolas-grekas
Member

I suggest waiting for #21289 before working on this PR - it will need a rebase and share code (new glob method)

@nicolas-grekas
Member

rebase unlocked, you can now use the new "glob" function:
https://github.com/symfony/symfony/blob/master/src/Symfony/Component/DependencyInjection/Loader/FileLoader.php#L95

If you happen to chosse to use "true" as second argument, then I invite you to remove this argument altogether because then it's the only value used in the code base (I tend to think we should do that.)

@nicolas-grekas
Member
nicolas-grekas commented Feb 13, 2017 edited

Thinking again about this one, I'd now say it should not handle directories at all - only files (meaning $recursive is required :) )

@pierredup
Contributor

Rebased and updated to use the new glob method.

I'd now say it should not handle directories at all - only files

Does that mean I also don't need to change the $type to directory when a path is a directory? Or is that part still fine?

@nicolas-grekas
Member

I think that part should be removed yes: just skip directories.

@pierredup
Contributor

It seems like directory loading can't be skipped, it makes the current tests fail. So I think either $recursive then needs to be true, or we need to pass directory as the type if the path is a directory

@nicolas-grekas
Member

This should do it, WDYT?

diff --git a/src/Symfony/Component/DependencyInjection/Loader/FileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/FileLoader.php
index 794e242..41c9da6 100644
--- a/src/Symfony/Component/DependencyInjection/Loader/FileLoader.php
+++ b/src/Symfony/Component/DependencyInjection/Loader/FileLoader.php
@@ -90,7 +90,7 @@ abstract class FileLoader extends BaseFileLoader
         $extRegexp = defined('HHVM_VERSION') ? '/\\.(?:php|hh)$/' : '/\\.php$/';
 
         foreach ($this->glob($resource, true, $prefixLen) as $path => $info) {
-            if (!preg_match($extRegexp, $path, $m) || !$info->isFile() || !$info->isReadable()) {
+            if (!preg_match($extRegexp, $path, $m) || !$info->isReadable()) {
                 continue;
             }
             $class = $namespace.ltrim(str_replace('/', '\\', substr($path, $prefixLen, -strlen($m[0]))), '\\');
@@ -112,6 +112,11 @@ abstract class FileLoader extends BaseFileLoader
     private function glob($resource, $recursive, &$prefixLen = null)
     {
         if (strlen($resource) === $i = strcspn($resource, '*?{[')) {
+            if (!$recursive) {
+                yield $resource => new \SplFileInfo($resource);
+
+                return;
+            }
             $resourcePrefix = $resource;
             $resource = '';
         } elseif (0 === $i) {
@@ -134,9 +139,11 @@ abstract class FileLoader extends BaseFileLoader
                 if ($recursive && is_dir($path)) {
                     $flags = \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::FOLLOW_SYMLINKS;
                     foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($path, $flags)) as $path => $info) {
-                        yield $path => $info;
+                        if ($info->isFile()) {
+                            yield $path => $info;
+                        }
                     }
-                } else {
+                } elseif (is_file($path)) {
                     yield $path => new \SplFileInfo($path);
                 }
             }
@@ -155,7 +162,7 @@ abstract class FileLoader extends BaseFileLoader
         }
 
         foreach ($finder->followLinks()->in($resourcePrefix) as $path => $info) {
-            if (preg_match($regex, substr($path, $prefixLen))) {
+            if (preg_match($regex, substr($path, $prefixLen)) && $info->isFile()) {
                 yield $path => $info;
             }
         }
+ foreach ($this->glob($resource, false) as $path => $info) {
+ parent::import($path, $type, $ignoreErrors, $sourceResource);
+ }
+ } catch (FileLocatorFileNotFoundException $exception) {
@nicolas-grekas
nicolas-grekas Feb 13, 2017 Member

Should be named $e for consistency with the code base

@pierredup pierredup changed the title from [Config] [DependencyInjection] Use glob pattern to load config files to [DependencyInjection] Use glob pattern to load config files Feb 14, 2017
@pierredup pierredup Use glob pattern to load config file
519180e
@pierredup
Contributor

Changes made and tests passing again

@nicolas-grekas

πŸ‘

@nicolas-grekas nicolas-grekas moved from In Progress to In Review in Lower entry bar Feb 14, 2017
@fabpot
Member
fabpot commented Feb 14, 2017

Thank you @pierredup.

@fabpot fabpot merged commit 519180e into symfony:master Feb 14, 2017

3 checks passed

continuous-integration/appveyor/pr AppVeyor build succeeded
Details
continuous-integration/travis-ci/pr The Travis CI build passed
Details
fabbot.io Your code looks good.
Details
@fabpot fabpot added a commit that referenced this pull request Feb 14, 2017
@fabpot fabpot feature #21270 [DependencyInjection] Use glob pattern to load config …
…files (pierredup)

This PR was merged into the 3.3-dev branch.

Discussion
----------

[DependencyInjection] Use glob pattern to load config files

| Q             | A
| ------------- | ---
| Branch?       | master
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #21173
| License       | MIT
| Doc PR        | -

This relates to #21173, but I'm not sure if it completely fixes the issue.

This allows to use a glob pattern to load config files, which makes the following possible:

```
# config.yml
imports:
    - { resource: "*.yml" }
    - { resource: "folder/*.yml" }
    - { resource: "/etc/myapp/*.{yml,xml}" }
```

It can also be used in a container extension, if a bundle uses a lot of configs:

```
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config'));
$loader->load('*.yml');
$loader->load('routing/*');
```

Commits
-------

519180e Use glob pattern to load config file
00d20ea
@pierredup pierredup deleted the pierredup:config-glob branch Feb 14, 2017
@nicolas-grekas nicolas-grekas moved from In Review to Done in Lower entry bar Feb 17, 2017
@fabpot fabpot added a commit that referenced this pull request Feb 18, 2017
@fabpot fabpot feature #21635 added support for glob loaders in Config (fabpot)
This PR was merged into the 3.3-dev branch.

Discussion
----------

added support for glob loaders in Config

| Q             | A
| ------------- | ---
| Branch?       | master
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | n/a
| License       | MIT
| Doc PR        | not yet

In #21270, we added the possibility to use glob patterns to import (not load) config files, but it was restricted to the container. The same feature could be useful (and I actually have a use case) for the routing.

So, this PR moves the logic to the Config component. It also adds a new `GlobFileLoader` class that allows to load glob patterns (not just import them as in #21270).

Last, but not least, the new glob file loader is registered in both the routing and the container default loaders.

Here is a simple, but powerful example, using the Symfony micro kernel (actually, this is a snippet from the Kernel used in Symfony Flex :)):

```php
<?php

namespace Symfony\Flex;

use Symfony\Component\HttpKernel\Kernel as BaseKernel;

class Kernel extends BaseKernel
{
    use MicroKernelTrait;

    const CONFIG_EXTS = '.{php,xml,yaml,yml}';

    // ...

    protected function configureContainer(ContainerBuilder $container, LoaderInterface $loader)
    {
        $confDir = dirname($this->getRootDir()).'/etc';
        $loader->import($confDir.'/packages/*'.self::CONFIG_EXTS, 'glob');
        $loader->import($confDir.'/packages/'.$this->getEnvironment().'/**/*'.self::CONFIG_EXTS, 'glob');
        $loader->import($confDir.'/container'.self::CONFIG_EXTS, 'glob');
    }

    protected function configureRoutes(RouteCollectionBuilder $routes)
    {
        $confDir = dirname($this->getRootDir()).'/etc';
        $routes->import($confDir.'/routing/*'.self::CONFIG_EXTS, '/', 'glob');
        $routes->import($confDir.'/routing/'.$this->getEnvironment().'/**/*'.self::CONFIG_EXTS, '/', 'glob');
        $routes->import($confDir.'/routing'.self::CONFIG_EXTS, '/', 'glob');
    }
}
```

Commits
-------

025585d added support for glob loaders in Config
5a6850b
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment