gmarche/vendor/zendframework/zend-expressive-router/src/Route.php

242 lines
6.8 KiB
PHP

<?php
/**
* @see https://github.com/zendframework/zend-expressive-router for the canonical source repository
* @copyright Copyright (c) 2015-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license https://github.com/zendframework/zend-expressive-router/blob/master/LICENSE.md New BSD License
*/
namespace Zend\Expressive\Router;
use Fig\Http\Message\RequestMethodInterface as RequestMethod;
/**
* Value object representing a single route.
*
* Routes are a combination of path, middleware, and HTTP methods; two routes
* representing the same path and overlapping HTTP methods are not allowed,
* while two routes representing the same path and non-overlapping HTTP methods
* can be used (and should typically resolve to different middleware).
*
* Internally, only those three properties are required. However, underlying
* router implementations may allow or require additional information, such as
* information defining how to generate a URL from the given route, qualifiers
* for how segments of a route match, or even default values to use. These may
* be provided after instantiation via the "options" property and related
* setOptions() method.
*/
class Route
{
const HTTP_METHOD_ANY = 0xff;
const HTTP_METHOD_SEPARATOR = ':';
/**
* @var bool If HEAD was not provided to the Route instance, indicate
* support for the method is implicit.
*/
private $implicitHead = true;
/**
* @var bool If OPTIONS was not provided to the Route instance, indicate
* support for the method is implicit.
*/
private $implicitOptions = true;
/**
* @var int|string[] HTTP methods allowed with this route.
*/
private $methods = self::HTTP_METHOD_ANY;
/**
* @var callable|string Middleware or service name of middleware associated with route.
*/
private $middleware;
/**
* @var array Options related to this route to pass to the routing implementation.
*/
private $options = [];
/**
* @var string
*/
private $path;
/**
* @var string
*/
private $name;
/**
* @param string $path Path to match.
* @param string|callable $middleware Middleware to use when this route is matched.
* @param int|array Allowed HTTP methods; defaults to HTTP_METHOD_ANY.
* @param string|null $name the route name
* @throws Exception\InvalidArgumentException for invalid path type.
* @throws Exception\InvalidArgumentException for invalid middleware type.
* @throws Exception\InvalidArgumentException for any invalid HTTP method names.
*/
public function __construct($path, $middleware, $methods = self::HTTP_METHOD_ANY, $name = null)
{
if (! is_string($path)) {
throw new Exception\InvalidArgumentException('Invalid path; must be a string');
}
if (! is_callable($middleware) && ! is_string($middleware) && ! is_array($middleware)) {
throw new Exception\InvalidArgumentException('Invalid middleware; must be callable or a service name');
}
if ($methods !== self::HTTP_METHOD_ANY && ! is_array($methods)) {
throw new Exception\InvalidArgumentException(sprintf(
'Invalid HTTP methods; must be an array or %s::HTTP_METHOD_ANY',
__CLASS__
));
}
$this->path = $path;
$this->middleware = $middleware;
$this->methods = is_array($methods) ? $this->validateHttpMethods($methods) : $methods;
if (empty($name)) {
$name = ($this->methods === self::HTTP_METHOD_ANY)
? $path
: $path . '^' . implode(self::HTTP_METHOD_SEPARATOR, $this->methods);
}
$this->name = $name;
$this->implicitHead = is_array($this->methods)
&& ! in_array(RequestMethod::METHOD_HEAD, $this->methods, true);
$this->implicitOptions = is_array($this->methods)
&& ! in_array(RequestMethod::METHOD_OPTIONS, $this->methods, true);
}
/**
* @return string
*/
public function getPath()
{
return $this->path;
}
/**
* Set the route name.
*
* @param string $name
*/
public function setName($name)
{
$this->name = (string) $name;
}
/**
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* @return string|callable|array
*/
public function getMiddleware()
{
return $this->middleware;
}
/**
* @return int|string[] Returns HTTP_METHOD_ANY or array of allowed methods.
*/
public function getAllowedMethods()
{
return $this->methods;
}
/**
* Indicate whether the specified method is allowed by the route.
*
* @param string $method HTTP method to test.
* @return bool
*/
public function allowsMethod($method)
{
$method = strtoupper($method);
if (RequestMethod::METHOD_HEAD === $method
|| RequestMethod::METHOD_OPTIONS === $method
|| $this->methods === self::HTTP_METHOD_ANY
|| in_array($method, $this->methods, true)
) {
return true;
}
return false;
}
/**
* @param array $options
*/
public function setOptions(array $options)
{
$this->options = $options;
}
/**
* @return array
*/
public function getOptions()
{
return $this->options;
}
/**
* Whether or not HEAD support is implicit (i.e., not explicitly specified)
*
* @return bool
*/
public function implicitHead()
{
return $this->implicitHead;
}
/**
* Whether or not OPTIONS support is implicit (i.e., not explicitly specified)
*
* @return bool
*/
public function implicitOptions()
{
return $this->implicitOptions;
}
/**
* Validate the provided HTTP method names.
*
* Validates, and then normalizes to upper case.
*
* @param string[] An array of HTTP method names.
* @return string[]
* @throws Exception\InvalidArgumentException for any invalid method names.
*/
private function validateHttpMethods(array $methods)
{
if (false === array_reduce($methods, function ($valid, $method) {
if (false === $valid) {
return false;
}
if (! is_string($method)) {
return false;
}
if (! preg_match('/^[!#$%&\'*+.^_`\|~0-9a-z-]+$/i', $method)) {
return false;
}
return $valid;
}, true)) {
throw new Exception\InvalidArgumentException('One or more HTTP methods were invalid');
}
return array_map('strtoupper', $methods);
}
}