8000 [Config] Fix resource tracking with new GlobResource · symfony/symfony@b9f7909 · GitHub
[go: up one dir, main page]

Skip to content

Commit b9f7909

Browse files
[Config] Fix resource tracking with new GlobResource
1 parent 85e2d2f commit b9f7909

File tree

11 files changed

+324
-73
lines changed

11 files changed

+324
-73
lines changed

src/Symfony/Component/Config/Exception/FileLocatorFileNotFoundException.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,17 @@
1818
*/
1919
class FileLocatorFileNotFoundException extends \InvalidArgumentException
2020
{
21+
private $paths;
22+
23+
public function __construct($message = '', $code = 0, $previous = null, array $paths = array())
24+
{
25+
parent::__construct($message, $code, $previous);
26+
27+
$this->paths = $paths;
28+
}
29+
30+
public function getPaths()
31+
{
32+
return $this->paths;
33+
}
2134
}

src/Symfony/Component/Config/FileLocator.php

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ public function locate($name, $currentPath = null, $first = true)
4343

4444
if ($this->isAbsolutePath($name)) {
4545
if (!file_exists($name)) {
46-
throw new FileLocatorFileNotFoundException(sprintf('The file "%s" does not exist.', $name));
46+
throw new FileLocatorFileNotFoundException(sprintf('The file "%s" does not exist.', $name), 0, null, array($name));
4747
}
4848

4949
return $name;
@@ -56,19 +56,21 @@ public function locate($name, $currentPath = null, $first = true)
5656
}
5757

5858
$paths = array_unique($paths);
59-
$filepaths = array();
59+
$filepaths = $notfound = array();
6060

6161
foreach ($paths as $path) {
6262
if (@file_exists($file = $path.DIRECTORY_SEPARATOR.$name)) {
6363
if (true === $first) {
6464
return $file;
6565
}
6666
$filepaths[] = $file;
67+
} else {
68+
$notfound[] = $file;
6769
}
6870
}
6971

7072
if (!$filepaths) {
71-
throw new FileLocatorFileNotFoundException(sprintf('The file "%s" does not exist (in: %s).', $name, implode(', ', $paths)));
73+
throw new FileLocatorFileNotFoundException(sprintf('The file "%s" does not exist (in: %s).', $name, implode(', ', $paths)), 0, null, $notfound);
7274
}
7375

7476
return $filepaths;

src/Symfony/Component/Config/Loader/FileLoader.php

Lines changed: 22 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@
1515
use Symfony\Component\Config\Exception\FileLoaderLoadException;
1616
use Symfony\Component\Config\Exception\FileLoaderImportCircularReferenceException;
1717
use Symfony\Component\Config\Exception\FileLocatorFileNotFoundException;
18+
use Symfony\Component\Config\Resource\FileExistenceResource;
19+
use Symfony\Component\Config\Resource\GlobResource;
20+
use Symfony\Component\Config\Resource\ResourceInterface;
1821
use Symfony\Component\Finder\Finder;
1922
use Symfony\Component\Finder\Glob;
2023

