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

Skip to content

Commit bf4c5fb

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

File tree

11 files changed

+320
-75
lines changed

11 files changed

+320
-75
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: 21 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@
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\Finder\Finder;
19-
use Symfony\Component\Finder\Glob;
18+
use Symfony\Component\Config\Resource\FileExistenceResource;
19+
use Symfony\Component\Config\Resource\GlobResource;
2020

2121
/**
2222
* FileLoader is the abstract class used by all built-in loaders that are file based.
@@ -85,9 +85,13 @@ public function import($resource, $type = null, $ignoreErrors = false, $sourceRe
8585
{
8686
$ret = array();
8787
$ct = 0;
88-
foreach ($this->glob($resource, false, $_, $ignoreErrors) as $resource => $info) {
89-
++$ct;
88+
if (!is_string($resource) || false === strpbrk($resource, '*?{[')) {
9089
$ret[] = $this->doImport($resource, $type, $ignoreErrors, $sourceResource);
90+
} else {
91+
foreach ($this->glob($resource, false, $_, $ignoreErrors) as $path => $info) {
92+
++$ct;
93+
$ret[] = $this->doImport($path, $type, $ignoreErrors, $sourceResource);
94+
}
9195
}
9296

9397
return $ct > 1 ? $ret : (isset($ret[0]) ? $ret[0] : null);
@@ -96,24 +100,17 @@ public function import($resource, $type = null, $ignoreErrors = false, $sourceRe
96100
/**
97101
* @internal
98102
*/
99-
protected function glob($resource, $recursive, &$prefix = null, $ignoreErrors = false)
103+
protected function glob($pattern, $recursive, &$resource = null, $ignoreErrors = false)
100104
{
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 = '';
105+
if (strlen($pattern) === $i = strcspn($pattern, '*?{[')) {
106+
$prefix = $pattern;
107+
$pattern = '';
111108
} elseif (0 === $i) {
112109
$prefix = '.';
113-
$resource = '/'.$resource;
110+
$pattern = '/'.$pattern;
114111
} else {
115-
$prefix = dirname(substr($resource, 0, 1 + $i));
116-
$resource = substr($resource, strlen($prefix));
112+
$prefix = dirname(substr($pattern, 0, 1 + $i));
113+
$pattern = substr($pattern, strlen($prefix));
117114
}
118115

119116
try {
@@ -123,52 +120,17 @@ protected function glob($resource, $recursive, &$prefix = null, $ignoreErrors =
123120
throw $e;
124121
}
125122

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 => 6D4E $info;
147-
}
148-
}
149-
} elseif (is_file($path)) {
150-
yield $path => new \SplFileInfo($path);
151-
}
123+
$resource = array();
124+
foreach ($e->getPaths() as $path) {
125+
$resource[] = new FileExistenceResource($path);
152126
}
153127

154128
return;
155129
}
130+
$resource = new GlobResource($prefix, $pattern, $recursive);
156131

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-
}
132+
foreach ($resource as $path => $info) {
133+
yield $path => $info;
172134
}
173135
}
174136

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

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

Whitespace-only changes.

0 commit comments

Comments
 (0)
0