vendor/symfony/config/Resource/GlobResource.php line 181

Open in your IDE?
  1. <?php
  2. /*
  3.  * This file is part of the Symfony package.
  4.  *
  5.  * (c) Fabien Potencier <fabien@symfony.com>
  6.  *
  7.  * For the full copyright and license information, please view the LICENSE
  8.  * file that was distributed with this source code.
  9.  */
  10. namespace Symfony\Component\Config\Resource;
  11. use Symfony\Component\Finder\Finder;
  12. use Symfony\Component\Finder\Glob;
  13. /**
  14.  * GlobResource represents a set of resources stored on the filesystem.
  15.  *
  16.  * Only existence/removal is tracked (not mtimes.)
  17.  *
  18.  * @author Nicolas Grekas <p@tchwork.com>
  19.  *
  20.  * @final
  21.  */
  22. class GlobResource implements \IteratorAggregateSelfCheckingResourceInterface
  23. {
  24.     private $prefix;
  25.     private $pattern;
  26.     private $recursive;
  27.     private $hash;
  28.     private $forExclusion;
  29.     private $excludedPrefixes;
  30.     private $globBrace;
  31.     /**
  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
  35.      *
  36.      * @throws \InvalidArgumentException
  37.      */
  38.     public function __construct(string $prefixstring $patternbool $recursivebool $forExclusion false, array $excludedPrefixes = [])
  39.     {
  40.         ksort($excludedPrefixes);
  41.         $this->prefix realpath($prefix) ?: (file_exists($prefix) ? $prefix false);
  42.         $this->pattern $pattern;
  43.         $this->recursive $recursive;
  44.         $this->forExclusion $forExclusion;
  45.         $this->excludedPrefixes $excludedPrefixes;
  46.         $this->globBrace \defined('GLOB_BRACE') ? \GLOB_BRACE 0;
  47.         if (false === $this->prefix) {
  48.             throw new \InvalidArgumentException(sprintf('The path "%s" does not exist.'$prefix));
  49.         }
  50.     }
  51.     public function getPrefix(): string
  52.     {
  53.         return $this->prefix;
  54.     }
  55.     /**
  56.      * {@inheritdoc}
  57.      */
  58.     public function __toString(): string
  59.     {
  60.         return 'glob.'.$this->prefix.(int) $this->recursive.$this->pattern.(int) $this->forExclusion.implode("\0"$this->excludedPrefixes);
  61.     }
  62.     /**
  63.      * {@inheritdoc}
  64.      */
  65.     public function isFresh(int $timestamp): bool
  66.     {
  67.         $hash $this->computeHash();
  68.         if (null === $this->hash) {
  69.             $this->hash $hash;
  70.         }
  71.         return $this->hash === $hash;
  72.     }
  73.     /**
  74.      * @internal
  75.      */
  76.     public function __sleep(): array
  77.     {
  78.         if (null === $this->hash) {
  79.             $this->hash $this->computeHash();
  80.         }
  81.         return ['prefix''pattern''recursive''hash''forExclusion''excludedPrefixes'];
  82.     }
  83.     /**
  84.      * @internal
  85.      */
  86.     public function __wakeup(): void
  87.     {
  88.         $this->globBrace \defined('GLOB_BRACE') ? \GLOB_BRACE 0;
  89.     }
  90.     public function getIterator(): \Traversable
  91.     {
  92.         if (!file_exists($this->prefix) || (!$this->recursive && '' === $this->pattern)) {
  93.             return;
  94.         }
  95.         $prefix str_replace('\\''/'$this->prefix);
  96.         $paths null;
  97.         if (!== strpos($this->prefix'phar://') && false === strpos($this->pattern'/**/')) {
  98.             if ($this->globBrace || false === strpos($this->pattern'{')) {
  99.                 $paths glob($this->prefix.$this->pattern\GLOB_NOSORT $this->globBrace);
  100.             } elseif (false === strpos($this->pattern'\\') || !preg_match('/\\\\[,{}]/'$this->pattern)) {
  101.                 foreach ($this->expandGlob($this->pattern) as $p) {
  102.                     $paths[] = glob($this->prefix.$p\GLOB_NOSORT);
  103.                 }
  104.                 $paths array_merge(...$paths);
  105.             }
  106.         }
  107.         if (null !== $paths) {
  108.             sort($paths);
  109.             foreach ($paths as $path) {
  110.                 if ($this->excludedPrefixes) {
  111.                     $normalizedPath str_replace('\\''/'$path);
  112.                     do {
  113.                         if (isset($this->excludedPrefixes[$dirPath $normalizedPath])) {
  114.                             continue 2;
  115.                         }
  116.                     } while ($prefix !== $dirPath && $dirPath !== $normalizedPath \dirname($dirPath));
  117.                 }
  118.                 if (is_file($path)) {
  119.                     yield $path => new \SplFileInfo($path);
  120.                 }
  121.                 if (!is_dir($path)) {
  122.                     continue;
  123.                 }
  124.                 if ($this->forExclusion) {
  125.                     yield $path => new \SplFileInfo($path);
  126.                     continue;
  127.                 }
  128.                 if (!$this->recursive || isset($this->excludedPrefixes[str_replace('\\''/'$path)])) {
  129.                     continue;
  130.                 }
  131.                 $files iterator_to_array(new \RecursiveIteratorIterator(
  132.                     new \RecursiveCallbackFilterIterator(
  133.                         new \RecursiveDirectoryIterator($path\FilesystemIterator::SKIP_DOTS \FilesystemIterator::FOLLOW_SYMLINKS),
  134.                         function (\SplFileInfo $file$path) {
  135.                             return !isset($this->excludedPrefixes[str_replace('\\''/'$path)]) && '.' !== $file->getBasename()[0];
  136.                         }
  137.                     ),
  138.                     \RecursiveIteratorIterator::LEAVES_ONLY
  139.                 ));
  140.                 uasort($files'strnatcmp');
  141.                 foreach ($files as $path => $info) {
  142.                     if ($info->isFile()) {
  143.                         yield $path => $info;
  144.                     }
  145.                 }
  146.             }
  147.             return;
  148.         }
  149.         if (!class_exists(Finder::class)) {
  150.             throw new \LogicException(sprintf('Extended glob pattern "%s" cannot be used as the Finder component is not installed.'$this->pattern));
  151.         }
  152.         $finder = new Finder();
  153.         $regex Glob::toRegex($this->pattern);
  154.         if ($this->recursive) {
  155.             $regex substr_replace($regex'(/|$)', -21);
  156.         }
  157.         $prefixLen \strlen($this->prefix);
  158.         foreach ($finder->followLinks()->sortByName()->in($this->prefix) as $path => $info) {
  159.             $normalizedPath str_replace('\\''/'$path);
  160.             if (!preg_match($regexsubstr($normalizedPath$prefixLen)) || !$info->isFile()) {
  161.                 continue;
  162.             }
  163.             if ($this->excludedPrefixes) {
  164.                 do {
  165.                     if (isset($this->excludedPrefixes[$dirPath $normalizedPath])) {
  166.                         continue 2;
  167.                     }
  168.                 } while ($prefix !== $dirPath && $dirPath !== $normalizedPath \dirname($dirPath));
  169.             }
  170.             yield $path => $info;
  171.         }
  172.     }
  173.     private function computeHash(): string
  174.     {
  175.         $hash hash_init('md5');
  176.         foreach ($this->getIterator() as $path => $info) {
  177.             hash_update($hash$path."\n");
  178.         }
  179.         return hash_final($hash);
  180.     }
  181.     private function expandGlob(string $pattern): array
  182.     {
  183.         $segments preg_split('/\{([^{}]*+)\}/'$pattern, -1\PREG_SPLIT_DELIM_CAPTURE);
  184.         $paths = [$segments[0]];
  185.         $patterns = [];
  186.         for ($i 1$i \count($segments); $i += 2) {
  187.             $patterns = [];
  188.             foreach (explode(','$segments[$i]) as $s) {
  189.                 foreach ($paths as $p) {
  190.                     $patterns[] = $p.$s.$segments[$i];
  191.                 }
  192.             }
  193.             $paths $patterns;
  194.         }
  195.         $j 0;
  196.         foreach ($patterns as $i => $p) {
  197.             if (false !== strpos($p'{')) {
  198.                 $p $this->expandGlob($p);
  199.                 array_splice($paths$i $j1$p);
  200.                 $j += \count($p) - 1;
  201.             }
  202.         }
  203.         return $paths;
  204.     }
  205. }