vendor/symfony/routing/Route.php line 20

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\Routing;
  11. /**
  12.  * A Route describes a route and its parameters.
  13.  *
  14.  * @author Fabien Potencier <fabien@symfony.com>
  15.  * @author Tobias Schultze <http://tobion.de>
  16.  */
  17. class Route implements \Serializable
  18. {
  19.     private $path '/';
  20.     private $host '';
  21.     private $schemes = [];
  22.     private $methods = [];
  23.     private $defaults = [];
  24.     private $requirements = [];
  25.     private $options = [];
  26.     private $condition '';
  27.     /**
  28.      * @var CompiledRoute|null
  29.      */
  30.     private $compiled;
  31.     /**
  32.      * Constructor.
  33.      *
  34.      * Available options:
  35.      *
  36.      *  * compiler_class: A class name able to compile this route instance (RouteCompiler by default)
  37.      *  * utf8:           Whether UTF-8 matching is enforced ot not
  38.      *
  39.      * @param string          $path         The path pattern to match
  40.      * @param array           $defaults     An array of default parameter values
  41.      * @param array           $requirements An array of requirements for parameters (regexes)
  42.      * @param array           $options      An array of options
  43.      * @param string|null     $host         The host pattern to match
  44.      * @param string|string[] $schemes      A required URI scheme or an array of restricted schemes
  45.      * @param string|string[] $methods      A required HTTP method or an array of restricted methods
  46.      * @param string|null     $condition    A condition that should evaluate to true for the route to match
  47.      */
  48.     public function __construct(string $path, array $defaults = [], array $requirements = [], array $options = [], ?string $host ''$schemes = [], $methods = [], ?string $condition '')
  49.     {
  50.         $this->setPath($path);
  51.         $this->addDefaults($defaults);
  52.         $this->addRequirements($requirements);
  53.         $this->setOptions($options);
  54.         $this->setHost($host);
  55.         $this->setSchemes($schemes);
  56.         $this->setMethods($methods);
  57.         $this->setCondition($condition);
  58.     }
  59.     public function __serialize(): array
  60.     {
  61.         return [
  62.             'path' => $this->path,
  63.             'host' => $this->host,
  64.             'defaults' => $this->defaults,
  65.             'requirements' => $this->requirements,
  66.             'options' => $this->options,
  67.             'schemes' => $this->schemes,
  68.             'methods' => $this->methods,
  69.             'condition' => $this->condition,
  70.             'compiled' => $this->compiled,
  71.         ];
  72.     }
  73.     /**
  74.      * @internal
  75.      */
  76.     final public function serialize(): string
  77.     {
  78.         return serialize($this->__serialize());
  79.     }
  80.     public function __unserialize(array $data): void
  81.     {
  82.         $this->path $data['path'];
  83.         $this->host $data['host'];
  84.         $this->defaults $data['defaults'];
  85.         $this->requirements $data['requirements'];
  86.         $this->options $data['options'];
  87.         $this->schemes $data['schemes'];
  88.         $this->methods $data['methods'];
  89.         if (isset($data['condition'])) {
  90.             $this->condition $data['condition'];
  91.         }
  92.         if (isset($data['compiled'])) {
  93.             $this->compiled $data['compiled'];
  94.         }
  95.     }
  96.     /**
  97.      * @internal
  98.      */
  99.     final public function unserialize($serialized)
  100.     {
  101.         $this->__unserialize(unserialize($serialized));
  102.     }
  103.     /**
  104.      * @return string
  105.      */
  106.     public function getPath()
  107.     {
  108.         return $this->path;
  109.     }
  110.     /**
  111.      * @return $this
  112.      */
  113.     public function setPath(string $pattern)
  114.     {
  115.         $pattern $this->extractInlineDefaultsAndRequirements($pattern);
  116.         // A pattern must start with a slash and must not have multiple slashes at the beginning because the
  117.         // generated path for this route would be confused with a network path, e.g. '//domain.com/path'.
  118.         $this->path '/'.ltrim(trim($pattern), '/');
  119.         $this->compiled null;
  120.         return $this;
  121.     }
  122.     /**
  123.      * @return string
  124.      */
  125.     public function getHost()
  126.     {
  127.         return $this->host;
  128.     }
  129.     /**
  130.      * @return $this
  131.      */
  132.     public function setHost(?string $pattern)
  133.     {
  134.         $this->host $this->extractInlineDefaultsAndRequirements((string) $pattern);
  135.         $this->compiled null;
  136.         return $this;
  137.     }
  138.     /**
  139.      * Returns the lowercased schemes this route is restricted to.
  140.      * So an empty array means that any scheme is allowed.
  141.      *
  142.      * @return string[]
  143.      */
  144.     public function getSchemes()
  145.     {
  146.         return $this->schemes;
  147.     }
  148.     /**
  149.      * Sets the schemes (e.g. 'https') this route is restricted to.
  150.      * So an empty array means that any scheme is allowed.
  151.      *
  152.      * @param string|string[] $schemes The scheme or an array of schemes
  153.      *
  154.      * @return $this
  155.      */
  156.     public function setSchemes($schemes)
  157.     {
  158.         $this->schemes array_map('strtolower', (array) $schemes);
  159.         $this->compiled null;
  160.         return $this;
  161.     }
  162.     /**
  163.      * Checks if a scheme requirement has been set.
  164.      *
  165.      * @return bool
  166.      */
  167.     public function hasScheme(string $scheme)
  168.     {
  169.         return \in_array(strtolower($scheme), $this->schemestrue);
  170.     }
  171.     /**
  172.      * Returns the uppercased HTTP methods this route is restricted to.
  173.      * So an empty array means that any method is allowed.
  174.      *
  175.      * @return string[]
  176.      */
  177.     public function getMethods()
  178.     {
  179.         return $this->methods;
  180.     }
  181.     /**
  182.      * Sets the HTTP methods (e.g. 'POST') this route is restricted to.
  183.      * So an empty array means that any method is allowed.
  184.      *
  185.      * @param string|string[] $methods The method or an array of methods
  186.      *
  187.      * @return $this
  188.      */
  189.     public function setMethods($methods)
  190.     {
  191.         $this->methods array_map('strtoupper', (array) $methods);
  192.         $this->compiled null;
  193.         return $this;
  194.     }
  195.     /**
  196.      * @return array
  197.      */
  198.     public function getOptions()
  199.     {
  200.         return $this->options;
  201.     }
  202.     /**
  203.      * @return $this
  204.      */
  205.     public function setOptions(array $options)
  206.     {
  207.         $this->options = [
  208.             'compiler_class' => 'Symfony\\Component\\Routing\\RouteCompiler',
  209.         ];
  210.         return $this->addOptions($options);
  211.     }
  212.     /**
  213.      * @return $this
  214.      */
  215.     public function addOptions(array $options)
  216.     {
  217.         foreach ($options as $name => $option) {
  218.             $this->options[$name] = $option;
  219.         }
  220.         $this->compiled null;
  221.         return $this;
  222.     }
  223.     /**
  224.      * Sets an option value.
  225.      *
  226.      * @param mixed $value The option value
  227.      *
  228.      * @return $this
  229.      */
  230.     public function setOption(string $name$value)
  231.     {
  232.         $this->options[$name] = $value;
  233.         $this->compiled null;
  234.         return $this;
  235.     }
  236.     /**
  237.      * Returns the option value or null when not found.
  238.      *
  239.      * @return mixed
  240.      */
  241.     public function getOption(string $name)
  242.     {
  243.         return $this->options[$name] ?? null;
  244.     }
  245.     /**
  246.      * @return bool
  247.      */
  248.     public function hasOption(string $name)
  249.     {
  250.         return \array_key_exists($name$this->options);
  251.     }
  252.     /**
  253.      * @return array
  254.      */
  255.     public function getDefaults()
  256.     {
  257.         return $this->defaults;
  258.     }
  259.     /**
  260.      * @return $this
  261.      */
  262.     public function setDefaults(array $defaults)
  263.     {
  264.         $this->defaults = [];
  265.         return $this->addDefaults($defaults);
  266.     }
  267.     /**
  268.      * @return $this
  269.      */
  270.     public function addDefaults(array $defaults)
  271.     {
  272.         if (isset($defaults['_locale']) && $this->isLocalized()) {
  273.             unset($defaults['_locale']);
  274.         }
  275.         foreach ($defaults as $name => $default) {
  276.             $this->defaults[$name] = $default;
  277.         }
  278.         $this->compiled null;
  279.         return $this;
  280.     }
  281.     /**
  282.      * @return mixed
  283.      */
  284.     public function getDefault(string $name)
  285.     {
  286.         return $this->defaults[$name] ?? null;
  287.     }
  288.     /**
  289.      * @return bool
  290.      */
  291.     public function hasDefault(string $name)
  292.     {
  293.         return \array_key_exists($name$this->defaults);
  294.     }
  295.     /**
  296.      * Sets a default value.
  297.      *
  298.      * @param mixed $default The default value
  299.      *
  300.      * @return $this
  301.      */
  302.     public function setDefault(string $name$default)
  303.     {
  304.         if ('_locale' === $name && $this->isLocalized()) {
  305.             return $this;
  306.         }
  307.         $this->defaults[$name] = $default;
  308.         $this->compiled null;
  309.         return $this;
  310.     }
  311.     /**
  312.      * @return array
  313.      */
  314.     public function getRequirements()
  315.     {
  316.         return $this->requirements;
  317.     }
  318.     /**
  319.      * @return $this
  320.      */
  321.     public function setRequirements(array $requirements)
  322.     {
  323.         $this->requirements = [];
  324.         return $this->addRequirements($requirements);
  325.     }
  326.     /**
  327.      * @return $this
  328.      */
  329.     public function addRequirements(array $requirements)
  330.     {
  331.         if (isset($requirements['_locale']) && $this->isLocalized()) {
  332.             unset($requirements['_locale']);
  333.         }
  334.         foreach ($requirements as $key => $regex) {
  335.             $this->requirements[$key] = $this->sanitizeRequirement($key$regex);
  336.         }
  337.         $this->compiled null;
  338.         return $this;
  339.     }
  340.     /**
  341.      * @return string|null
  342.      */
  343.     public function getRequirement(string $key)
  344.     {
  345.         return $this->requirements[$key] ?? null;
  346.     }
  347.     /**
  348.      * @return bool
  349.      */
  350.     public function hasRequirement(string $key)
  351.     {
  352.         return \array_key_exists($key$this->requirements);
  353.     }
  354.     /**
  355.      * @return $this
  356.      */
  357.     public function setRequirement(string $keystring $regex)
  358.     {
  359.         if ('_locale' === $key && $this->isLocalized()) {
  360.             return $this;
  361.         }
  362.         $this->requirements[$key] = $this->sanitizeRequirement($key$regex);
  363.         $this->compiled null;
  364.         return $this;
  365.     }
  366.     /**
  367.      * @return string
  368.      */
  369.     public function getCondition()
  370.     {
  371.         return $this->condition;
  372.     }
  373.     /**
  374.      * @return $this
  375.      */
  376.     public function setCondition(?string $condition)
  377.     {
  378.         $this->condition = (string) $condition;
  379.         $this->compiled null;
  380.         return $this;
  381.     }
  382.     /**
  383.      * Compiles the route.
  384.      *
  385.      * @return CompiledRoute
  386.      *
  387.      * @throws \LogicException If the Route cannot be compiled because the
  388.      *                         path or host pattern is invalid
  389.      *
  390.      * @see RouteCompiler which is responsible for the compilation process
  391.      */
  392.     public function compile()
  393.     {
  394.         if (null !== $this->compiled) {
  395.             return $this->compiled;
  396.         }
  397.         $class $this->getOption('compiler_class');
  398.         return $this->compiled $class::compile($this);
  399.     }
  400.     private function extractInlineDefaultsAndRequirements(string $pattern): string
  401.     {
  402.         if (false === strpbrk($pattern'?<')) {
  403.             return $pattern;
  404.         }
  405.         return preg_replace_callback('#\{(!?)(\w++)(<.*?>)?(\?[^\}]*+)?\}#', function ($m) {
  406.             if (isset($m[4][0])) {
  407.                 $this->setDefault($m[2], '?' !== $m[4] ? substr($m[4], 1) : null);
  408.             }
  409.             if (isset($m[3][0])) {
  410.                 $this->setRequirement($m[2], substr($m[3], 1, -1));
  411.             }
  412.             return '{'.$m[1].$m[2].'}';
  413.         }, $pattern);
  414.     }
  415.     private function sanitizeRequirement(string $keystring $regex)
  416.     {
  417.         if ('' !== $regex) {
  418.             if ('^' === $regex[0]) {
  419.                 $regex substr($regex1);
  420.             } elseif (=== strpos($regex'\\A')) {
  421.                 $regex substr($regex2);
  422.             }
  423.         }
  424.         if (str_ends_with($regex'$')) {
  425.             $regex substr($regex0, -1);
  426.         } elseif (\strlen($regex) - === strpos($regex'\\z')) {
  427.             $regex substr($regex0, -2);
  428.         }
  429.         if ('' === $regex) {
  430.             throw new \InvalidArgumentException(sprintf('Routing requirement for "%s" cannot be empty.'$key));
  431.         }
  432.         return $regex;
  433.     }
  434.     private function isLocalized(): bool
  435.     {
  436.         return isset($this->defaults['_locale']) && isset($this->defaults['_canonical_route']) && ($this->requirements['_locale'] ?? null) === preg_quote($this->defaults['_locale']);
  437.     }
  438. }