vendor/symfony/routing/Loader/Psr4DirectoryLoader.php line 39

  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\Routing\Loader;
  11. use Symfony\Component\Config\FileLocatorInterface;
  12. use Symfony\Component\Config\Loader\DirectoryAwareLoaderInterface;
  13. use Symfony\Component\Config\Loader\Loader;
  14. use Symfony\Component\Config\Resource\DirectoryResource;
  15. use Symfony\Component\Routing\RouteCollection;
  16. /**
  17.  * A loader that discovers controller classes in a directory that follows PSR-4.
  18.  *
  19.  * @author Alexander M. Turek <me@derrabus.de>
  20.  */
  21. final class Psr4DirectoryLoader extends Loader implements DirectoryAwareLoaderInterface
  22. {
  23.     private ?string $currentDirectory null;
  24.     public function __construct(
  25.         private readonly FileLocatorInterface $locator,
  26.     ) {
  27.         // PSR-4 directory loader has no env-aware logic, so we drop the $env constructor parameter.
  28.         parent::__construct();
  29.     }
  30.     /**
  31.      * @param array{path: string, namespace: string} $resource
  32.      */
  33.     public function load(mixed $resourcestring $type null): ?RouteCollection
  34.     {
  35.         $path $this->locator->locate($resource['path'], $this->currentDirectory);
  36.         if (!is_dir($path)) {
  37.             return new RouteCollection();
  38.         }
  39.         return $this->loadFromDirectory($pathtrim($resource['namespace'], '\\'));
  40.     }
  41.     public function supports(mixed $resourcestring $type null): bool
  42.     {
  43.         return ('attribute' === $type || 'annotation' === $type) && \is_array($resource) && isset($resource['path'], $resource['namespace']);
  44.     }
  45.     public function forDirectory(string $currentDirectory): static
  46.     {
  47.         $loader = clone $this;
  48.         $loader->currentDirectory $currentDirectory;
  49.         return $loader;
  50.     }
  51.     private function loadFromDirectory(string $directorystring $psr4Prefix): RouteCollection
  52.     {
  53.         $collection = new RouteCollection();
  54.         $collection->addResource(new DirectoryResource($directory'/\.php$/'));
  55.         $files iterator_to_array(new \RecursiveIteratorIterator(
  56.             new \RecursiveCallbackFilterIterator(
  57.                 new \RecursiveDirectoryIterator($directory\FilesystemIterator::SKIP_DOTS \FilesystemIterator::FOLLOW_SYMLINKS),
  58.                 function (\SplFileInfo $current) {
  59.                     return !str_starts_with($current->getBasename(), '.');
  60.                 }
  61.             ),
  62.             \RecursiveIteratorIterator::SELF_FIRST
  63.         ));
  64.         usort($files, function (\SplFileInfo $a\SplFileInfo $b) {
  65.             return (string) $a > (string) $b : -1;
  66.         });
  67.         /** @var \SplFileInfo $file */
  68.         foreach ($files as $file) {
  69.             if ($file->isDir()) {
  70.                 $collection->addCollection($this->loadFromDirectory($file->getPathname(), $psr4Prefix.'\\'.$file->getFilename()));
  71.                 continue;
  72.             }
  73.             if ('php' !== $file->getExtension() || !class_exists($className $psr4Prefix.'\\'.$file->getBasename('.php')) || (new \ReflectionClass($className))->isAbstract()) {
  74.                 continue;
  75.             }
  76.             $collection->addCollection($this->import($className'attribute'));
  77.         }
  78.         return $collection;
  79.     }
  80. }