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