vendor/symfony/framework-bundle/Routing/Router.php line 65

  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\Bundle\FrameworkBundle\Routing;
  11. use Psr\Container\ContainerInterface;
  12. use Psr\Log\LoggerInterface;
  13. use Symfony\Component\Config\Loader\LoaderInterface;
  14. use Symfony\Component\Config\Resource\FileExistenceResource;
  15. use Symfony\Component\Config\Resource\FileResource;
  16. use Symfony\Component\DependencyInjection\Config\ContainerParametersResource;
  17. use Symfony\Component\DependencyInjection\ContainerInterface as SymfonyContainerInterface;
  18. use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException;
  19. use Symfony\Component\DependencyInjection\Exception\RuntimeException;
  20. use Symfony\Component\HttpKernel\CacheWarmer\WarmableInterface;
  21. use Symfony\Component\Routing\RequestContext;
  22. use Symfony\Component\Routing\RouteCollection;
  23. use Symfony\Component\Routing\Router as BaseRouter;
  24. use Symfony\Contracts\Service\ServiceSubscriberInterface;
  25. /**
  26.  * This Router creates the Loader only when the cache is empty.
  27.  *
  28.  * @author Fabien Potencier <fabien@symfony.com>
  29.  */
  30. class Router extends BaseRouter implements WarmableInterfaceServiceSubscriberInterface
  31. {
  32.     private ContainerInterface $container;
  33.     private array $collectedParameters = [];
  34.     private \Closure $paramFetcher;
  35.     /**
  36.      * @param mixed $resource The main resource to load
  37.      */
  38.     public function __construct(ContainerInterface $containermixed $resource, array $options = [], RequestContext $context nullContainerInterface $parameters nullLoggerInterface $logger nullstring $defaultLocale null)
  39.     {
  40.         $this->container $container;
  41.         $this->resource $resource;
  42.         $this->context $context ?? new RequestContext();
  43.         $this->logger $logger;
  44.         $this->setOptions($options);
  45.         if ($parameters) {
  46.             $this->paramFetcher $parameters->get(...);
  47.         } elseif ($container instanceof SymfonyContainerInterface) {
  48.             $this->paramFetcher $container->getParameter(...);
  49.         } else {
  50.             throw new \LogicException(sprintf('You should either pass a "%s" instance or provide the $parameters argument of the "%s" method.'SymfonyContainerInterface::class, __METHOD__));
  51.         }
  52.         $this->defaultLocale $defaultLocale;
  53.     }
  54.     public function getRouteCollection(): RouteCollection
  55.     {
  56.         if (null === $this->collection) {
  57.             $this->collection $this->container->get('routing.loader')->load($this->resource$this->options['resource_type']);
  58.             $this->resolveParameters($this->collection);
  59.             $this->collection->addResource(new ContainerParametersResource($this->collectedParameters));
  60.             try {
  61.                 $containerFile = ($this->paramFetcher)('kernel.cache_dir').'/'.($this->paramFetcher)('kernel.container_class').'.php';
  62.                 if (file_exists($containerFile)) {
  63.                     $this->collection->addResource(new FileResource($containerFile));
  64.                 } else {
  65.                     $this->collection->addResource(new FileExistenceResource($containerFile));
  66.                 }
  67.             } catch (ParameterNotFoundException) {
  68.             }
  69.         }
  70.         return $this->collection;
  71.     }
  72.     /**
  73.      * @return string[] A list of classes to preload on PHP 7.4+
  74.      */
  75.     public function warmUp(string $cacheDir): array
  76.     {
  77.         $currentDir $this->getOption('cache_dir');
  78.         // force cache generation
  79.         $this->setOption('cache_dir'$cacheDir);
  80.         $this->getMatcher();
  81.         $this->getGenerator();
  82.         $this->setOption('cache_dir'$currentDir);
  83.         return [
  84.             $this->getOption('generator_class'),
  85.             $this->getOption('matcher_class'),
  86.         ];
  87.     }
  88.     /**
  89.      * Replaces placeholders with service container parameter values in:
  90.      * - the route defaults,
  91.      * - the route requirements,
  92.      * - the route path,
  93.      * - the route host,
  94.      * - the route schemes,
  95.      * - the route methods.
  96.      */
  97.     private function resolveParameters(RouteCollection $collection)
  98.     {
  99.         foreach ($collection as $route) {
  100.             foreach ($route->getDefaults() as $name => $value) {
  101.                 $route->setDefault($name$this->resolve($value));
  102.             }
  103.             foreach ($route->getRequirements() as $name => $value) {
  104.                 $route->setRequirement($name$this->resolve($value));
  105.             }
  106.             $route->setPath($this->resolve($route->getPath()));
  107.             $route->setHost($this->resolve($route->getHost()));
  108.             $schemes = [];
  109.             foreach ($route->getSchemes() as $scheme) {
  110.                 $schemes[] = explode('|'$this->resolve($scheme));
  111.             }
  112.             $route->setSchemes(array_merge([], ...$schemes));
  113.             $methods = [];
  114.             foreach ($route->getMethods() as $method) {
  115.                 $methods[] = explode('|'$this->resolve($method));
  116.             }
  117.             $route->setMethods(array_merge([], ...$methods));
  118.             $route->setCondition($this->resolve($route->getCondition()));
  119.         }
  120.     }
  121.     /**
  122.      * Recursively replaces %placeholders% with the service container parameters.
  123.      *
  124.      * @throws ParameterNotFoundException When a placeholder does not exist as a container parameter
  125.      * @throws RuntimeException           When a container value is not a string or a numeric value
  126.      */
  127.     private function resolve(mixed $value): mixed
  128.     {
  129.         if (\is_array($value)) {
  130.             foreach ($value as $key => $val) {
  131.                 $value[$key] = $this->resolve($val);
  132.             }
  133.             return $value;
  134.         }
  135.         if (!\is_string($value)) {
  136.             return $value;
  137.         }
  138.         $escapedValue preg_replace_callback('/%%|%([^%\s]++)%/', function ($match) use ($value) {
  139.             // skip %%
  140.             if (!isset($match[1])) {
  141.                 return '%%';
  142.             }
  143.             if (preg_match('/^env\((?:\w++:)*+\w++\)$/'$match[1])) {
  144.                 throw new RuntimeException(sprintf('Using "%%%s%%" is not allowed in routing configuration.'$match[1]));
  145.             }
  146.             $resolved = ($this->paramFetcher)($match[1]);
  147.             if (\is_scalar($resolved)) {
  148.                 $this->collectedParameters[$match[1]] = $resolved;
  149.                 if (\is_string($resolved)) {
  150.                     $resolved $this->resolve($resolved);
  151.                 }
  152.                 if (\is_scalar($resolved)) {
  153.                     return false === $resolved '0' : (string) $resolved;
  154.                 }
  155.             }
  156.             throw new RuntimeException(sprintf('The container parameter "%s", used in the route configuration value "%s", must be a string or numeric, but it is of type "%s".'$match[1], $valueget_debug_type($resolved)));
  157.         }, $value);
  158.         return str_replace('%%''%'$escapedValue);
  159.     }
  160.     public static function getSubscribedServices(): array
  161.     {
  162.         return [
  163.             'routing.loader' => LoaderInterface::class,
  164.         ];
  165.     }
  166. }