@@ -85,9 +88,13 @@ public function import($resource, $type = null, $ignoreErrors = false, $sourceRe
8588
{
8689
$ret = array();
8790
$ct = 0;
88-
foreach ($this->glob($resource, false, $_, $ignoreErrors) as $resource => $info) {
89-
++$ct;
91+
if (!is_string($resource) || false === strpbrk($resource, '*?{[')) {
9092
$ret[] = $this->doImport($resource, $type, $ignoreErrors, $sourceResource);
93+
} else {
94+
foreach ($this->glob($resource, false, $_, $ignoreErrors) as $path => $info) {
95+
++$ct;
96+
$ret[] = $this->doImport($path, $type, $ignoreErrors, $sourceResource);
97+
}
9198
}
9299

93100
return $ct > 1 ? $ret : (isset($ret[0]) ? $ret[0] : null);
@@ -96,24 +103,17 @@ public function import($resource, $type = null, $ignoreErrors = false, $sourceRe
96103
/**
97104
* @internal
98105
*/
99-
protected function glob($resource, $recursive, &$prefix = null, $ignoreErrors = false)
106+
protected function glob($pattern, $recursive, &$resource = null, $ignoreErrors = false)
100107
{
101-
if (strlen($resource) === $i = strcspn($resource, '*?{[')) {
102-
if (!$recursive) {
103-
$prefix = null;
104-
105-
yield $resource => new \SplFileInfo($resource);
106-
107-
return;
108-
}
109-
$prefix = $resource;
110-
$resource = '';
108+
if (strlen($pattern) === $i = strcspn($pattern, '*?{[')) {
109+
$prefix = $pattern;
110+
$pattern = '';
111111
} elseif (0 === $i) {
112112
$prefix = '.';
113-
$resource = '/'.$resource;
113+
$pattern = '/'.$pattern;
114114
} else {
115-
$prefix = dirname(substr($resource, 0, 1 + $i));
116-
$resource = substr($resource, strlen($prefix));
115+
$prefix = dirname(substr($pattern, 0, 1 + $i));
116+
$pattern = substr($pattern, strlen($prefix));
117117
}
118118

119119
try {
@@ -123,52 +123,17 @@ protected function glob($resource, $recursive, &$prefix = null, $ignoreErrors =
123123
throw $e;
124124
}
125125

126-
return;
127-
}
128-
$prefix = realpath($prefix) ?: $prefix;
129-
130-
if (false === strpos($resource, '/**/') && (defined('GLOB_BRACE') || false === strpos($resource, '{'))) {
131-
foreach (glob($prefix.$resource, defined('GLOB_BRACE') ? GLOB_BRACE : 0) as $path) {
132-
if ($recursive && is_dir($path)) {
133-
$files = iterator_to_array(new \RecursiveIteratorIterator(
134-
new \RecursiveCallbackFilterIterator(
135-
new \RecursiveDirectoryIterator($path, \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::FOLLOW_SYMLINKS),
136-
function (\SplFileInfo $file) { return '.' !== $file->getBasename()[0]; }
137-
),
138-
\RecursiveIteratorIterator::LEAVES_ONLY
139-
));
140-
uasort($files, function (\SplFileInfo $a, \SplFileInfo $b) {
141-
return (string) $a > (string) $b ? 1 : -1;
142-
});
143-
144-
foreach ($files as $path => $info) {
145-
if ($info->isFile()) {
146-
yield $path => $info;
147-
}
148-
}
149-
} elseif (is_file($path)) {
150-
yield $path => new \SplFileInfo($path);
151-
}
126+
$resource = array();
127+
foreach ($e->getPaths() as $path) {
128+
$resource[] = new FileExistenceResource($path);
152129
}
153130

154131
return;
155132
}
133+
$resource = new GlobResource($prefix, $pattern, $recursive);
156134

157-
if (!class_exists(Finder::class)) {
158-
throw new \LogicException(sprintf('Extended glob pattern "%s" cannot be used as the Finder component is not installed.', $resource));
159-
}
160-
161-
$finder = new Finder();
162-
$regex = Glob::toRegex($resource);
163-
if ($recursive) {
164-
$regex = substr_replace($regex, '(/|$)', -2, 1);
165-
}
166-
167-
$prefixLen = strlen($prefix);
168-
foreach ($finder->followLinks()->sortByName()->in($prefix) as $path => $info) {
169-
if (preg_match($regex, substr($path, $prefixLen)) && $info->isFile()) {
170-
yield $path => $info;
171-
}
135+
foreach ($resource as $path => $info) {
136+
yield $path => $info;
172137
}
173138
}
174139

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Config\Resource;
13+
14+
/**
15+
* GlobResource represents a set of resources stored on the filesystem.
16+
*
17+
* Only existence/removal is tracked (not mtimes.)
18+
*
19+
* @author Nicolas Grekas <p@tchwork.com>
20+
*/
21+
class GlobResource implements \IteratorAggregate, SelfCheckingResourceInterface, \Serializable
22+
{
23+
private $prefix;
24+
private $pattern;
25+
private $recursive;
26+
private $hash;
27+
28+
/**
29+
* Constructor.
30+
*
31+
* @param string $prefix A directory prefix
32+
* @param string $pattern A glob pattern
33+
* @param bool $recursive Whether directories should be scanned recursively or not
34+
*
35+
* @throws \InvalidArgumentException
36+
*/
37+
public function __construct($prefix, $pattern, $recursive)
38+
{
39+
$this->prefix = realpath($prefix) ?: (file_exists($prefix) ? $prefix : false);
40+
$this->pattern = $pattern;
41+
$this->recursive = $recursive;
42+
43+
if (false === $this->prefix) {
44+
throw new \InvalidArgumentException(sprintf('The path "%s" does not exist.', $prefix));
45+
}
46+
}
47+
48+
public function getPrefix()
49+
{
50+
return $this->prefix;
51+
}
52+
53+
/**
54+
* {@inheritdoc}
55+
*/
56+
public function __toString()
57+
{
58+
return 'glob.'.$this->prefix.$this->pattern.(int) $this->recursive;
59+
}
60+
61+
/**
62+
* {@inheritdoc}
63+
*/
64+
public function isFresh($timestamp)
65+
{
66+
$hash = $this->computeHash();
67+
68+
if (null === $this->hash) {
69+
$this->hash = $hash;
70+
}
71+
72+
return $this->hash === $hash;
73+
}
74+
75+
public function serialize()
76+
{
77+
if (null === $this->hash) {
78+
$this->hash = $this->computeHash();
79+
}
80+
81+
return serialize(array($this->prefix, $this->pattern, $this->recursive, $this->hash));
82+
}
83+
84+
public function unserialize($serialized)
85+
{
86+
list($this->prefix, $this->pattern, $this->recursive, $this->hash) = unserialize($serialized);
87+
}
88+
89+
public function getIterator()
90+
{
91+
if (!file_exists($this->prefix)) {
92+
return;
93+
}
94+
95+
if (!$this->recursive && '' === $this->pattern) {
96+
yield $this->prefix => new \SplFileInfo($this->prefix);
97+
98+
return;
99+
}
100+
101+
if (false === strpos($this->pattern, '/**/') && (defined('GLOB_BRACE') || false === strpos($this->pattern, '{'))) {
102+
foreach (glob($this->prefix.$this->pattern, defined('GLOB_BRACE') ? GLOB_BRACE : 0) as $path) {
103+
if ($this->recursive && is_dir($path)) {
104+
$files = iterator_to_array(new \RecursiveIteratorIterator(
105+
new \RecursiveCallbackFilterIterator(
106+
new \RecursiveDirectoryIterator($path, \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::FOLLOW_SYMLINKS),
107+
function (\SplFileInfo $file) { return '.' !== $file->getBasename()[0]; }
108+
),
109+
\RecursiveIteratorIterator::LEAVES_ONLY
110+
));
111+
uasort($files, function (\SplFileInfo $a, \SplFileInfo $b) {
112+
return (string) $a > (string) $b ? 1 : -1;
113+
});
114+
115+
foreach ($files as $path => $info) {
116+
if ($info->isFile()) {
117+
yield $path => $info;
118+
}
119+
}
120+
} elseif (is_file($path)) {
121+
yield $path => new \SplFileInfo($path);
122+
}
123+
}
124+
125+
return;
126+
}
127+
128+
if (!class_exists(Finder::class)) {
129+
throw new \LogicException(sprintf('Extended glob pattern "%s" cannot be used as the Finder component is not installed.', $this->pattern));
130+
}
131+
132+
$finder = new Finder();
133+
$regex = Glob::toRegex($this->pattern);
134+
if ($this->recursive) {
135+
$regex = substr_replace($regex, '(/|$)', -2, 1);
136+
}
137+
138+
$prefixLen = strlen($this->prefix);
139+
foreach ($finder->followLinks()->sortByName()->in($this->prefix) as $path => $info) {
140+
if (preg_match($regex, substr($path, $prefixLen)) && $info->isFile()) {
141+
yield $path => $info;
142+
}
143+
}
144+
}
145+
146+
private function computeHash()
147+
{
148+
$hash = hash_init('md5');
149+
150+
foreach ($this->getIterator() as $path => $info) {
151+
hash_update($hash, $path."\n");
152+
}
153+
154+
return hash_final($hash);
155+
}
156+
}

src/Symfony/Component/Config/Tests/Fixtures/Resource/.hiddenFile

Whitespace-only changes.

0 commit comments

Comments
 (0)
0