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

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