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

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 since Symfony 4.3
  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()
  52.     {
  53.         return $this->prefix;
  54.     }
  55.     /**
  56.      * {@inheritdoc}
  57.      */
  58.     public function __toString()
  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($timestamp)
  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.      * @return \Traversable
  85.      */
  86.     public function getIterator()
  87.     {
  88.         if (!file_exists($this->prefix) || (!$this->recursive && '' === $this->pattern)) {
  89.             return;
  90.         }
  91.         $prefix str_replace('\\''/'$this->prefix);
  92.         $paths null;
  93.         if (!== strpos($this->prefix'phar://') && false === strpos($this->pattern'/**/')) {
  94.             if ($this->globBrace || false === strpos($this->pattern'{')) {
  95.                 $paths glob($this->prefix.$this->patternGLOB_NOSORT $this->globBrace);
  96.             } elseif (false === strpos($this->pattern'\\') || !preg_match('/\\\\[,{}]/'$this->pattern)) {
  97.                 foreach ($this->expandGlob($this->pattern) as $p) {
  98.                     $paths[] = glob($this->prefix.$pGLOB_NOSORT);
  99.                 }
  100.                 $paths array_merge(...$paths);
  101.             }
  102.         }
  103.         if (null !== $paths) {
  104.             sort($paths);
  105.             foreach ($paths as $path) {
  106.                 if ($this->excludedPrefixes) {
  107.                     $normalizedPath str_replace('\\''/'$path);
  108.                     do {
  109.                         if (isset($this->excludedPrefixes[$dirPath $normalizedPath])) {
  110.                             continue 2;
  111.                         }
  112.                     } while ($prefix !== $dirPath && $dirPath !== $normalizedPath \dirname($dirPath));
  113.                 }
  114.                 if (is_file($path)) {
  115.                     yield $path => new \SplFileInfo($path);
  116.                 }
  117.                 if (!is_dir($path)) {
  118.                     continue;
  119.                 }
  120.                 if ($this->forExclusion) {
  121.                     yield $path => new \SplFileInfo($path);
  122.                     continue;
  123.                 }
  124.                 if (!$this->recursive || isset($this->excludedPrefixes[str_replace('\\''/'$path)])) {
  125.                     continue;
  126.                 }
  127.                 $files iterator_to_array(new \RecursiveIteratorIterator(
  128.                     new \RecursiveCallbackFilterIterator(
  129.                         new \RecursiveDirectoryIterator($path\FilesystemIterator::SKIP_DOTS \FilesystemIterator::FOLLOW_SYMLINKS),
  130.                         function (\SplFileInfo $file$path) {
  131.                             return !isset($this->excludedPrefixes[str_replace('\\''/'$path)]) && '.' !== $file->getBasename()[0];
  132.                         }
  133.                     ),
  134.                     \RecursiveIteratorIterator::LEAVES_ONLY
  135.                 ));
  136.                 uasort($files'strnatcmp');
  137.                 foreach ($files as $path => $info) {
  138.                     if ($info->isFile()) {
  139.                         yield $path => $info;
  140.                     }
  141.                 }
  142.             }
  143.             return;
  144.         }
  145.         if (!class_exists(Finder::class)) {
  146.             throw new \LogicException(sprintf('Extended glob pattern "%s" cannot be used as the Finder component is not installed.'$this->pattern));
  147.         }
  148.         $finder = new Finder();
  149.         $regex Glob::toRegex($this->pattern);
  150.         if ($this->recursive) {
  151.             $regex substr_replace($regex'(/|$)', -21);
  152.         }
  153.         $prefixLen \strlen($this->prefix);
  154.         foreach ($finder->followLinks()->sortByName()->in($this->prefix) as $path => $info) {
  155.             $normalizedPath str_replace('\\''/'$path);
  156.             if (!preg_match($regexsubstr($normalizedPath$prefixLen)) || !$info->isFile()) {
  157.                 continue;
  158.             }
  159.             if ($this->excludedPrefixes) {
  160.                 do {
  161.                     if (isset($this->excludedPrefixes[$dirPath $normalizedPath])) {
  162.                         continue 2;
  163.                     }
  164.                 } while ($prefix !== $dirPath && $dirPath !== $normalizedPath \dirname($dirPath));
  165.             }
  166.             yield $path => $info;
  167.         }
  168.     }
  169.     private function computeHash(): string
  170.     {
  171.         $hash hash_init('md5');
  172.         foreach ($this->getIterator() as $path => $info) {
  173.             hash_update($hash$path."\n");
  174.         }
  175.         return hash_final($hash);
  176.     }
  177.     private function expandGlob(string $pattern): array
  178.     {
  179.         $segments preg_split('/\{([^{}]*+)\}/'$pattern, -1PREG_SPLIT_DELIM_CAPTURE);
  180.         $paths = [$segments[0]];
  181.         $patterns = [];
  182.         for ($i 1$i \count($segments); $i += 2) {
  183.             $patterns = [];
  184.             foreach (explode(','$segments[$i]) as $s) {
  185.                 foreach ($paths as $p) {
  186.                     $patterns[] = $p.$s.$segments[$i];
  187.                 }
  188.             }
  189.             $paths $patterns;
  190.         }
  191.         $j 0;
  192.         foreach ($patterns as $i => $p) {
  193.             if (false !== strpos($p'{')) {
  194.                 $p $this->expandGlob($p);
  195.                 array_splice($paths$i $j1$p);
  196.                 $j += \count($p) - 1;
  197.             }
  198.         }
  199.         return $paths;
  200.     }
  201. }