Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit d7a0679

Browse files
bug #35015 [Config] fix perf of glob discovery when GLOB_BRACE is not available (nicolas-grekas)
This PR was merged into the 4.4 branch. Discussion ---------- [Config] fix perf of glob discovery when GLOB_BRACE is not available | Q | A | ------------- | --- | Branch? | 4.4 | Bug fix? | no | New feature? | no | Deprecations? | no | Tickets | Fix #35009 | License | MIT | Doc PR | - This PR implements a fast fallback implementation of GLOB_BRACE for musl-based libc, as found on Alpine. It is *not* a feature-complete fallback implementation. Implementing one would be [much more involving](https://github.com/zendframework/zend-stdlib/blob/master/src/Glob.php). But the provided implementation is good enough in practice IMHO, and the slow path is still used when not-covered glob patterns are used. Here is the comparison: ![image](https://user-images.githubusercontent.com/243674/71022909-eb9f7000-2101-11ea-99f5-eab0286c77a3.png) ![image](https://user-images.githubusercontent.com/243674/71022899-e4786200-2101-11ea-8663-80c1674602db.png) Commits ------- 8af6d86 [Config] improve perf of glob discovery when GLOB_BRACE is not available
2 parents b450a2b + 8af6d86 commit d7a0679

File tree

2 files changed

+74
-2
lines changed

2 files changed

+74
-2
lines changed

src/Symfony/Component/Config/Resource/GlobResource.php

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ class GlobResource implements \IteratorAggregate, SelfCheckingResourceInterface
3131
private $hash;
3232
private $forExclusion;
3333
private $excludedPrefixes;
34+
private $globBrace;
3435

3536
/**
3637
* @param string $prefix A directory prefix
@@ -47,6 +48,7 @@ public function __construct(string $prefix, string $pattern, bool $recursive, bo
4748
$this->recursive = $recursive;
4849
$this->forExclusion = $forExclusion;
4950
$this->excludedPrefixes = $excludedPrefixes;
51+
$this->globBrace = \defined('GLOB_BRACE') ? GLOB_BRACE : 0;
5052

5153
if (false === $this->prefix) {
5254
throw new \InvalidArgumentException(sprintf('The path "%s" does not exist.', $prefix));
@@ -101,9 +103,20 @@ public function getIterator()
101103
return;
102104
}
103105
$prefix = str_replace('\\', '/', $this->prefix);
106+
$paths = null;
107+
108+
if (0 !== strpos($this->prefix, 'phar://') && false === strpos($this->pattern, '/**/')) {
109+
if ($this->globBrace || false === strpos($this->pattern, '{')) {
110+
$paths = glob($this->prefix.$this->pattern, GLOB_NOSORT | $this->globBrace);
111+
} elseif (false === strpos($this->pattern, '\\') || !preg_match('/\\\\[,{}]/', $this->pattern)) {
112+
foreach ($this->expandGlob($this->pattern) as $p) {
113+
$paths[] = glob($this->prefix.$p, GLOB_NOSORT);
114+
}
115+
$paths = array_merge(...$paths);
116+
}
117+
}
104118

105-
if (0 !== strpos($this->prefix, 'phar://') && false === strpos($this->pattern, '/**/') && (\defined('GLOB_BRACE') || false === strpos($this->pattern, '{'))) {
106-
$paths = glob($this->prefix.$this->pattern, GLOB_NOSORT | (\defined('GLOB_BRACE') ? GLOB_BRACE : 0));
119+
if (null !== $paths) {
107120
sort($paths);
108121
foreach ($paths as $path) {
109122
if ($this->excludedPrefixes) {
@@ -187,4 +200,34 @@ private function computeHash(): string
187200

188201
return hash_final($hash);
189202
}
203+
204+
private function expandGlob(string $pattern): array
205+
{
206+
$segments = preg_split('/\{([^{}]*+)\}/', $pattern, -1, PREG_SPLIT_DELIM_CAPTURE);
207+
$paths = [$segments[0]];
208+
$patterns = [];
209+
210+
for ($i = 1; $i < \count($segments); $i += 2) {
211+
$patterns = [];
212+
213+
foreach (explode(',', $segments[$i]) as $s) {
214+
foreach ($paths as $p) {
215+
$patterns[] = $p.$s.$segments[1 + $i];
216+
}
217+
}
218+
219+
$paths = $patterns;
220+
}
221+
222+
$j = 0;
223+
foreach ($patterns as $i => $p) {
224+
if (false !== strpos($p, '{')) {
225+
$p = $this->expandGlob($p);
226+
array_splice($paths, $i + $j, 1, $p);
227+
$j += \count($p) - 1;
228+
}
229+
}
230+
231+
return $paths;
232+
}
190233
}

src/Symfony/Component/Config/Tests/Resource/GlobResourceTest.php

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,4 +165,33 @@ public function testIsFreshRecursiveDetectsNewFile()
165165
touch($dir.'/Resource/TmpGlob');
166166
$this->assertFalse($resource->isFresh(0));
167167
}
168+
169+
public function testBraceFallback()
170+
{
171+
$dir = \dirname(__DIR__).\DIRECTORY_SEPARATOR.'Fixtures';
172+
$resource = new GlobResource($dir, '/*{/*/*.txt,.x{m,n}l}', true);
173+
174+
$p = new \ReflectionProperty($resource, 'globBrace');
175+
$p->setAccessible(true);
176+
$p->setValue($resource, 0);
177+
178+
$expected = [
179+
$dir.'/Exclude/ExcludeToo/AnotheExcludedFile.txt',
180+
$dir.'/foo.xml',
181+
];
182+
183+
$this->assertSame($expected, array_keys(iterator_to_array($resource)));
184+
}
185+
186+
public function testUnbalancedBraceFallback()
187+
{
188+
$dir = \dirname(__DIR__).\DIRECTORY_SEPARATOR.'Fixtures';
189+
$resource = new GlobResource($dir, '/*{/*/*.txt,.x{m,nl}', true);
190+
191+
$p = new \ReflectionProperty($resource, 'globBrace');
192+
$p->setAccessible(true);
193+
$p->setValue($resource, 0);
194+
195+
$this->assertSame([], array_keys(iterator_to_array($resource)));
196+
}
168197
}

0 commit comments

Comments
 (0)