Commit version 24.12.13800
This commit is contained in:
@ -1,10 +1,6 @@
|
||||
<?php
|
||||
/**
|
||||
* Flight: An extensible micro-framework.
|
||||
*
|
||||
* @copyright Copyright (c) 2011, Mike Cao <mike@mikecao.com>
|
||||
* @license MIT, http://flightphp.com/license
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace flight\net;
|
||||
|
||||
@ -12,133 +8,259 @@ namespace flight\net;
|
||||
* The Route class is responsible for routing an HTTP request to
|
||||
* an assigned callback function. The Router tries to match the
|
||||
* requested URL against a series of URL patterns.
|
||||
*
|
||||
* @license MIT, http://flightphp.com/license
|
||||
* @copyright Copyright (c) 2011, Mike Cao <mike@mikecao.com>
|
||||
*/
|
||||
class Route {
|
||||
class Route
|
||||
{
|
||||
/**
|
||||
* @var string URL pattern
|
||||
* URL pattern
|
||||
*/
|
||||
public $pattern;
|
||||
public string $pattern;
|
||||
|
||||
/**
|
||||
* @var mixed Callback function
|
||||
* Callback function
|
||||
*
|
||||
* @var mixed
|
||||
*/
|
||||
public $callback;
|
||||
|
||||
/**
|
||||
* @var array HTTP methods
|
||||
* HTTP methods
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
public $methods = array();
|
||||
public array $methods = [];
|
||||
|
||||
/**
|
||||
* @var array Route parameters
|
||||
* Route parameters
|
||||
*
|
||||
* @var array<int, ?string>
|
||||
*/
|
||||
public $params = array();
|
||||
public array $params = [];
|
||||
|
||||
/**
|
||||
* @var string Matching regular expression
|
||||
* Matching regular expression
|
||||
*/
|
||||
public $regex;
|
||||
public ?string $regex = null;
|
||||
|
||||
/**
|
||||
* @var string URL splat content
|
||||
* URL splat content
|
||||
*/
|
||||
public $splat = '';
|
||||
public string $splat = '';
|
||||
|
||||
/**
|
||||
* @var boolean Pass self in callback parameters
|
||||
* Pass self in callback parameters
|
||||
*/
|
||||
public $pass = false;
|
||||
public bool $pass = false;
|
||||
|
||||
/**
|
||||
* The alias is a way to identify the route using a simple name ex: 'login' instead of /admin/login
|
||||
*/
|
||||
public string $alias = '';
|
||||
|
||||
/**
|
||||
* The middleware to be applied to the route
|
||||
*
|
||||
* @var array<int, callable|object|string>
|
||||
*/
|
||||
public array $middleware = [];
|
||||
|
||||
/** Whether the response for this route should be streamed. */
|
||||
public bool $is_streamed = false;
|
||||
|
||||
/**
|
||||
* If this route is streamed, the headers to be sent before the response.
|
||||
*
|
||||
* @var array<string, mixed>
|
||||
*/
|
||||
public array $streamed_headers = [];
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $pattern URL pattern
|
||||
* @param mixed $callback Callback function
|
||||
* @param array $methods HTTP methods
|
||||
* @param boolean $pass Pass self in callback parameters
|
||||
* @param string $pattern URL pattern
|
||||
* @param callable|string $callback Callback function
|
||||
* @param array<int, string> $methods HTTP methods
|
||||
* @param bool $pass Pass self in callback parameters
|
||||
*/
|
||||
public function __construct($pattern, $callback, $methods, $pass) {
|
||||
public function __construct(string $pattern, $callback, array $methods, bool $pass, string $alias = '')
|
||||
{
|
||||
$this->pattern = $pattern;
|
||||
$this->callback = $callback;
|
||||
$this->methods = $methods;
|
||||
$this->pass = $pass;
|
||||
$this->alias = $alias;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a URL matches the route pattern. Also parses named parameters in the URL.
|
||||
*
|
||||
* @param string $url Requested URL
|
||||
* @param boolean $case_sensitive Case sensitive matching
|
||||
* @return boolean Match status
|
||||
* @param string $url Requested URL (original format, not URL decoded)
|
||||
* @param bool $case_sensitive Case sensitive matching
|
||||
*
|
||||
* @return bool Match status
|
||||
*/
|
||||
public function matchUrl($url, $case_sensitive = false) {
|
||||
public function matchUrl(string $url, bool $case_sensitive = false): bool
|
||||
{
|
||||
// Wildcard or exact match
|
||||
if ($this->pattern === '*' || $this->pattern === $url) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$ids = array();
|
||||
$ids = [];
|
||||
$last_char = substr($this->pattern, -1);
|
||||
|
||||
// Get splat
|
||||
if ($last_char === '*') {
|
||||
$n = 0;
|
||||
$len = strlen($url);
|
||||
$len = \strlen($url);
|
||||
$count = substr_count($this->pattern, '/');
|
||||
|
||||
for ($i = 0; $i < $len; $i++) {
|
||||
if ($url[$i] == '/') $n++;
|
||||
if ($n == $count) break;
|
||||
if ($url[$i] === '/') {
|
||||
++$n;
|
||||
}
|
||||
|
||||
if ($n === $count) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$this->splat = (string)substr($url, $i+1);
|
||||
$this->splat = urldecode(strval(substr($url, $i + 1)));
|
||||
}
|
||||
|
||||
// Build the regex for matching
|
||||
$regex = str_replace(array(')','/*'), array(')?','(/?|/.*?)'), $this->pattern);
|
||||
$pattern_utf_chars_encoded = preg_replace_callback(
|
||||
'#(\\p{L}+)#u',
|
||||
static function ($matches) {
|
||||
return urlencode($matches[0]);
|
||||
},
|
||||
$this->pattern
|
||||
);
|
||||
$regex = str_replace([')', '/*'], [')?', '(/?|/.*?)'], $pattern_utf_chars_encoded);
|
||||
|
||||
$regex = preg_replace_callback(
|
||||
'#@([\w]+)(:([^/\(\)]*))?#',
|
||||
function($matches) use (&$ids) {
|
||||
static function ($matches) use (&$ids) {
|
||||
$ids[$matches[1]] = null;
|
||||
if (isset($matches[3])) {
|
||||
return '(?P<'.$matches[1].'>'.$matches[3].')';
|
||||
return '(?P<' . $matches[1] . '>' . $matches[3] . ')';
|
||||
}
|
||||
return '(?P<'.$matches[1].'>[^/\?]+)';
|
||||
|
||||
return '(?P<' . $matches[1] . '>[^/\?]+)';
|
||||
},
|
||||
$regex
|
||||
);
|
||||
|
||||
// Fix trailing slash
|
||||
if ($last_char === '/') {
|
||||
$regex .= '?';
|
||||
}
|
||||
// Allow trailing slash
|
||||
else {
|
||||
$regex .= '/?';
|
||||
}
|
||||
$regex .= $last_char === '/' ? '?' : '/?';
|
||||
|
||||
// Attempt to match route and named parameters
|
||||
if (preg_match('#^'.$regex.'(?:\?.*)?$#'.(($case_sensitive) ? '' : 'i'), $url, $matches)) {
|
||||
foreach ($ids as $k => $v) {
|
||||
$this->params[$k] = (array_key_exists($k, $matches)) ? urldecode($matches[$k]) : null;
|
||||
}
|
||||
|
||||
$this->regex = $regex;
|
||||
|
||||
return true;
|
||||
if (!preg_match('#^' . $regex . '(?:\?[\s\S]*)?$#' . (($case_sensitive) ? '' : 'i'), $url, $matches)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
foreach (array_keys($ids) as $k) {
|
||||
$this->params[$k] = (\array_key_exists($k, $matches)) ? urldecode($matches[$k]) : null;
|
||||
}
|
||||
|
||||
$this->regex = $regex;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if an HTTP method matches the route methods.
|
||||
*
|
||||
* @param string $method HTTP method
|
||||
*
|
||||
* @return bool Match status
|
||||
*/
|
||||
public function matchMethod($method) {
|
||||
return count(array_intersect(array($method, '*'), $this->methods)) > 0;
|
||||
public function matchMethod(string $method): bool
|
||||
{
|
||||
return \count(array_intersect([$method, '*'], $this->methods)) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if an alias matches the route alias.
|
||||
*/
|
||||
public function matchAlias(string $alias): bool
|
||||
{
|
||||
return $this->alias === $alias;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hydrates the route url with the given parameters
|
||||
*
|
||||
* @param array<string, mixed> $params the parameters to pass to the route
|
||||
*/
|
||||
public function hydrateUrl(array $params = []): string
|
||||
{
|
||||
$url = preg_replace_callback("/(?:@([\w]+)(?:\:([^\/]+))?\)*)/i", function ($match) use ($params) {
|
||||
if (isset($match[1]) && isset($params[$match[1]])) {
|
||||
return $params[$match[1]];
|
||||
}
|
||||
}, $this->pattern);
|
||||
|
||||
// catches potential optional parameter
|
||||
$url = str_replace('(/', '/', $url);
|
||||
// trim any trailing slashes
|
||||
if ($url !== '/') {
|
||||
$url = rtrim($url, '/');
|
||||
}
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the route alias
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setAlias(string $alias): self
|
||||
{
|
||||
$this->alias = $alias;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the route middleware
|
||||
*
|
||||
* @param array<int, callable|string>|callable|string $middleware
|
||||
*/
|
||||
public function addMiddleware($middleware): self
|
||||
{
|
||||
if (is_array($middleware) === true) {
|
||||
$this->middleware = array_merge($this->middleware, $middleware);
|
||||
} else {
|
||||
$this->middleware[] = $middleware;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the response should be streamed
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function stream(): self
|
||||
{
|
||||
$this->is_streamed = true;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* This will allow the response for this route to be streamed.
|
||||
*
|
||||
* @param array<string, mixed> $headers a key value of headers to set before the stream starts.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function streamWithHeaders(array $headers): self
|
||||
{
|
||||
$this->is_streamed = true;
|
||||
$this->streamed_headers = $headers;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user