mirror of
https://github.com/dannyvankooten/AltoRouter.git
synced 2025-08-23 16:42:48 +02:00
Compare commits
51 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
9931b97642 | ||
|
41fcec6b8e | ||
|
9d62094f74 | ||
|
bc6b911a6d | ||
|
5159909a81 | ||
|
71389237e5 | ||
|
36f67da100 | ||
|
4270bb5ca2 | ||
|
77a2e14681 | ||
|
f327fbb5bf | ||
|
63bb784d76 | ||
|
bb7b009331 | ||
|
20674b8953 | ||
|
ac028a7f3f | ||
|
0a94ba4142 | ||
|
85c453b12c | ||
|
f6fede4f94 | ||
|
4efac02fad | ||
|
6ffb025022 | ||
|
127f6e9699 | ||
|
a80bb36f11 | ||
|
a51c74793a | ||
|
7e2b48db00 | ||
|
3a97b2c432 | ||
|
98e97407de | ||
|
17d00ed90e | ||
|
a34caaf60c | ||
|
a01166760c | ||
|
cd918de64b | ||
|
2360f527b0 | ||
|
24360d6162 | ||
|
dce6efdea2 | ||
|
ecb5f69042 | ||
|
9e9767e8fa | ||
|
87d93b6840 | ||
|
9bdfc55eb3 | ||
|
df96d7270c | ||
|
e2a07ec452 | ||
|
ca2d425bc6 | ||
|
281c5631db | ||
|
58299f4d0d | ||
|
1ff4165b28 | ||
|
689670f4dc | ||
|
72dd5199bd | ||
|
be64536dcb | ||
|
f18a57d481 | ||
|
e72248c744 | ||
|
693752b77a | ||
|
a8ee8e875f | ||
|
48524b67cb | ||
|
6fe6f4b196 |
20
.github/workflows/php-check-syntax.yml
vendored
Normal file
20
.github/workflows/php-check-syntax.yml
vendored
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
name: Check PHP syntax
|
||||||
|
|
||||||
|
on: [ push, pull_request ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
php-versions: ['7.3', '7.4', '8.0', '8.1', '8.2', '8.3', 'highest']
|
||||||
|
steps:
|
||||||
|
- name: Setup PHP
|
||||||
|
uses: shivammathur/setup-php@v2
|
||||||
|
with:
|
||||||
|
php-version: ${{ matrix.php-versions }}
|
||||||
|
|
||||||
|
- name: checkout repo
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- run: composer run check-syntax
|
39
.github/workflows/php.yml
vendored
Normal file
39
.github/workflows/php.yml
vendored
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
name: PHP
|
||||||
|
|
||||||
|
on: [ push, pull_request ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
php-versions: [ '7.3', 'highest' ]
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- uses: shivammathur/setup-php@v2
|
||||||
|
with:
|
||||||
|
php-version: ${{ matrix.php-versions }}
|
||||||
|
tools: composer
|
||||||
|
|
||||||
|
- name: Validate composer.json and composer.lock
|
||||||
|
run: composer validate
|
||||||
|
|
||||||
|
- name: Get composer cache directory
|
||||||
|
id: composer-cache
|
||||||
|
run: echo "::set-output name=dir::$(composer config cache-files-dir)"
|
||||||
|
|
||||||
|
- name: Cache dependencies
|
||||||
|
uses: actions/cache@v2
|
||||||
|
with:
|
||||||
|
path: ${{ steps.composer-cache.outputs.dir }}
|
||||||
|
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
|
||||||
|
restore-keys: ${{ runner.os }}-composer-
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
if: steps.composer-cache.outputs.cache-hit != 'true'
|
||||||
|
run: composer install --no-progress
|
||||||
|
|
||||||
|
- name: Run test suite
|
||||||
|
run: composer run-script test
|
1
.gitignore
vendored
1
.gitignore
vendored
@@ -6,3 +6,4 @@ build
|
|||||||
vendor
|
vendor
|
||||||
composer.lock
|
composer.lock
|
||||||
phpunit.xml
|
phpunit.xml
|
||||||
|
.phpunit.result.cache
|
22
.travis.yml
22
.travis.yml
@@ -1,22 +0,0 @@
|
|||||||
language: php
|
|
||||||
php:
|
|
||||||
- 5.3
|
|
||||||
- 5.4
|
|
||||||
- 5.5
|
|
||||||
- 5.6
|
|
||||||
- 7.0
|
|
||||||
- hhvm
|
|
||||||
install:
|
|
||||||
- composer update --prefer-source
|
|
||||||
script:
|
|
||||||
- ./vendor/bin/phpunit
|
|
||||||
after_script:
|
|
||||||
- ./vendor/bin/test-reporter
|
|
||||||
env:
|
|
||||||
global:
|
|
||||||
secure: XnXSc7nxJMIrm/EJ1KuwlN4f+sj2R/sR0IFHdOdbOfMKyZ/u6WEgZ3vNrdeAsisiC/QUJJ00DGku1pAl3t3Hzvam0N/SiHtXjB1ZLVbX00S1PEZ6Z+h9zoaUBXWoN6+0OdKN0Xjmj2lwvTpvUxUZXNabilOw0F9WS/+JasofqBQ=
|
|
||||||
sudo: false
|
|
||||||
cache:
|
|
||||||
directories:
|
|
||||||
- vendor
|
|
||||||
- $HOME/.composer/cache
|
|
500
AltoRouter.php
500
AltoRouter.php
@@ -1,294 +1,300 @@
|
|||||||
<?php
|
<?php
|
||||||
|
/*
|
||||||
|
MIT License
|
||||||
|
|
||||||
class AltoRouter {
|
Copyright (c) 2012 Danny van Kooten <hi@dannyvankooten.com>
|
||||||
|
|
||||||
/**
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
* @var array Array of all routes (incl. named routes).
|
|
||||||
*/
|
|
||||||
protected $routes = array();
|
|
||||||
|
|
||||||
/**
|
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||||
* @var array Array of all named routes.
|
|
||||||
*/
|
|
||||||
protected $namedRoutes = array();
|
|
||||||
|
|
||||||
/**
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
* @var string Can be used to ignore leading part of the Request URL (if main file lives in subdirectory of host)
|
*/
|
||||||
*/
|
|
||||||
protected $basePath = '';
|
|
||||||
|
|
||||||
/**
|
class AltoRouter
|
||||||
* @var array Array of default match types (regex helpers)
|
{
|
||||||
*/
|
|
||||||
protected $matchTypes = array(
|
|
||||||
'i' => '[0-9]++',
|
|
||||||
'a' => '[0-9A-Za-z]++',
|
|
||||||
'h' => '[0-9A-Fa-f]++',
|
|
||||||
'*' => '.+?',
|
|
||||||
'**' => '.++',
|
|
||||||
'' => '[^/\.]++'
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create router in one call from config.
|
* @var array Array of all routes (incl. named routes).
|
||||||
*
|
*/
|
||||||
* @param array $routes
|
protected $routes = [];
|
||||||
* @param string $basePath
|
|
||||||
* @param array $matchTypes
|
|
||||||
*/
|
|
||||||
public function __construct( $routes = array(), $basePath = '', $matchTypes = array() ) {
|
|
||||||
$this->addRoutes($routes);
|
|
||||||
$this->setBasePath($basePath);
|
|
||||||
$this->addMatchTypes($matchTypes);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves all routes.
|
* @var array Array of all named routes.
|
||||||
* Useful if you want to process or display routes.
|
*/
|
||||||
* @return array All routes.
|
protected $namedRoutes = [];
|
||||||
*/
|
|
||||||
public function getRoutes() {
|
|
||||||
return $this->routes;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add multiple routes at once from array in the following format:
|
* @var string Can be used to ignore leading part of the Request URL (if main file lives in subdirectory of host)
|
||||||
*
|
*/
|
||||||
* $routes = array(
|
protected $basePath = '';
|
||||||
* array($method, $route, $target, $name)
|
|
||||||
* );
|
|
||||||
*
|
|
||||||
* @param array $routes
|
|
||||||
* @return void
|
|
||||||
* @author Koen Punt
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
public function addRoutes($routes){
|
|
||||||
if(!is_array($routes) && !$routes instanceof Traversable) {
|
|
||||||
throw new \Exception('Routes should be an array or an instance of Traversable');
|
|
||||||
}
|
|
||||||
foreach($routes as $route) {
|
|
||||||
call_user_func_array(array($this, 'map'), $route);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the base path.
|
* @var array Array of default match types (regex helpers)
|
||||||
* Useful if you are running your application from a subdirectory.
|
*/
|
||||||
*/
|
protected $matchTypes = [
|
||||||
public function setBasePath($basePath) {
|
'i' => '[0-9]++',
|
||||||
$this->basePath = $basePath;
|
'a' => '[0-9A-Za-z]++',
|
||||||
}
|
'h' => '[0-9A-Fa-f]++',
|
||||||
|
'*' => '.+?',
|
||||||
|
'**' => '.++',
|
||||||
|
'' => '[^/\.]++'
|
||||||
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add named match types. It uses array_merge so keys can be overwritten.
|
* Create router in one call from config.
|
||||||
*
|
*
|
||||||
* @param array $matchTypes The key is the name and the value is the regex.
|
* @param array $routes
|
||||||
*/
|
* @param string $basePath
|
||||||
public function addMatchTypes($matchTypes) {
|
* @param array $matchTypes
|
||||||
$this->matchTypes = array_merge($this->matchTypes, $matchTypes);
|
* @throws Exception
|
||||||
}
|
*/
|
||||||
|
public function __construct(array $routes = [], string $basePath = '', array $matchTypes = [])
|
||||||
|
{
|
||||||
|
$this->addRoutes($routes);
|
||||||
|
$this->setBasePath($basePath);
|
||||||
|
$this->addMatchTypes($matchTypes);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Map a route to a target
|
* Retrieves all routes.
|
||||||
*
|
* Useful if you want to process or display routes.
|
||||||
* @param string $method One of 5 HTTP Methods, or a pipe-separated list of multiple HTTP Methods (GET|POST|PATCH|PUT|DELETE)
|
* @return array All routes.
|
||||||
* @param string $route The route regex, custom regex must start with an @. You can use multiple pre-set regex filters, like [i:id]
|
*/
|
||||||
* @param mixed $target The target where this route should point to. Can be anything.
|
public function getRoutes(): array
|
||||||
* @param string $name Optional name of this route. Supply if you want to reverse route this url in your application.
|
{
|
||||||
* @throws Exception
|
return $this->routes;
|
||||||
*/
|
}
|
||||||
public function map($method, $route, $target, $name = null) {
|
|
||||||
|
|
||||||
$this->routes[] = array($method, $route, $target, $name);
|
/**
|
||||||
|
* Add multiple routes at once from array in the following format:
|
||||||
|
*
|
||||||
|
* $routes = [
|
||||||
|
* [$method, $route, $target, $name]
|
||||||
|
* ];
|
||||||
|
*
|
||||||
|
* @param array $routes
|
||||||
|
* @return void
|
||||||
|
* @author Koen Punt
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public function addRoutes($routes)
|
||||||
|
{
|
||||||
|
if (!is_array($routes) && !$routes instanceof Traversable) {
|
||||||
|
throw new RuntimeException('Routes should be an array or an instance of Traversable');
|
||||||
|
}
|
||||||
|
foreach ($routes as $route) {
|
||||||
|
call_user_func_array([$this, 'map'], $route);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if($name) {
|
/**
|
||||||
if(isset($this->namedRoutes[$name])) {
|
* Set the base path.
|
||||||
throw new \Exception("Can not redeclare route '{$name}'");
|
* Useful if you are running your application from a subdirectory.
|
||||||
} else {
|
* @param string $basePath
|
||||||
$this->namedRoutes[$name] = $route;
|
*/
|
||||||
}
|
public function setBasePath(string $basePath)
|
||||||
|
{
|
||||||
|
$this->basePath = $basePath;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
/**
|
||||||
|
* Add named match types. It uses array_merge so keys can be overwritten.
|
||||||
|
*
|
||||||
|
* @param array $matchTypes The key is the name and the value is the regex.
|
||||||
|
*/
|
||||||
|
public function addMatchTypes(array $matchTypes)
|
||||||
|
{
|
||||||
|
$this->matchTypes = array_merge($this->matchTypes, $matchTypes);
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
/**
|
||||||
}
|
* Map a route to a target
|
||||||
|
*
|
||||||
|
* @param string $method One of 5 HTTP Methods, or a pipe-separated list of multiple HTTP Methods (GET|POST|PATCH|PUT|DELETE)
|
||||||
|
* @param string $route The route regex, custom regex must start with an @. You can use multiple pre-set regex filters, like [i:id]
|
||||||
|
* @param mixed $target The target where this route should point to. Can be anything.
|
||||||
|
* @param string $name Optional name of this route. Supply if you want to reverse route this url in your application.
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public function map(string $method, string $route, $target, ?string $name = null)
|
||||||
|
{
|
||||||
|
|
||||||
/**
|
$this->routes[] = [$method, $route, $target, $name];
|
||||||
* Reversed routing
|
|
||||||
*
|
|
||||||
* Generate the URL for a named route. Replace regexes with supplied parameters
|
|
||||||
*
|
|
||||||
* @param string $routeName The name of the route.
|
|
||||||
* @param array @params Associative array of parameters to replace placeholders with.
|
|
||||||
* @return string The URL of the route with named parameters in place.
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
public function generate($routeName, array $params = array()) {
|
|
||||||
|
|
||||||
// Check if named route exists
|
if ($name) {
|
||||||
if(!isset($this->namedRoutes[$routeName])) {
|
if (isset($this->namedRoutes[$name])) {
|
||||||
throw new \Exception("Route '{$routeName}' does not exist.");
|
throw new RuntimeException("Can not redeclare route '{$name}'");
|
||||||
}
|
}
|
||||||
|
$this->namedRoutes[$name] = $route;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Replace named parameters
|
/**
|
||||||
$route = $this->namedRoutes[$routeName];
|
* Reversed routing
|
||||||
|
*
|
||||||
|
* Generate the URL for a named route. Replace regexes with supplied parameters
|
||||||
|
*
|
||||||
|
* @param string $routeName The name of the route.
|
||||||
|
* @param array $params Associative array of parameters to replace placeholders with.
|
||||||
|
* @return string The URL of the route with named parameters in place.
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public function generate(string $routeName, array $params = []): string
|
||||||
|
{
|
||||||
|
|
||||||
// prepend base path to route url again
|
// Check if named route exists
|
||||||
$url = $this->basePath . $route;
|
if (!isset($this->namedRoutes[$routeName])) {
|
||||||
|
throw new RuntimeException("Route '{$routeName}' does not exist.");
|
||||||
|
}
|
||||||
|
|
||||||
if (preg_match_all('`(/|\.|)\[([^:\]]*+)(?::([^:\]]*+))?\](\?|)`', $route, $matches, PREG_SET_ORDER)) {
|
// Replace named parameters
|
||||||
|
$route = $this->namedRoutes[$routeName];
|
||||||
|
|
||||||
foreach($matches as $match) {
|
// prepend base path to route url again
|
||||||
list($block, $pre, $type, $param, $optional) = $match;
|
$url = $this->basePath . $route;
|
||||||
|
|
||||||
if ($pre) {
|
if (preg_match_all('`(/|\.|)\[([^:\]]*+)(?::([^:\]]*+))?\](\?|)`', $route, $matches, PREG_SET_ORDER)) {
|
||||||
$block = substr($block, 1);
|
foreach ($matches as $index => $match) {
|
||||||
}
|
list($block, $pre, $type, $param, $optional) = $match;
|
||||||
|
|
||||||
if(isset($params[$param])) {
|
if ($pre) {
|
||||||
$url = str_replace($block, $params[$param], $url);
|
$block = substr($block, 1);
|
||||||
} elseif ($optional) {
|
}
|
||||||
$url = str_replace($pre . $block, '', $url);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (isset($params[$param])) {
|
||||||
|
// Part is found, replace for param value
|
||||||
|
$url = str_replace($block, $params[$param], $url);
|
||||||
|
} elseif ($optional && $index !== 0) {
|
||||||
|
// Only strip preceding slash if it's not at the base
|
||||||
|
$url = str_replace($pre . $block, '', $url);
|
||||||
|
} else {
|
||||||
|
// Strip match block
|
||||||
|
$url = str_replace($block, '', $url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
return $url;
|
||||||
|
}
|
||||||
|
|
||||||
return $url;
|
/**
|
||||||
}
|
* Match a given Request Url against stored routes
|
||||||
|
* @param string $requestUrl
|
||||||
|
* @param string $requestMethod
|
||||||
|
* @return array|boolean Array with route information on success, false on failure (no match).
|
||||||
|
*/
|
||||||
|
public function match(?string $requestUrl = null, ?string $requestMethod = null)
|
||||||
|
{
|
||||||
|
|
||||||
/**
|
$params = [];
|
||||||
* Match a given Request Url against stored routes
|
|
||||||
* @param string $requestUrl
|
|
||||||
* @param string $requestMethod
|
|
||||||
* @return array|boolean Array with route information on success, false on failure (no match).
|
|
||||||
*/
|
|
||||||
public function match($requestUrl = null, $requestMethod = null) {
|
|
||||||
|
|
||||||
$params = array();
|
// set Request Url if it isn't passed as parameter
|
||||||
$match = false;
|
if ($requestUrl === null) {
|
||||||
|
$requestUrl = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '/';
|
||||||
|
}
|
||||||
|
|
||||||
// set Request Url if it isn't passed as parameter
|
// strip base path from request url
|
||||||
if($requestUrl === null) {
|
$requestUrl = substr($requestUrl, strlen($this->basePath));
|
||||||
$requestUrl = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '/';
|
|
||||||
}
|
|
||||||
|
|
||||||
// strip base path from request url
|
// Strip query string (?a=b) from Request Url
|
||||||
$requestUrl = substr($requestUrl, strlen($this->basePath));
|
if (($strpos = strpos($requestUrl, '?')) !== false) {
|
||||||
|
$requestUrl = substr($requestUrl, 0, $strpos);
|
||||||
|
}
|
||||||
|
|
||||||
// Strip query string (?a=b) from Request Url
|
$lastRequestUrlChar = $requestUrl ? $requestUrl[strlen($requestUrl)-1] : '';
|
||||||
if (($strpos = strpos($requestUrl, '?')) !== false) {
|
|
||||||
$requestUrl = substr($requestUrl, 0, $strpos);
|
|
||||||
}
|
|
||||||
|
|
||||||
// set Request Method if it isn't passed as a parameter
|
// set Request Method if it isn't passed as a parameter
|
||||||
if($requestMethod === null) {
|
if ($requestMethod === null) {
|
||||||
$requestMethod = isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : 'GET';
|
$requestMethod = isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : 'GET';
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach($this->routes as $handler) {
|
foreach ($this->routes as $handler) {
|
||||||
list($method, $_route, $target, $name) = $handler;
|
list($methods, $route, $target, $name) = $handler;
|
||||||
|
|
||||||
$methods = explode('|', $method);
|
$method_match = (stripos($methods, $requestMethod) !== false);
|
||||||
$method_match = false;
|
|
||||||
|
|
||||||
// Check if request method matches. If not, abandon early. (CHEAP)
|
// Method did not match, continue to next route.
|
||||||
foreach($methods as $method) {
|
if (!$method_match) {
|
||||||
if (strcasecmp($requestMethod, $method) === 0) {
|
continue;
|
||||||
$method_match = true;
|
}
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Method did not match, continue to next route.
|
if ($route === '*') {
|
||||||
if(!$method_match) continue;
|
// * wildcard (matches all)
|
||||||
|
$match = true;
|
||||||
|
} elseif (isset($route[0]) && $route[0] === '@') {
|
||||||
|
// @ regex delimiter
|
||||||
|
$pattern = '`' . substr($route, 1) . '`u';
|
||||||
|
$match = preg_match($pattern, $requestUrl, $params) === 1;
|
||||||
|
} elseif (($position = strpos($route, '[')) === false) {
|
||||||
|
// No params in url, do string comparison
|
||||||
|
$match = strcmp($requestUrl, $route) === 0;
|
||||||
|
} else {
|
||||||
|
// Compare longest non-param string with url before moving on to regex
|
||||||
|
// Check if last character before param is a slash, because it could be optional if param is optional too (see https://github.com/dannyvankooten/AltoRouter/issues/241)
|
||||||
|
if (strncmp($requestUrl, $route, $position) !== 0 && ($lastRequestUrlChar === '/' || $route[$position-1] !== '/')) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Check for a wildcard (matches all)
|
$regex = $this->compileRoute($route);
|
||||||
if ($_route === '*') {
|
$match = preg_match($regex, $requestUrl, $params) === 1;
|
||||||
$match = true;
|
}
|
||||||
} elseif (isset($_route[0]) && $_route[0] === '@') {
|
|
||||||
$pattern = '`' . substr($_route, 1) . '`u';
|
|
||||||
$match = preg_match($pattern, $requestUrl, $params);
|
|
||||||
} else {
|
|
||||||
$route = null;
|
|
||||||
$regex = false;
|
|
||||||
$j = 0;
|
|
||||||
$n = isset($_route[0]) ? $_route[0] : null;
|
|
||||||
$i = 0;
|
|
||||||
|
|
||||||
// Find the longest non-regex substring and match it against the URI
|
if ($match) {
|
||||||
while (true) {
|
if ($params) {
|
||||||
if (!isset($_route[$i])) {
|
foreach ($params as $key => $value) {
|
||||||
break;
|
if (is_numeric($key)) {
|
||||||
} elseif (false === $regex) {
|
unset($params[$key]);
|
||||||
$c = $n;
|
}
|
||||||
$regex = $c === '[' || $c === '(' || $c === '.';
|
}
|
||||||
if (false === $regex && false !== isset($_route[$i+1])) {
|
}
|
||||||
$n = $_route[$i + 1];
|
|
||||||
$regex = $n === '?' || $n === '+' || $n === '*' || $n === '{';
|
|
||||||
}
|
|
||||||
if (false === $regex && $c !== '/' && (!isset($requestUrl[$j]) || $c !== $requestUrl[$j])) {
|
|
||||||
continue 2;
|
|
||||||
}
|
|
||||||
$j++;
|
|
||||||
}
|
|
||||||
$route .= $_route[$i++];
|
|
||||||
}
|
|
||||||
|
|
||||||
$regex = $this->compileRoute($route);
|
return [
|
||||||
$match = preg_match($regex, $requestUrl, $params);
|
'target' => $target,
|
||||||
}
|
'params' => $params,
|
||||||
|
'name' => $name
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(($match == true || $match > 0)) {
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if($params) {
|
/**
|
||||||
foreach($params as $key => $value) {
|
* Compile the regex for a given route (EXPENSIVE)
|
||||||
if(is_numeric($key)) unset($params[$key]);
|
* @param string $route
|
||||||
}
|
* @return string
|
||||||
}
|
*/
|
||||||
|
protected function compileRoute(string $route): string
|
||||||
|
{
|
||||||
|
if (preg_match_all('`(/|\.|)\[([^:\]]*+)(?::([^:\]]*+))?\](\?|)`', $route, $matches, PREG_SET_ORDER)) {
|
||||||
|
$matchTypes = $this->matchTypes;
|
||||||
|
foreach ($matches as $match) {
|
||||||
|
list($block, $pre, $type, $param, $optional) = $match;
|
||||||
|
|
||||||
return array(
|
if (isset($matchTypes[$type])) {
|
||||||
'target' => $target,
|
$type = $matchTypes[$type];
|
||||||
'params' => $params,
|
}
|
||||||
'name' => $name
|
if ($pre === '.') {
|
||||||
);
|
$pre = '\.';
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
$optional = $optional !== '' ? '?' : null;
|
||||||
* Compile the regex for a given route (EXPENSIVE)
|
|
||||||
*/
|
|
||||||
private function compileRoute($route) {
|
|
||||||
if (preg_match_all('`(/|\.|)\[([^:\]]*+)(?::([^:\]]*+))?\](\?|)`', $route, $matches, PREG_SET_ORDER)) {
|
|
||||||
|
|
||||||
$matchTypes = $this->matchTypes;
|
//Older versions of PCRE require the 'P' in (?P<named>)
|
||||||
foreach($matches as $match) {
|
$pattern = '(?:'
|
||||||
list($block, $pre, $type, $param, $optional) = $match;
|
. ($pre !== '' ? $pre : null)
|
||||||
|
. '('
|
||||||
|
. ($param !== '' ? "?P<$param>" : null)
|
||||||
|
. $type
|
||||||
|
. ')'
|
||||||
|
. $optional
|
||||||
|
. ')'
|
||||||
|
. $optional;
|
||||||
|
|
||||||
if (isset($matchTypes[$type])) {
|
$route = str_replace($block, $pattern, $route);
|
||||||
$type = $matchTypes[$type];
|
}
|
||||||
}
|
}
|
||||||
if ($pre === '.') {
|
return "`^$route$`u";
|
||||||
$pre = '\.';
|
}
|
||||||
}
|
|
||||||
|
|
||||||
//Older versions of PCRE require the 'P' in (?P<named>)
|
|
||||||
$pattern = '(?:'
|
|
||||||
. ($pre !== '' ? $pre : null)
|
|
||||||
. '('
|
|
||||||
. ($param !== '' ? "?P<$param>" : null)
|
|
||||||
. $type
|
|
||||||
. '))'
|
|
||||||
. ($optional !== '' ? '?' : null);
|
|
||||||
|
|
||||||
$route = str_replace($block, $pattern, $route);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
return "`^$route$`u";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
9
LICENSE.md
Normal file
9
LICENSE.md
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2012 Danny van Kooten <hi@dannyvankooten.com>
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
32
README.md
32
README.md
@@ -1,19 +1,23 @@
|
|||||||
# AltoRouter [](http://travis-ci.org/dannyvankooten/AltoRouter) [](https://packagist.org/packages/altorouter/altorouter) [](https://packagist.org/packages/altorouter/altorouter) [](https://codeclimate.com/github/dannyvankooten/AltoRouter) [](https://codeclimate.com/github/dannyvankooten/AltoRouter)
|
# AltoRouter  [](https://packagist.org/packages/altorouter/altorouter) [](https://packagist.org/packages/altorouter/altorouter)
|
||||||
AltoRouter is a small but powerful routing class for PHP 5.3+, heavily inspired by [klein.php](https://github.com/chriso/klein.php/).
|
|
||||||
|
AltoRouter is a small but powerful routing class, heavily inspired by [klein.php](https://github.com/chriso/klein.php/).
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$router = new AltoRouter();
|
$router = new AltoRouter();
|
||||||
|
|
||||||
// map homepage
|
// map homepage
|
||||||
$router->map( 'GET', '/', function() {
|
$router->map('GET', '/', function() {
|
||||||
require __DIR__ . '/views/home.php';
|
require __DIR__ . '/views/home.php';
|
||||||
});
|
});
|
||||||
|
|
||||||
// map users details page
|
// dynamic named route
|
||||||
$router->map( 'GET|POST', '/users/[i:id]/', function( $id ) {
|
$router->map('GET|POST', '/users/[i:id]/', function($id) {
|
||||||
$user = .....
|
$user = .....
|
||||||
require __DIR__ . '/views/user/details.php';
|
require __DIR__ . '/views/user/details.php';
|
||||||
});
|
}, 'user-details');
|
||||||
|
|
||||||
|
// echo URL to user-details page for ID 5
|
||||||
|
echo $router->generate('user-details', ['id' => 5]); // Output: "/users/5"
|
||||||
```
|
```
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
@@ -26,13 +30,13 @@ $router->map( 'GET|POST', '/users/[i:id]/', function( $id ) {
|
|||||||
|
|
||||||
## Getting started
|
## Getting started
|
||||||
|
|
||||||
You need PHP >= 5.3 to use AltoRouter.
|
You need PHP >= 7.3 to use AltoRouter, although we highly recommend you [use an officially supported PHP version](https://secure.php.net/supported-versions.php) that is not EOL.
|
||||||
|
|
||||||
- [Install AltoRouter](http://altorouter.com/usage/install.html)
|
- [Install AltoRouter](https://dannyvankooten.github.io/AltoRouter//usage/install.html)
|
||||||
- [Rewrite all requests to AltoRouter](http://altorouter.com/usage/rewrite-requests.html)
|
- [Rewrite all requests to AltoRouter](https://dannyvankooten.github.io/AltoRouter//usage/rewrite-requests.html)
|
||||||
- [Map your routes](http://altorouter.com/usage/mapping-routes.html)
|
- [Map your routes](https://dannyvankooten.github.io/AltoRouter//usage/mapping-routes.html)
|
||||||
- [Match requests](http://altorouter.com/usage/matching-requests.html)
|
- [Match requests](https://dannyvankooten.github.io/AltoRouter//usage/matching-requests.html)
|
||||||
- [Process the request your preferred way](http://altorouter.com/usage/processing-requests.html)
|
- [Process the request your preferred way](https://dannyvankooten.github.io/AltoRouter//usage/processing-requests.html)
|
||||||
|
|
||||||
## Contributors
|
## Contributors
|
||||||
- [Danny van Kooten](https://github.com/dannyvankooten)
|
- [Danny van Kooten](https://github.com/dannyvankooten)
|
||||||
@@ -42,9 +46,9 @@ You need PHP >= 5.3 to use AltoRouter.
|
|||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
(MIT License)
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2012-2015 Danny van Kooten <hi@dannyvankooten.com>
|
Copyright (c) 2012 Danny van Kooten <hi@dannyvankooten.com>
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
@@ -20,16 +20,17 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"require": {
|
"require": {
|
||||||
"php": ">=5.3.0"
|
"php": ">=7.3"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"phpunit/phpunit": "4.5.*",
|
"phpunit/phpunit": "9.6.*",
|
||||||
"codeclimate/php-test-reporter": "dev-master"
|
"squizlabs/php_codesniffer": "3.6.2"
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"classmap": ["AltoRouter.php"]
|
"classmap": ["AltoRouter.php"]
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "vendor/bin/phpunit"
|
"test": "vendor/bin/phpunit",
|
||||||
|
"check-syntax": "find . -name '*.php' -not -path './vendor/*' -print0 | xargs -0 -n1 php -l"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,13 +1,21 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
require '../../AltoRouter.php';
|
require __DIR__ . '/../../AltoRouter.php';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This can be useful if you're using PHP's built-in web server, to serve files like images or css
|
||||||
|
* @link https://secure.php.net/manual/en/features.commandline.webserver.php
|
||||||
|
*/
|
||||||
|
if (file_exists($_SERVER['SCRIPT_FILENAME']) && pathinfo($_SERVER['SCRIPT_FILENAME'], PATHINFO_EXTENSION) !== 'php') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
$router = new AltoRouter();
|
$router = new AltoRouter();
|
||||||
$router->setBasePath('/AltoRouter/examples/basic');
|
$router->setBasePath('/AltoRouter/examples/basic');
|
||||||
$router->map('GET|POST','/', 'home#index', 'home');
|
$router->map('GET|POST', '/', 'home#index', 'home');
|
||||||
$router->map('GET','/users/', array('c' => 'UserController', 'a' => 'ListAction'));
|
$router->map('GET', '/users/', ['c' => 'UserController', 'a' => 'ListAction']);
|
||||||
$router->map('GET','/users/[i:id]', 'users#show', 'users_show');
|
$router->map('GET', '/users/[i:id]', 'users#show', 'users_show');
|
||||||
$router->map('POST','/users/[i:id]/[delete|update:action]', 'usersController#doAction', 'users_do');
|
$router->map('POST', '/users/[i:id]/[delete|update:action]', 'usersController#doAction', 'users_do');
|
||||||
|
|
||||||
// match current request
|
// match current request
|
||||||
$match = $router->match();
|
$match = $router->match();
|
||||||
@@ -16,12 +24,12 @@ $match = $router->match();
|
|||||||
|
|
||||||
<h3>Current request: </h3>
|
<h3>Current request: </h3>
|
||||||
<pre>
|
<pre>
|
||||||
Target: <?php var_dump($match['target']); ?>
|
Target: <?php var_dump($match['target']); ?>
|
||||||
Params: <?php var_dump($match['params']); ?>
|
Params: <?php var_dump($match['params']); ?>
|
||||||
Name: <?php var_dump($match['name']); ?>
|
Name: <?php var_dump($match['name']); ?>
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
<h3>Try these requests: </h3>
|
<h3>Try these requests: </h3>
|
||||||
<p><a href="<?php echo $router->generate('home'); ?>">GET <?php echo $router->generate('home'); ?></a></p>
|
<p><a href="<?php echo $router->generate('home'); ?>">GET <?php echo $router->generate('home'); ?></a></p>
|
||||||
<p><a href="<?php echo $router->generate('users_show', array('id' => 5)); ?>">GET <?php echo $router->generate('users_show', array('id' => 5)); ?></a></p>
|
<p><a href="<?php echo $router->generate('users_show', ['id' => 5]); ?>">GET <?php echo $router->generate('users_show', ['id' => 5]); ?></a></p>
|
||||||
<p><form action="<?php echo $router->generate('users_do', array('id' => 10, 'action' => 'update')); ?>" method="post"><button type="submit"><?php echo $router->generate('users_do', array('id' => 10, 'action' => 'update')); ?></button></form></p>
|
<p><form action="<?php echo $router->generate('users_do', ['id' => 10, 'action' => 'update']); ?>" method="post"><button type="submit"><?php echo $router->generate('users_do', ['id' => 10, 'action' => 'update']); ?></button></form></p>
|
||||||
|
10
phpcs.xml
Normal file
10
phpcs.xml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<ruleset name="rules">
|
||||||
|
<description>rules</description>
|
||||||
|
<rule ref="PSR2"/>
|
||||||
|
<rule ref="Generic.Arrays.DisallowLongArraySyntax"/>
|
||||||
|
<file>tests</file>
|
||||||
|
<file>AltoRouter.php</file>
|
||||||
|
<file>examples/</file>
|
||||||
|
<arg name="colors"/>
|
||||||
|
</ruleset>
|
@@ -1,17 +1,17 @@
|
|||||||
<phpunit
|
<?xml version="1.0"?>
|
||||||
colors="true"
|
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" colors="true" verbose="true" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd">
|
||||||
verbose="true">
|
<coverage>
|
||||||
<testsuites>
|
<include>
|
||||||
<testsuite name="altorouter">
|
<file>./AltoRouter.php</file>
|
||||||
<directory>./tests/</directory>
|
</include>
|
||||||
</testsuite>
|
<report>
|
||||||
</testsuites>
|
<clover outputFile="build/logs/clover.xml"/>
|
||||||
<filter>
|
</report>
|
||||||
<blacklist>
|
</coverage>
|
||||||
<directory>./vendor/</directory>
|
<testsuites>
|
||||||
</blacklist>
|
<testsuite name="altorouter">
|
||||||
</filter>
|
<directory>./tests/</directory>
|
||||||
<logging>
|
</testsuite>
|
||||||
<log type="coverage-clover" target="build/logs/clover.xml"/>
|
</testsuites>
|
||||||
</logging>
|
<logging/>
|
||||||
</phpunit>
|
</phpunit>
|
File diff suppressed because it is too large
Load Diff
106
tests/benchmark-parse-api.php
Normal file
106
tests/benchmark-parse-api.php
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Benchmark Altorouter
|
||||||
|
*
|
||||||
|
* Usage: php ./tests/benchmark-parse-api.php <iterations>
|
||||||
|
*
|
||||||
|
* Options:
|
||||||
|
*
|
||||||
|
* <iterations>:
|
||||||
|
* The number of routes to map & match. Defaults to 1000.
|
||||||
|
*/
|
||||||
|
|
||||||
|
require __DIR__ . '/../vendor/autoload.php';
|
||||||
|
$routes = [
|
||||||
|
["POST", "/1/classes/[a:className]"],
|
||||||
|
["GET", "/1/classes/[a:className]/[i:objectId]"],
|
||||||
|
["PUT", "/1/classes/[a:className]/[i:objectId]"],
|
||||||
|
["GET", "/1/classes/[a:className]"],
|
||||||
|
["DELETE", "/1/classes/[a:className]/[i:objectId]"],
|
||||||
|
|
||||||
|
// Users
|
||||||
|
["POST", "/1/users"],
|
||||||
|
["GET", "/1/login"],
|
||||||
|
["GET", "/1/users/[i:objectId]"],
|
||||||
|
["PUT", "/1/users/[i:objectId]"],
|
||||||
|
["GET", "/1/users"],
|
||||||
|
["DELETE", "/1/users/[i:objectId]"],
|
||||||
|
["POST", "/1/requestPasswordReset"],
|
||||||
|
|
||||||
|
// Roles
|
||||||
|
["POST", "/1/roles"],
|
||||||
|
["GET", "/1/roles/[i:objectId]"],
|
||||||
|
["PUT", "/1/roles/[i:objectId]"],
|
||||||
|
["GET", "/1/roles"],
|
||||||
|
["DELETE", "/1/roles/[i:objectId]"],
|
||||||
|
|
||||||
|
// Files
|
||||||
|
["POST", "/1/files/:fileName"],
|
||||||
|
|
||||||
|
// Analytics
|
||||||
|
["POST", "/1/events/[a:eventName]"],
|
||||||
|
|
||||||
|
// Push Notifications
|
||||||
|
["POST", "/1/push"],
|
||||||
|
|
||||||
|
// Installations
|
||||||
|
["POST", "/1/installations"],
|
||||||
|
["GET", "/1/installations/[i:objectId]"],
|
||||||
|
["PUT", "/1/installations/[i:objectId]"],
|
||||||
|
["GET", "/1/installations"],
|
||||||
|
["DELETE", "/1/installations/[i:objectId]"],
|
||||||
|
|
||||||
|
// Cloud Functions
|
||||||
|
["POST", "/1/functions"],
|
||||||
|
];
|
||||||
|
$total_time = 0;
|
||||||
|
$router = new AltoRouter();
|
||||||
|
|
||||||
|
// map requests
|
||||||
|
$start = microtime(true);
|
||||||
|
foreach ($routes as $r) {
|
||||||
|
$router->map($r[0], $r[1], '');
|
||||||
|
}
|
||||||
|
$end = microtime(true);
|
||||||
|
$time = $end - $start;
|
||||||
|
$total_time += $time;
|
||||||
|
echo sprintf('Map time: %.3f ms', $time * 1000) . PHP_EOL;
|
||||||
|
|
||||||
|
// match a static route
|
||||||
|
$start = microtime(true);
|
||||||
|
$router->match('/1/login', 'GET');
|
||||||
|
$end = microtime(true);
|
||||||
|
$time = $end - $start;
|
||||||
|
$total_time += $time;
|
||||||
|
echo sprintf('Match time (existing route, no params): %.3f ms', $time * 1000) . PHP_EOL;
|
||||||
|
|
||||||
|
// match a route with 1 parameter
|
||||||
|
$start = microtime(true);
|
||||||
|
$res = $router->match('/1/classes/foo', 'GET');
|
||||||
|
$end = microtime(true);
|
||||||
|
$time = $end - $start;
|
||||||
|
$total_time += $time;
|
||||||
|
echo sprintf('Match time (existing route, 1 param): %.3f ms', $time * 1000) . PHP_EOL;
|
||||||
|
|
||||||
|
// match a route with 2 parameters
|
||||||
|
$start = microtime(true);
|
||||||
|
$res = $router->match('/1/classes/foo/500', 'GET');
|
||||||
|
$end = microtime(true);
|
||||||
|
$time = $end - $start;
|
||||||
|
$total_time += $time;
|
||||||
|
echo sprintf('Match time (existing route, 2 params): %.3f ms', $time * 1000) . PHP_EOL;
|
||||||
|
|
||||||
|
|
||||||
|
// match unexisting route
|
||||||
|
$start = microtime(true);
|
||||||
|
$router->match('/55-foo-bar', 'GET');
|
||||||
|
$end = microtime(true);
|
||||||
|
$time = $end - $start;
|
||||||
|
$total_time += $time;
|
||||||
|
echo sprintf('Match time (unexisting route): %.3f ms', $time * 1000) . PHP_EOL;
|
||||||
|
|
||||||
|
// print totals
|
||||||
|
echo sprintf('Total time: %.3f ms', $total_time * 1000) . PHP_EOL;
|
||||||
|
echo sprintf('Memory usage: %d KB', round(memory_get_usage() / 1024)) . PHP_EOL;
|
||||||
|
echo sprintf('Peak memory usage: %d KB', round(memory_get_peak_usage(true) / 1024)) . PHP_EOL;
|
Reference in New Issue
Block a user