4 * This file is part of the Symfony package.
6 * (c) Fabien Potencier <fabien@symfony.com>
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
12 namespace Symfony\Component\Config\Resource;
14 use Symfony\Component\Finder\Finder;
15 use Symfony\Component\Finder\Glob;
18 * GlobResource represents a set of resources stored on the filesystem.
20 * Only existence/removal is tracked (not mtimes.)
22 * @author Nicolas Grekas <p@tchwork.com>
24 class GlobResource implements \IteratorAggregate, SelfCheckingResourceInterface, \Serializable
32 * @param string $prefix A directory prefix
33 * @param string $pattern A glob pattern
34 * @param bool $recursive Whether directories should be scanned recursively or not
36 * @throws \InvalidArgumentException
38 public function __construct($prefix, $pattern, $recursive)
40 $this->prefix = realpath($prefix) ?: (file_exists($prefix) ? $prefix : false);
41 $this->pattern = $pattern;
42 $this->recursive = $recursive;
44 if (false === $this->prefix) {
45 throw new \InvalidArgumentException(sprintf('The path "%s" does not exist.', $prefix));
49 public function getPrefix()
57 public function __toString()
59 return 'glob.'.$this->prefix.$this->pattern.(int) $this->recursive;
65 public function isFresh($timestamp)
67 $hash = $this->computeHash();
69 if (null === $this->hash) {
73 return $this->hash === $hash;
76 public function serialize()
78 if (null === $this->hash) {
79 $this->hash = $this->computeHash();
82 return serialize(array($this->prefix, $this->pattern, $this->recursive, $this->hash));
85 public function unserialize($serialized)
87 list($this->prefix, $this->pattern, $this->recursive, $this->hash) = unserialize($serialized);
90 public function getIterator()
92 if (!file_exists($this->prefix) || (!$this->recursive && '' === $this->pattern)) {
96 if (false === strpos($this->pattern, '/**/') && (defined('GLOB_BRACE') || false === strpos($this->pattern, '{'))) {
97 foreach (glob($this->prefix.$this->pattern, defined('GLOB_BRACE') ? GLOB_BRACE : 0) as $path) {
98 if ($this->recursive && is_dir($path)) {
99 $files = iterator_to_array(new \RecursiveIteratorIterator(
100 new \RecursiveCallbackFilterIterator(
101 new \RecursiveDirectoryIterator($path, \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::FOLLOW_SYMLINKS),
102 function (\SplFileInfo $file) { return '.' !== $file->getBasename()[0]; }
104 \RecursiveIteratorIterator::LEAVES_ONLY
106 uasort($files, function (\SplFileInfo $a, \SplFileInfo $b) {
107 return (string) $a > (string) $b ? 1 : -1;
110 foreach ($files as $path => $info) {
111 if ($info->isFile()) {
112 yield $path => $info;
115 } elseif (is_file($path)) {
116 yield $path => new \SplFileInfo($path);
123 if (!class_exists(Finder::class)) {
124 throw new \LogicException(sprintf('Extended glob pattern "%s" cannot be used as the Finder component is not installed.', $this->pattern));
127 $finder = new Finder();
128 $regex = Glob::toRegex($this->pattern);
129 if ($this->recursive) {
130 $regex = substr_replace($regex, '(/|$)', -2, 1);
133 $prefixLen = strlen($this->prefix);
134 foreach ($finder->followLinks()->sortByName()->in($this->prefix) as $path => $info) {
135 if (preg_match($regex, substr('\\' === \DIRECTORY_SEPARATOR ? str_replace('\\', '/', $path) : $path, $prefixLen)) && $info->isFile()) {
136 yield $path => $info;
141 private function computeHash()
143 $hash = hash_init('md5');
145 foreach ($this->getIterator() as $path => $info) {
146 hash_update($hash, $path."\n");
149 return hash_final($hash);