Update website

This commit is contained in:
Guilhem Lavaux 2024-11-19 09:35:33 +01:00
parent bb4b0f9be8
commit 011b183e28
4263 changed files with 3014 additions and 720369 deletions

View file

@ -1,7 +0,0 @@
<?php
namespace FastRoute;
class BadRouteException extends \LogicException
{
}

View file

@ -1,26 +0,0 @@
<?php
namespace FastRoute;
interface DataGenerator
{
/**
* Adds a route to the data generator. The route data uses the
* same format that is returned by RouterParser::parser().
*
* The handler doesn't necessarily need to be a callable, it
* can be arbitrary data that will be returned when the route
* matches.
*
* @param string $httpMethod
* @param array $routeData
* @param mixed $handler
*/
public function addRoute($httpMethod, $routeData, $handler);
/**
* Returns dispatcher data in some unspecified format, which
* depends on the used method of dispatch.
*/
public function getData();
}

View file

@ -1,31 +0,0 @@
<?php
namespace FastRoute\DataGenerator;
class CharCountBased extends RegexBasedAbstract
{
protected function getApproxChunkSize()
{
return 30;
}
protected function processChunk($regexToRoutesMap)
{
$routeMap = [];
$regexes = [];
$suffixLen = 0;
$suffix = '';
$count = count($regexToRoutesMap);
foreach ($regexToRoutesMap as $regex => $route) {
$suffixLen++;
$suffix .= "\t";
$regexes[] = '(?:' . $regex . '/(\t{' . $suffixLen . '})\t{' . ($count - $suffixLen) . '})';
$routeMap[$suffix] = [$route->handler, $route->variables];
}
$regex = '~^(?|' . implode('|', $regexes) . ')$~';
return ['regex' => $regex, 'suffix' => '/' . $suffix, 'routeMap' => $routeMap];
}
}

View file

@ -1,30 +0,0 @@
<?php
namespace FastRoute\DataGenerator;
class GroupCountBased extends RegexBasedAbstract
{
protected function getApproxChunkSize()
{
return 10;
}
protected function processChunk($regexToRoutesMap)
{
$routeMap = [];
$regexes = [];
$numGroups = 0;
foreach ($regexToRoutesMap as $regex => $route) {
$numVariables = count($route->variables);
$numGroups = max($numGroups, $numVariables);
$regexes[] = $regex . str_repeat('()', $numGroups - $numVariables);
$routeMap[$numGroups + 1] = [$route->handler, $route->variables];
++$numGroups;
}
$regex = '~^(?|' . implode('|', $regexes) . ')$~';
return ['regex' => $regex, 'routeMap' => $routeMap];
}
}

View file

@ -1,27 +0,0 @@
<?php
namespace FastRoute\DataGenerator;
class GroupPosBased extends RegexBasedAbstract
{
protected function getApproxChunkSize()
{
return 10;
}
protected function processChunk($regexToRoutesMap)
{
$routeMap = [];
$regexes = [];
$offset = 1;
foreach ($regexToRoutesMap as $regex => $route) {
$regexes[] = $regex;
$routeMap[$offset] = [$route->handler, $route->variables];
$offset += count($route->variables);
}
$regex = '~^(?:' . implode('|', $regexes) . ')$~';
return ['regex' => $regex, 'routeMap' => $routeMap];
}
}

View file

@ -1,27 +0,0 @@
<?php
namespace FastRoute\DataGenerator;
class MarkBased extends RegexBasedAbstract
{
protected function getApproxChunkSize()
{
return 30;
}
protected function processChunk($regexToRoutesMap)
{
$routeMap = [];
$regexes = [];
$markName = 'a';
foreach ($regexToRoutesMap as $regex => $route) {
$regexes[] = $regex . '(*MARK:' . $markName . ')';
$routeMap[$markName] = [$route->handler, $route->variables];
++$markName;
}
$regex = '~^(?|' . implode('|', $regexes) . ')$~';
return ['regex' => $regex, 'routeMap' => $routeMap];
}
}

View file

