Chris@0: Chris@0: * Chris@0: * For the full copyright and license information, please view the LICENSE Chris@0: * file that was distributed with this source code. Chris@0: */ Chris@0: Chris@0: namespace Symfony\Component\Routing; Chris@0: Chris@0: /** Chris@0: * A Route describes a route and its parameters. Chris@0: * Chris@0: * @author Fabien Potencier Chris@0: * @author Tobias Schultze Chris@0: */ Chris@0: class Route implements \Serializable Chris@0: { Chris@0: private $path = '/'; Chris@0: private $host = ''; Chris@17: private $schemes = []; Chris@17: private $methods = []; Chris@17: private $defaults = []; Chris@17: private $requirements = []; Chris@17: private $options = []; Chris@14: private $condition = ''; Chris@0: Chris@0: /** Chris@17: * @var CompiledRoute|null Chris@0: */ Chris@0: private $compiled; Chris@0: Chris@0: /** Chris@0: * Constructor. Chris@0: * Chris@0: * Available options: Chris@0: * Chris@0: * * compiler_class: A class name able to compile this route instance (RouteCompiler by default) Chris@0: * * utf8: Whether UTF-8 matching is enforced ot not Chris@0: * Chris@14: * @param string $path The path pattern to match Chris@14: * @param array $defaults An array of default parameter values Chris@14: * @param array $requirements An array of requirements for parameters (regexes) Chris@14: * @param array $options An array of options Chris@14: * @param string $host The host pattern to match Chris@14: * @param string|string[] $schemes A required URI scheme or an array of restricted schemes Chris@14: * @param string|string[] $methods A required HTTP method or an array of restricted methods Chris@14: * @param string $condition A condition that should evaluate to true for the route to match Chris@0: */ Chris@17: public function __construct($path, array $defaults = [], array $requirements = [], array $options = [], $host = '', $schemes = [], $methods = [], $condition = '') Chris@0: { Chris@0: $this->setPath($path); Chris@0: $this->setDefaults($defaults); Chris@0: $this->setRequirements($requirements); Chris@0: $this->setOptions($options); Chris@0: $this->setHost($host); Chris@0: $this->setSchemes($schemes); Chris@0: $this->setMethods($methods); Chris@0: $this->setCondition($condition); Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function serialize() Chris@0: { Chris@17: return serialize([ Chris@0: 'path' => $this->path, Chris@0: 'host' => $this->host, Chris@0: 'defaults' => $this->defaults, Chris@0: 'requirements' => $this->requirements, Chris@0: 'options' => $this->options, Chris@0: 'schemes' => $this->schemes, Chris@0: 'methods' => $this->methods, Chris@0: 'condition' => $this->condition, Chris@0: 'compiled' => $this->compiled, Chris@17: ]); Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function unserialize($serialized) Chris@0: { Chris@0: $data = unserialize($serialized); Chris@0: $this->path = $data['path']; Chris@0: $this->host = $data['host']; Chris@0: $this->defaults = $data['defaults']; Chris@0: $this->requirements = $data['requirements']; Chris@0: $this->options = $data['options']; Chris@0: $this->schemes = $data['schemes']; Chris@0: $this->methods = $data['methods']; Chris@0: Chris@0: if (isset($data['condition'])) { Chris@0: $this->condition = $data['condition']; Chris@0: } Chris@0: if (isset($data['compiled'])) { Chris@0: $this->compiled = $data['compiled']; Chris@0: } Chris@0: } Chris@0: Chris@0: /** Chris@0: * Returns the pattern for the path. Chris@0: * Chris@0: * @return string The path pattern Chris@0: */ Chris@0: public function getPath() Chris@0: { Chris@0: return $this->path; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Sets the pattern for the path. Chris@0: * Chris@0: * This method implements a fluent interface. Chris@0: * Chris@0: * @param string $pattern The path pattern Chris@0: * Chris@0: * @return $this Chris@0: */ Chris@0: public function setPath($pattern) Chris@0: { Chris@0: // A pattern must start with a slash and must not have multiple slashes at the beginning because the Chris@0: // generated path for this route would be confused with a network path, e.g. '//domain.com/path'. Chris@0: $this->path = '/'.ltrim(trim($pattern), '/'); Chris@0: $this->compiled = null; Chris@0: Chris@0: return $this; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Returns the pattern for the host. Chris@0: * Chris@0: * @return string The host pattern Chris@0: */ Chris@0: public function getHost() Chris@0: { Chris@0: return $this->host; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Sets the pattern for the host. Chris@0: * Chris@0: * This method implements a fluent interface. Chris@0: * Chris@0: * @param string $pattern The host pattern Chris@0: * Chris@0: * @return $this Chris@0: */ Chris@0: public function setHost($pattern) Chris@0: { Chris@0: $this->host = (string) $pattern; Chris@0: $this->compiled = null; Chris@0: Chris@0: return $this; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Returns the lowercased schemes this route is restricted to. Chris@0: * So an empty array means that any scheme is allowed. Chris@0: * Chris@14: * @return string[] The schemes Chris@0: */ Chris@0: public function getSchemes() Chris@0: { Chris@0: return $this->schemes; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Sets the schemes (e.g. 'https') this route is restricted to. Chris@0: * So an empty array means that any scheme is allowed. Chris@0: * Chris@0: * This method implements a fluent interface. Chris@0: * Chris@14: * @param string|string[] $schemes The scheme or an array of schemes Chris@0: * Chris@0: * @return $this Chris@0: */ Chris@0: public function setSchemes($schemes) Chris@0: { Chris@0: $this->schemes = array_map('strtolower', (array) $schemes); Chris@0: $this->compiled = null; Chris@0: Chris@0: return $this; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Checks if a scheme requirement has been set. Chris@0: * Chris@0: * @param string $scheme Chris@0: * Chris@0: * @return bool true if the scheme requirement exists, otherwise false Chris@0: */ Chris@0: public function hasScheme($scheme) Chris@0: { Chris@17: return \in_array(strtolower($scheme), $this->schemes, true); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Returns the uppercased HTTP methods this route is restricted to. Chris@0: * So an empty array means that any method is allowed. Chris@0: * Chris@14: * @return string[] The methods Chris@0: */ Chris@0: public function getMethods() Chris@0: { Chris@0: return $this->methods; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Sets the HTTP methods (e.g. 'POST') this route is restricted to. Chris@0: * So an empty array means that any method is allowed. Chris@0: * Chris@0: * This method implements a fluent interface. Chris@0: * Chris@14: * @param string|string[] $methods The method or an array of methods Chris@0: * Chris@0: * @return $this Chris@0: */ Chris@0: public function setMethods($methods) Chris@0: { Chris@0: $this->methods = array_map('strtoupper', (array) $methods); Chris@0: $this->compiled = null; Chris@0: Chris@0: return $this; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Returns the options. Chris@0: * Chris@0: * @return array The options Chris@0: */ Chris@0: public function getOptions() Chris@0: { Chris@0: return $this->options; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Sets the options. Chris@0: * Chris@0: * This method implements a fluent interface. Chris@0: * Chris@0: * @param array $options The options Chris@0: * Chris@0: * @return $this Chris@0: */ Chris@0: public function setOptions(array $options) Chris@0: { Chris@17: $this->options = [ Chris@0: 'compiler_class' => 'Symfony\\Component\\Routing\\RouteCompiler', Chris@17: ]; Chris@0: Chris@0: return $this->addOptions($options); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Adds options. Chris@0: * Chris@0: * This method implements a fluent interface. Chris@0: * Chris@0: * @param array $options The options Chris@0: * Chris@0: * @return $this Chris@0: */ Chris@0: public function addOptions(array $options) Chris@0: { Chris@0: foreach ($options as $name => $option) { Chris@0: $this->options[$name] = $option; Chris@0: } Chris@0: $this->compiled = null; Chris@0: Chris@0: return $this; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Sets an option value. Chris@0: * Chris@0: * This method implements a fluent interface. Chris@0: * Chris@0: * @param string $name An option name Chris@0: * @param mixed $value The option value Chris@0: * Chris@0: * @return $this Chris@0: */ Chris@0: public function setOption($name, $value) Chris@0: { Chris@0: $this->options[$name] = $value; Chris@0: $this->compiled = null; Chris@0: Chris@0: return $this; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Get an option value. Chris@0: * Chris@0: * @param string $name An option name Chris@0: * Chris@0: * @return mixed The option value or null when not given Chris@0: */ Chris@0: public function getOption($name) Chris@0: { Chris@0: return isset($this->options[$name]) ? $this->options[$name] : null; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Checks if an option has been set. Chris@0: * Chris@0: * @param string $name An option name Chris@0: * Chris@0: * @return bool true if the option is set, false otherwise Chris@0: */ Chris@0: public function hasOption($name) Chris@0: { Chris@18: return \array_key_exists($name, $this->options); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Returns the defaults. Chris@0: * Chris@0: * @return array The defaults Chris@0: */ Chris@0: public function getDefaults() Chris@0: { Chris@0: return $this->defaults; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Sets the defaults. Chris@0: * Chris@0: * This method implements a fluent interface. Chris@0: * Chris@0: * @param array $defaults The defaults Chris@0: * Chris@0: * @return $this Chris@0: */ Chris@0: public function setDefaults(array $defaults) Chris@0: { Chris@17: $this->defaults = []; Chris@0: Chris@0: return $this->addDefaults($defaults); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Adds defaults. Chris@0: * Chris@0: * This method implements a fluent interface. Chris@0: * Chris@0: * @param array $defaults The defaults Chris@0: * Chris@0: * @return $this Chris@0: */ Chris@0: public function addDefaults(array $defaults) Chris@0: { Chris@0: foreach ($defaults as $name => $default) { Chris@0: $this->defaults[$name] = $default; Chris@0: } Chris@0: $this->compiled = null; Chris@0: Chris@0: return $this; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Gets a default value. Chris@0: * Chris@0: * @param string $name A variable name Chris@0: * Chris@0: * @return mixed The default value or null when not given Chris@0: */ Chris@0: public function getDefault($name) Chris@0: { Chris@0: return isset($this->defaults[$name]) ? $this->defaults[$name] : null; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Checks if a default value is set for the given variable. Chris@0: * Chris@0: * @param string $name A variable name Chris@0: * Chris@0: * @return bool true if the default value is set, false otherwise Chris@0: */ Chris@0: public function hasDefault($name) Chris@0: { Chris@18: return \array_key_exists($name, $this->defaults); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Sets a default value. Chris@0: * Chris@0: * @param string $name A variable name Chris@0: * @param mixed $default The default value Chris@0: * Chris@0: * @return $this Chris@0: */ Chris@0: public function setDefault($name, $default) Chris@0: { Chris@0: $this->defaults[$name] = $default; Chris@0: $this->compiled = null; Chris@0: Chris@0: return $this; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Returns the requirements. Chris@0: * Chris@0: * @return array The requirements Chris@0: */ Chris@0: public function getRequirements() Chris@0: { Chris@0: return $this->requirements; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Sets the requirements. Chris@0: * Chris@0: * This method implements a fluent interface. Chris@0: * Chris@0: * @param array $requirements The requirements Chris@0: * Chris@0: * @return $this Chris@0: */ Chris@0: public function setRequirements(array $requirements) Chris@0: { Chris@17: $this->requirements = []; Chris@0: Chris@0: return $this->addRequirements($requirements); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Adds requirements. Chris@0: * Chris@0: * This method implements a fluent interface. Chris@0: * Chris@0: * @param array $requirements The requirements Chris@0: * Chris@0: * @return $this Chris@0: */ Chris@0: public function addRequirements(array $requirements) Chris@0: { Chris@0: foreach ($requirements as $key => $regex) { Chris@0: $this->requirements[$key] = $this->sanitizeRequirement($key, $regex); Chris@0: } Chris@0: $this->compiled = null; Chris@0: Chris@0: return $this; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Returns the requirement for the given key. Chris@0: * Chris@0: * @param string $key The key Chris@0: * Chris@0: * @return string|null The regex or null when not given Chris@0: */ Chris@0: public function getRequirement($key) Chris@0: { Chris@0: return isset($this->requirements[$key]) ? $this->requirements[$key] : null; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Checks if a requirement is set for the given key. Chris@0: * Chris@0: * @param string $key A variable name Chris@0: * Chris@0: * @return bool true if a requirement is specified, false otherwise Chris@0: */ Chris@0: public function hasRequirement($key) Chris@0: { Chris@18: return \array_key_exists($key, $this->requirements); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Sets a requirement for the given key. Chris@0: * Chris@0: * @param string $key The key Chris@0: * @param string $regex The regex Chris@0: * Chris@0: * @return $this Chris@0: */ Chris@0: public function setRequirement($key, $regex) Chris@0: { Chris@0: $this->requirements[$key] = $this->sanitizeRequirement($key, $regex); Chris@0: $this->compiled = null; Chris@0: Chris@0: return $this; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Returns the condition. Chris@0: * Chris@0: * @return string The condition Chris@0: */ Chris@0: public function getCondition() Chris@0: { Chris@0: return $this->condition; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Sets the condition. Chris@0: * Chris@0: * This method implements a fluent interface. Chris@0: * Chris@0: * @param string $condition The condition Chris@0: * Chris@0: * @return $this Chris@0: */ Chris@0: public function setCondition($condition) Chris@0: { Chris@0: $this->condition = (string) $condition; Chris@0: $this->compiled = null; Chris@0: Chris@0: return $this; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Compiles the route. Chris@0: * Chris@0: * @return CompiledRoute A CompiledRoute instance Chris@0: * Chris@0: * @throws \LogicException If the Route cannot be compiled because the Chris@0: * path or host pattern is invalid Chris@0: * Chris@0: * @see RouteCompiler which is responsible for the compilation process Chris@0: */ Chris@0: public function compile() Chris@0: { Chris@0: if (null !== $this->compiled) { Chris@0: return $this->compiled; Chris@0: } Chris@0: Chris@0: $class = $this->getOption('compiler_class'); Chris@0: Chris@0: return $this->compiled = $class::compile($this); Chris@0: } Chris@0: Chris@0: private function sanitizeRequirement($key, $regex) Chris@0: { Chris@17: if (!\is_string($regex)) { Chris@0: throw new \InvalidArgumentException(sprintf('Routing requirement for "%s" must be a string.', $key)); Chris@0: } Chris@0: Chris@0: if ('' !== $regex && '^' === $regex[0]) { Chris@0: $regex = (string) substr($regex, 1); // returns false for a single character Chris@0: } Chris@0: Chris@0: if ('$' === substr($regex, -1)) { Chris@0: $regex = substr($regex, 0, -1); Chris@0: } Chris@0: Chris@0: if ('' === $regex) { Chris@0: throw new \InvalidArgumentException(sprintf('Routing requirement for "%s" cannot be empty.', $key)); Chris@0: } Chris@0: Chris@0: return $regex; Chris@0: } Chris@0: }