@ -1,186 +0,0 @@
<?php
namespace FastRoute\DataGenerator;
use FastRoute\BadRouteException;
use FastRoute\DataGenerator;
use FastRoute\Route;
abstract class RegexBasedAbstract implements DataGenerator
{
/** @var mixed[][] */
protected $staticRoutes = [];
/** @var Route[][] */
protected $methodToRegexToRoutesMap = [];
/**
* @return int
*/
abstract protected function getApproxChunkSize();
/**
* @return mixed[]
*/
abstract protected function processChunk($regexToRoutesMap);
public function addRoute($httpMethod, $routeData, $handler)
{
if ($this->isStaticRoute($routeData)) {
$this->addStaticRoute($httpMethod, $routeData, $handler);
} else {
$this->addVariableRoute($httpMethod, $routeData, $handler);
}
}
/**
* @return mixed[]
*/
public function getData()
{
if (empty($this->methodToRegexToRoutesMap)) {
return [$this->staticRoutes, []];
}
return [$this->staticRoutes, $this->generateVariableRouteData()];
}
/**
* @return mixed[]
*/
private function generateVariableRouteData()
{
$data = [];
foreach ($this->methodToRegexToRoutesMap as $method => $regexToRoutesMap) {
$chunkSize = $this->computeChunkSize(count($regexToRoutesMap));
$chunks = array_chunk($regexToRoutesMap, $chunkSize, true);
$data[$method] = array_map([$this, 'processChunk'], $chunks);
}
return $data;
}
/**
* @param int
* @return int
*/
private function computeChunkSize($count)
{
$numParts = max(1, round($count / $this->getApproxChunkSize()));
return (int) ceil($count / $numParts);
}
/**
* @param mixed[]
* @return bool
*/
private function isStaticRoute($routeData)
{
return count($routeData) === 1 && is_string($routeData[0]);
}
private function addStaticRoute($httpMethod, $routeData, $handler)
{
$routeStr = $routeData[0];
if (isset($this->staticRoutes[$httpMethod][$routeStr])) {
throw new BadRouteException(sprintf(
'Cannot register two routes matching "%s" for method "%s"',
$routeStr, $httpMethod
));
}
if (isset($this->methodToRegexToRoutesMap[$httpMethod])) {
foreach ($this->methodToRegexToRoutesMap[$httpMethod] as $route) {
if ($route->matches($routeStr)) {
throw new BadRouteException(sprintf(
'Static route "%s" is shadowed by previously defined variable route "%s" for method "%s"',
$routeStr, $route->regex, $httpMethod
));
}
}
}
$this->staticRoutes[$httpMethod][$routeStr] = $handler;
}
private function addVariableRoute($httpMethod, $routeData, $handler)
{
list($regex, $variables) = $this->buildRegexForRoute($routeData);
if (isset($this->methodToRegexToRoutesMap[$httpMethod][$regex])) {
throw new BadRouteException(sprintf(
'Cannot register two routes matching "%s" for method "%s"',
$regex, $httpMethod
));
}
$this->methodToRegexToRoutesMap[$httpMethod][$regex] = new Route(
$httpMethod, $handler, $regex, $variables
);
}
/**
* @param mixed[]
* @return mixed[]
*/
private function buildRegexForRoute($routeData)
{
$regex = '';
$variables = [];
foreach ($routeData as $part) {
if (is_string($part)) {
$regex .= preg_quote($part, '~');
continue;
}
list($varName, $regexPart) = $part;
if (isset($variables[$varName])) {
throw new BadRouteException(sprintf(
'Cannot use the same placeholder "%s" twice', $varName
));
}
if ($this->regexHasCapturingGroups($regexPart)) {
throw new BadRouteException(sprintf(
'Regex "%s" for parameter "%s" contains a capturing group',
$regexPart, $varName
));
}
$variables[$varName] = $varName;
$regex .= '(' . $regexPart . ')';
}
return [$regex, $variables];
}
/**
* @param string
* @return bool
*/
private function regexHasCapturingGroups($regex)
{
if (false === strpos($regex, '(')) {
// Needs to have at least a ( to contain a capturing group
return false;
}
// Semi-accurate detection for capturing groups
return (bool) preg_match(
'~
(?:
\(\?\(
| \[ [^\]\\\\]* (?: \\\\ . [^\]\\\\]* )* \]
| \\\\ .
) (*SKIP)(*FAIL) |
\(
(?!
\? (?! <(?![!=]) | P< | \' )
| \*
)
~x',
$regex
);
}
}

View file

@ -1,26 +0,0 @@
<?php
namespace FastRoute;
interface Dispatcher
{
const NOT_FOUND = 0;
const FOUND = 1;
const METHOD_NOT_ALLOWED = 2;
/**
* Dispatches against the provided HTTP method verb and URI.
*
* Returns array with one of the following formats:
*
* [self::NOT_FOUND]
* [self::METHOD_NOT_ALLOWED, ['GET', 'OTHER_ALLOWED_METHODS']]
* [self::FOUND, $handler, ['varName' => 'value', ...]]
*
* @param string $httpMethod
* @param string $uri
*
* @return array
*/
public function dispatch($httpMethod, $uri);
}

View file

@ -1,31 +0,0 @@
<?php
namespace FastRoute\Dispatcher;
class CharCountBased extends RegexBasedAbstract
{
public function __construct($data)
{
list($this->staticRouteMap, $this->variableRouteData) = $data;
}
protected function dispatchVariableRoute($routeData, $uri)
{
foreach ($routeData as $data) {
if (!preg_match($data['regex'], $uri . $data['suffix'], $matches)) {
continue;
}
list($handler, $varNames) = $data['routeMap'][end($matches)];
$vars = [];
$i = 0;
foreach ($varNames as $varName) {
$vars[$varName] = $matches[++$i];
}
return [self::FOUND, $handler, $vars];
}
return [self::NOT_FOUND];
}
}

View file

@ -1,31 +0,0 @@
<?php
namespace FastRoute\Dispatcher;
class GroupCountBased extends RegexBasedAbstract
{
public function __construct($data)
{
list($this->staticRouteMap, $this->variableRouteData) = $data;
}
protected function dispatchVariableRoute($routeData, $uri)
{
foreach ($routeData as $data) {
if (!preg_match($data['regex'], $uri, $matches)) {
continue;
}
list($handler, $varNames) = $data['routeMap'][count($matches)];
$vars = [];
$i = 0;
foreach ($varNames as $varName) {
$vars[$varName] = $matches[++$i];
}
return [self::FOUND, $handler, $vars];
}
return [self::NOT_FOUND];
}
}

View file

@ -1,33 +0,0 @@
<?php
namespace FastRoute\Dispatcher;
class GroupPosBased extends RegexBasedAbstract
{
public function __construct($data)
{
list($this->staticRouteMap, $this->variableRouteData) = $data;
}
protected function dispatchVariableRoute($routeData, $uri)
{
foreach ($routeData as $data) {
if (!preg_match($data['regex'], $uri, $matches)) {
continue;
}
// find first non-empty match
for ($i = 1; '' === $matches[$i]; ++$i);
list($handler, $varNames) = $data['routeMap'][$i];
$vars = [];
foreach ($varNames as $varName) {
$vars[$varName] = $matches[$i++];
}
return [self::FOUND, $handler, $vars];
}
return [self::NOT_FOUND];
}
}

View file

@ -1,31 +0,0 @@
<?php
namespace FastRoute\Dispatcher;
class MarkBased extends RegexBasedAbstract
{
public function __construct($data)
{
list($this->staticRouteMap, $this->variableRouteData) = $data;
}
protected function dispatchVariableRoute($routeData, $uri)
{
foreach ($routeData as $data) {
if (!preg_match($data['regex'], $uri, $matches)) {
continue;
}
list($handler, $varNames) = $data['routeMap'][$matches['MARK']];
$vars = [];
$i = 0;
foreach ($varNames as $varName) {
$vars[$varName] = $matches[++$i];
}
return [self::FOUND, $handler, $vars];
}
return [self::NOT_FOUND];
}
}

View file

@ -1,88 +0,0 @@
<?php
namespace FastRoute\Dispatcher;
use FastRoute\Dispatcher;
abstract class RegexBasedAbstract implements Dispatcher
{
/** @var mixed[][] */
protected $staticRouteMap = [];
/** @var mixed[] */
protected $variableRouteData = [];
/**
* @return mixed[]
*/
abstract protected function dispatchVariableRoute($routeData, $uri);
public function dispatch($httpMethod, $uri)
{
if (isset($this->staticRouteMap[$httpMethod][$uri])) {
$handler = $this->staticRouteMap[$httpMethod][$uri];
return [self::FOUND, $handler, []];
}
$varRouteData = $this->variableRouteData;
if (isset($varRouteData[$httpMethod])) {
$result = $this->dispatchVariableRoute($varRouteData[$httpMethod], $uri);
if ($result[0] === self::FOUND) {
return $result;
}
}
// For HEAD requests, attempt fallback to GET
if ($httpMethod === 'HEAD') {
if (isset($this->staticRouteMap['GET'][$uri])) {
$handler = $this->staticRouteMap['GET'][$uri];
return [self::FOUND, $handler, []];
}
if (isset($varRouteData['GET'])) {
$result = $this->dispatchVariableRoute($varRouteData['GET'], $uri);
if ($result[0] === self::FOUND) {
return $result;
}
}
}
// If nothing else matches, try fallback routes
if (isset($this->staticRouteMap['*'][$uri])) {
$handler = $this->staticRouteMap['*'][$uri];
return [self::FOUND, $handler, []];
}
if (isset($varRouteData['*'])) {
$result = $this->dispatchVariableRoute($varRouteData['*'], $uri);
if ($result[0] === self::FOUND) {
return $result;
}
}
// Find allowed methods for this URI by matching against all other HTTP methods as well
$allowedMethods = [];
foreach ($this->staticRouteMap as $method => $uriMap) {
if ($method !== $httpMethod && isset($uriMap[$uri])) {
$allowedMethods[] = $method;
}
}
foreach ($varRouteData as $method => $routeData) {
if ($method === $httpMethod) {
continue;
}
$result = $this->dispatchVariableRoute($routeData, $uri);
if ($result[0] === self::FOUND) {
$allowedMethods[] = $method;
}
}
// If there are no allowed methods the route simply does not exist
if ($allowedMethods) {
return [self::METHOD_NOT_ALLOWED, $allowedMethods];
}
return [self::NOT_FOUND];
}
}

View file

@ -1,47 +0,0 @@
<?php
namespace FastRoute;
class Route
{
/** @var string */
public $httpMethod;
/** @var string */
public $regex;
/** @var array */
public $variables;
/** @var mixed */
public $handler;
/**
* Constructs a route (value object).
*
* @param string $httpMethod
* @param mixed $handler
* @param string $regex
* @param array $variables
*/
public function __construct($httpMethod, $handler, $regex, $variables)
{
$this->httpMethod = $httpMethod;
$this->handler = $handler;
$this->regex = $regex;
$this->variables = $variables;
}
/**
* Tests whether this route matches the given string.
*
* @param string $str
*
* @return bool
*/
public function matches($str)
{
$regex = '~^' . $this->regex . '$~';
return (bool) preg_match($regex, $str);
}
}

View file

@ -1,152 +0,0 @@
<?php
namespace FastRoute;
class RouteCollector
{
/** @var RouteParser */
protected $routeParser;
/** @var DataGenerator */
protected $dataGenerator;
/** @var string */
protected $currentGroupPrefix;
/**
* Constructs a route collector.
*
* @param RouteParser $routeParser
* @param DataGenerator $dataGenerator
*/
public function __construct(RouteParser $routeParser, DataGenerator $dataGenerator)
{
$this->routeParser = $routeParser;
$this->dataGenerator = $dataGenerator;
$this->currentGroupPrefix = '';
}
/**
* Adds a route to the collection.
*
* The syntax used in the $route string depends on the used route parser.
*
* @param string|string[] $httpMethod
* @param string $route
* @param mixed $handler
*/
public function addRoute($httpMethod, $route, $handler)
{
$route = $this->currentGroupPrefix . $route;
$routeDatas = $this->routeParser->parse($route);
foreach ((array) $httpMethod as $method) {
foreach ($routeDatas as $routeData) {
$this->dataGenerator->addRoute($method, $routeData, $handler);
}
}
}
/**
* Create a route group with a common prefix.
*
* All routes created in the passed callback will have the given group prefix prepended.
*
* @param string $prefix
* @param callable $callback
*/
public function addGroup($prefix, callable $callback)
{
$previousGroupPrefix = $this->currentGroupPrefix;
$this->currentGroupPrefix = $previousGroupPrefix . $prefix;
$callback($this);
$this->currentGroupPrefix = $previousGroupPrefix;
}
/**
* Adds a GET route to the collection
*
* This is simply an alias of $this->addRoute('GET', $route, $handler)
*
* @param string $route
* @param mixed $handler
*/
public function get($route, $handler)
{
$this->addRoute('GET', $route, $handler);
}
/**
* Adds a POST route to the collection
*
* This is simply an alias of $this->addRoute('POST', $route, $handler)
*
* @param string $route
* @param mixed $handler
*/
public function post($route, $handler)
{
$this->addRoute('POST', $route, $handler);
}
/**
* Adds a PUT route to the collection
*
* This is simply an alias of $this->addRoute('PUT', $route, $handler)
*
* @param string $route
* @param mixed $handler
*/
public function put($route, $handler)
{
$this->addRoute('PUT', $route, $handler);
}
/**
* Adds a DELETE route to the collection
*
* This is simply an alias of $this->addRoute('DELETE', $route, $handler)
*
* @param string $route
* @param mixed $handler
*/
public function delete($route, $handler)
{
$this->addRoute('DELETE', $route, $handler);
}
/**
* Adds a PATCH route to the collection
*
* This is simply an alias of $this->addRoute('PATCH', $route, $handler)
*
* @param string $route
* @param mixed $handler
*/
public function patch($route, $handler)
{
$this->addRoute('PATCH', $route, $handler);
}
/**
* Adds a HEAD route to the collection
*
* This is simply an alias of $this->addRoute('HEAD', $route, $handler)
*
* @param string $route
* @param mixed $handler
*/
public function head($route, $handler)
{
$this->addRoute('HEAD', $route, $handler);
}
/**
* Returns the collected route data, as provided by the data generator.
*
* @return array
*/
public function getData()
{
return $this->dataGenerator->getData();
}
}

View file

@ -1,37 +0,0 @@
<?php
namespace FastRoute;
interface RouteParser
{
/**
* Parses a route string into multiple route data arrays.
*
* The expected output is defined using an example:
*
* For the route string "/fixedRoutePart/{varName}[/moreFixed/{varName2:\d+}]", if {varName} is interpreted as
* a placeholder and [...] is interpreted as an optional route part, the expected result is:
*
* [
* // first route: without optional part
* [
* "/fixedRoutePart/",
* ["varName", "[^/]+"],
* ],
* // second route: with optional part
* [
* "/fixedRoutePart/",
* ["varName", "[^/]+"],
* "/moreFixed/",
* ["varName2", [0-9]+"],
* ],
* ]
*
* Here one route string was converted into two route data arrays.
*
* @param string $route Route string to parse
*
* @return mixed[][] Array of route data arrays
*/
public function parse($route);
}

View file

@ -1,87 +0,0 @@
<?php
namespace FastRoute\RouteParser;
use FastRoute\BadRouteException;
use FastRoute\RouteParser;
/**
* Parses route strings of the following form:
*
* "/user/{name}[/{id:[0-9]+}]"
*/
class Std implements RouteParser
{
const VARIABLE_REGEX = <<<'REGEX'
\{
\s* ([a-zA-Z_][a-zA-Z0-9_-]*) \s*
(?:
: \s* ([^{}]*(?:\{(?-1)\}[^{}]*)*)
)?
\}
REGEX;
const DEFAULT_DISPATCH_REGEX = '[^/]+';
public function parse($route)
{
$routeWithoutClosingOptionals = rtrim($route, ']');
$numOptionals = strlen($route) - strlen($routeWithoutClosingOptionals);
// Split on [ while skipping placeholders
$segments = preg_split('~' . self::VARIABLE_REGEX . '(*SKIP)(*F) | \[~x', $routeWithoutClosingOptionals);
if ($numOptionals !== count($segments) - 1) {
// If there are any ] in the middle of the route, throw a more specific error message
if (preg_match('~' . self::VARIABLE_REGEX . '(*SKIP)(*F) | \]~x', $routeWithoutClosingOptionals)) {
throw new BadRouteException('Optional segments can only occur at the end of a route');
}
throw new BadRouteException("Number of opening '[' and closing ']' does not match");
}
$currentRoute = '';
$routeDatas = [];
foreach ($segments as $n => $segment) {
if ($segment === '' && $n !== 0) {
throw new BadRouteException('Empty optional part');
}
$currentRoute .= $segment;
$routeDatas[] = $this->parsePlaceholders($currentRoute);
}
return $routeDatas;
}
/**
* Parses a route string that does not contain optional segments.
*
* @param string
* @return mixed[]
*/
private function parsePlaceholders($route)
{
if (!preg_match_all(
'~' . self::VARIABLE_REGEX . '~x', $route, $matches,
PREG_OFFSET_CAPTURE | PREG_SET_ORDER
)) {
return [$route];
}
$offset = 0;
$routeData = [];
foreach ($matches as $set) {
if ($set[0][1] > $offset) {
$routeData[] = substr($route, $offset, $set[0][1] - $offset);
}
$routeData[] = [
$set[1][0],
isset($set[2]) ? trim($set[2][0]) : self::DEFAULT_DISPATCH_REGEX
];
$offset = $set[0][1] + strlen($set[0][0]);
}
if ($offset !== strlen($route)) {
$routeData[] = substr($route, $offset);
}
return $routeData;
}
}

View file

@ -1,12 +0,0 @@
<?php
namespace FastRoute;
require __DIR__ . '/functions.php';
spl_autoload_register(function ($class) {
if (strpos($class, 'FastRoute\\') === 0) {
$name = substr($class, strlen('FastRoute'));
require __DIR__ . strtr($name, '\\', DIRECTORY_SEPARATOR) . '.php';
}
});

View file

@ -1,74 +0,0 @@
<?php
namespace FastRoute;
if (!function_exists('FastRoute\simpleDispatcher')) {
/**
* @param callable $routeDefinitionCallback
* @param array $options
*
* @return Dispatcher
*/
function simpleDispatcher(callable $routeDefinitionCallback, array $options = [])
{
$options += [
'routeParser' => 'FastRoute\\RouteParser\\Std',
'dataGenerator' => 'FastRoute\\DataGenerator\\GroupCountBased',
'dispatcher' => 'FastRoute\\Dispatcher\\GroupCountBased',
'routeCollector' => 'FastRoute\\RouteCollector',
];
/** @var RouteCollector $routeCollector */
$routeCollector = new $options['routeCollector'](
new $options['routeParser'], new $options['dataGenerator']
);
$routeDefinitionCallback($routeCollector);
return new $options['dispatcher']($routeCollector->getData());
}
/**
* @param callable $routeDefinitionCallback
* @param array $options
*
* @return Dispatcher
*/
function cachedDispatcher(callable $routeDefinitionCallback, array $options = [])
{
$options += [
'routeParser' => 'FastRoute\\RouteParser\\Std',
'dataGenerator' => 'FastRoute\\DataGenerator\\GroupCountBased',
'dispatcher' => 'FastRoute\\Dispatcher\\GroupCountBased',
'routeCollector' => 'FastRoute\\RouteCollector',
'cacheDisabled' => false,
];
if (!isset($options['cacheFile'])) {
throw new \LogicException('Must specify "cacheFile" option');
}
if (!$options['cacheDisabled'] && file_exists($options['cacheFile'])) {
$dispatchData = require $options['cacheFile'];
if (!is_array($dispatchData)) {
throw new \RuntimeException('Invalid cache file "' . $options['cacheFile'] . '"');
}
return new $options['dispatcher']($dispatchData);
}
$routeCollector = new $options['routeCollector'](
new $options['routeParser'], new $options['dataGenerator']
);
$routeDefinitionCallback($routeCollector);
/** @var RouteCollector $routeCollector */
$dispatchData = $routeCollector->getData();
if (!$options['cacheDisabled']) {
file_put_contents(
$options['cacheFile'],
'<?php return ' . var_export($dispatchData, true) . ';'
);
}
return new $options['dispatcher']($dispatchData);
}
}