Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

a very lightweight and fast speed php request router

License

Notifications You must be signed in to change notification settings

webdev123456/php-srouter

Repository files navigation

php simple router

License Php Version Latest Stable Version

非常轻量级的路由器。无依赖、简洁、速度快、自定义性强

  • 轻量级且速度快,查找速度不受路由数量的影响
  • 支持路由组。支持路由参数定义,以及丰富的自定义路由选项(比如设定 默认值、domains、schemas等检查限制)
  • 支持请求方法: GET POST PUT DELETE HEAD OPTIONS ...
  • 支持自动匹配路由到控制器就像 Yii 一样, 请参看配置项 autoRoute.
  • 三个版本:静态版本 SRouter, 对象版本 ORouter, 支持路由缓存的对象版本 CachedRouter

内置了一个调度器:

  • 支持事件: found notFound execStart execEnd execError. 当触发事件时你可以做一些事情(比如记录日志等)
  • 支持动态获取action名。支持设置方法执行器(actionExecutor),通过方法执行器来自定义调用真实请求方法.
  • 支持通过方法 SRouter::dispatch($path, $method) 手动调度一个路由
  • 你即使不配置任何东西, 它也能很好的工作

EN README

项目地址

安装

  • composer 命令
composer require inhere/sroute
  • composer.json
{
 "require": {
 "inhere/sroute": "dev-master"
 }
}
  • 直接拉取
git clone https://github.com/inhere/php-srouter.git // github
git clone https://git.oschina.net/inhere/php-srouter.git // git@osc

压测

自动生成了1000条路由,每条有9个参数位,分别测试1000次的

  • 第一条路由匹配
  • 最后一条路由匹配
  • 不会匹配到的路由

压测结果

Worst-case matching

This benchmark matches the last route and unknown route. It generates a randomly prefixed and suffixed route in an attempt to thwart any optimization. 1,000 routes each with 9 arguments.

This benchmark consists of 14 tests. Each test is executed 1,000 times, the results pruned, and then averaged. Values that fall outside of 3 standard deviations of the mean are discarded.

Test Name Results Time + Interval Change
ORouter - unknown route (1000 routes) 988 0.0000120063 +0.0000000000 baseline
ORouter - last route (1000 routes) 988 0.0000122867 +0.0000002804 2% slower
SRouter - unknown route (1000 routes) 983 0.0000123633 +0.0000003570 3% slower
SRouter - last route (1000 routes) 998 0.0000142205 +0.0000022142 18% slower
Symfony2 Dumped - last route (1000 routes) 990 0.0000468579 +0.0000348516 290% slower
Symfony2 Dumped - unknown route (1000 routes) 995 0.0000490268 +0.0000370205 308% slower
FastRoute - unknown route (1000 routes) 968 0.0001358227 +0.0001238164 1031% slower
FastRoute(cached) - last route (1000 routes) 999 0.0001397746 +0.0001277683 1064% slower
FastRoute(cached) - unknown route (1000 routes) 960 0.0001424064 +0.0001304001 1086% slower
FastRoute - last route (1000 routes) 999 0.0001659009 +0.0001538946 1282% slower
Pux PHP - unknown route (1000 routes) 964 0.0013507533 +0.0013387470 11150% slower
Pux PHP - last route (1000 routes) 999 0.0014749475 +0.0014629412 12185% slower
Symfony2 - unknown route (1000 routes) 979 0.0038350259 +0.0038230196 31842% slower
Symfony2 - last route (1000 routes) 999 0.0040060059 +0.0039939995 33266% slower

First route matching

This benchmark tests how quickly each router can match the first route. 1,000 routes each with 9 arguments.

This benchmark consists of 7 tests. Each test is executed 1,000 times, the results pruned, and then averaged. Values that fall outside of 3 standard deviations of the mean are discarded.

Test Name Results Time + Interval Change
Pux PHP - first route(1000) 993 0.0000105502 +0.0000000000 baseline
ORouter - first route(1000) 984 0.0000118334 +0.0000012832 12% slower
SRouter - first route(1000) 982 0.0000118473 +0.0000012971 12% slower
FastRoute(cached) - first route(1000) 999 0.0000143361 +0.0000037859 36% slower
FastRoute - first route(1000) 999 0.0000143980 +0.0000038477 36% slower
Symfony2 Dumped - first route 993 0.0000350874 +0.0000245372 233% slower
Symfony2 - first route 999 0.0000630564 +0.0000525061 498% slower

使用

  • Inhere\Route\SRouter 是静态类版本
  • Inhere\Route\ORouter 是对象版本

两个类的方法名和参数都是一样的

首先, 导入类

use Inhere\Route\SRouter;

添加路由

// 匹配 GET 请求. 处理器是个闭包 Closure
SRouter::get('/', function() {
 echo 'hello';
});
// 匹配参数 'test/john'
SRouter::get('/test/{name}', function($params) {
 echo $params['name']; // 'john'
}, [
 'params' => [
 'name' => '\w+', // 添加参数匹配限制。若不添加对应的限制,将会自动设置为匹配除了'/'外的任何字符
 ]
]);
// 可选参数支持。匹配 'hello' 'hello/john'
SRouter::get('/hello[/{name}]', function() {
 echo $params['name'] ?? 'No input'; // 'john'
}, [
 'params' => [
 'name' => '\w+', // 添加参数匹配限制
 ]
]);
// 匹配 POST 请求
SRouter::post('/user/login', function() {
 var_dump($_POST);
});
// 匹配 GET 或者 POST
SRouter::map(['get', 'post'], '/user/login', function() {
 var_dump($_GET, $_POST);
});
// 允许任何请求方法
SRouter::any('/home', function() {
 echo 'hello, you request page is /home';
});
SRouter::any('/404', function() {
 echo "Sorry,This page not found.";
});
// 路由组
SRouter::group('/user', function () {
 SRouter::get('/', function () {
 echo 'hello. you access: /user/';
 });
 SRouter::get('/index', function () {
 echo 'hello. you access: /user/index';
 });
});
// 使用 控制器
SRouter::get('/', app\controllers\HomeController::class);
SRouter::get('/index', 'app\controllers\HomeController@index');

如果配置了 'ignoreLastSep' => true, '/index' 等同于 '/index/'

添加路由方法

public function map(string|array $methods, string $route, mixed $handler, array $opts = [])

添加路由方法

其他的添加路由方法底层都是调用的 map() 方法,除了没有第一个参数外,其他参数都是一样的

  • $methods string|array 请求的METHOD. e.g GET ['GET', 'POST]
  • $route string 定义的路由字符串 e.g /user/login /article/{id}
  • $handler string|object 对应路由的处理者
  • $opts array 选项设置,可以添加自定义的数据。匹配成功会将选项数据返回(e.g middleware, domains),自己再做进一步验证等。下面是已使用的选项
    • params 添加路由时设置的参数匹配信息, 若有的话 e.g 'name' => '\w+'
    • defaults 有可选参数时,可以设置默认值

一个较为完整的示例:

提示: 若不在选项中设置参数匹配,那么参数将会匹配除了 '/' 之外的任何字符

$router->map(['get', 'post'], '/im/{name}[/{age}]', function(array $params) {
 var_dump($params);
}, [
 // 设置参数匹配
 'params' => [
 'name' => '\w+',
 'age' => '\d+',
 ],
 'defaults' => [
 'age' => 20, // 给可选参数 age 添加一个默认值
 ]
 
 // 可添加更多自定义设置
 'middleware' => ['AuthCheck'],
 'domains' => ['localhost'],
 ... ...
]);

Now, 访问 /im/john/18 或者 /im/john 查看效果

自动匹配路由

支持根据请求的URI自动匹配路由(就像 yii 一样), 需配置 autoRoute.

 'autoRoute' => 1, // 启用
 'controllerNamespace' => 'App\\Controllers', // 控制器类所在命名空间
 'controllerSuffix' => 'Controller', // 控制器类后缀

请参看示例 example 中的使用

此时请求没有配置路由的 /demo /demo/test。将会自动尝试从 App\\Controllers 命名空间下去查找 DemoController

查找逻辑是

  • 只有一节的(如/demo),直接定义它为控制器类名进行查找
  • 大于等于两节的默认先认为最后一节是控制器类名,进行查找
  • 若失败,再尝试将倒数第二节认为是控制器名,最后一节是action名

匹配所有

配置 intercept 可用于拦截所有请求。 (例如网站维护时)

可允许配置 intercept 的值为

  • 路由path
 'intercept' => '/about', // a route path

将会直接执行此路由后停止执行

  • 回调
 'intercept' => function () {
 echo '系统维护中 :)';
 },

将会直接执行此回调后停止执行

设置路由配置

// set config
SRouter::setConfig([
 'ignoreLastSep' => true, 
 'autoRoute' => 1,
 'controllerNamespace' => 'app\\controllers',
 'controllerSuffix' => 'Controller',
]);

NOTICE: 必须在添加路由之前调用 SRouter::setConfig()

路由匹配

array public function match($path, $method)
  • $path string 请求的URI path
  • $method string 请求的request method
  • 返回 array 返回匹配结果信息

示例

根据请求的 URI path 和 请求 METHOD 查找匹配我们定义的路由信息。

$path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$method = $_SERVER['REQUEST_METHOD'];
$route = SRouter::match($path, $method);

将会返回如下格式的信息. 可以根据此信息进行 判断匹配是否成功 -> 路由调度

[
 // 路由匹配结果状态. 
 // 可能为: RouterInterface::FOUND, RouterInterface::NOT_FOUND, RouterInterface::METHOD_NOT_ALLOWED
 INT, 
 
 // 格式化后的 $path 的返回(会去除多余的空白,'/'等字符)
 'URI PATH', 
 
 // 路由信息。 匹配失败时(RouterInterface::NOT_FOUND)为 null 
 [
 // (可能存在)配置的请求 METHOD。 自动匹配时无此key
 'method' => 'GET', 
 
 // 此路由的 handler callback
 'handler' => 'handler', 
 
 // (可能存在)此路由的 原始path。 仅动态路由有
 'original' => '/hi/{name}', 
 
 // (可能存在) 有参数匹配的路由匹配成功后,会将参数值放入这里
 'matches' => ['name' => value ],
 
 // 此路由的自定义选项信息. 可能为空
 // - params - 来自添加路由时设置的参数匹配信息, 若有的话
 // - defaults - 有可选参数时,可以设置默认值
 // 还可以自定义追加此路由的选项:如下经供参考
 // - domains 允许访问路由的域名
 // - schemas 允许访问路由的schema
 // - enter 进入路由的事件回调
 // ... ...
 'option' => [
 'params' => [],
 'defaults' => [],
 // 'domains' => null,
 // 'schemas' => null, // ['http','https'],
 // route event. custom design ...
 // 'enter' => null,
 // 'leave' => null,
 ], 
 
 ],
]

路由调度

已内置了一个路由调度器 Inhere\Route\Dispatcher

use Inhere\Route\Dispatcher;
$dispatcher = new Dispatcher([
 'dynamicAction' => true,
]);

设置事件处理

// 成功匹配路由
$dispatcher->on(Dispatcher::ON_FOUND, function ($uri, $cb) use ($app) {
 $app->logger->debug("Matched uri path: $uri, setting callback is: " . is_string($cb) ? $cb : get_class($cb));
});
// 当匹配失败, 重定向到 '/404'
$dispatcher->on('notFound', '/404');
// 或者, 当匹配失败, 输出消息...
$dispatcher->on('notFound', function ($uri) {
 echo "the page $uri not found!";
});

使用控制器方法

通过@符号连接控制器类和方法名可以指定执行方法。

SRouter::get('/', app\controllers\HomeController::class);
SRouter::get('/index', 'app\controllers\HomeController@index');
SRouter::get('/about', 'app\controllers\HomeController@about');

NOTICE: 若第二个参数仅仅是个 类,将会尝试执行通过 defaultAction 配置的默认方法

动态匹配控制器方法

动态匹配控制器方法, 需配置 'dynamicAction' => true

NOTICE: 使用动态匹配控制器方法, 应当使用 any() 添加路由. 即此时无法限定请求方法 REQUEST_METHOD

// 访问 '/home/test' 将会执行 'app\controllers\HomeController::test()'
SRouter::any('/home/{any}', app\controllers\HomeController::class);
// 可匹配 '/home', '/home/test' 等
SRouter::any('/home[/{name}]', app\controllers\HomeController::class);

NOTICE: 上面两个的区别是 第一个无法匹配 /home

使用方法执行器

配置 actionExecutor 为你需要的方法名,例如配置为 'actionExecutor' => 'run',那所有的方法请求都会提交给此方法。 会将真实的 action 作为参数传入run($action), 需要你在此方法中调度来执行真正的请求方法。

NOTICE: 在你需要将路由器整合到自己的框架时很有用

示例:

// 访问 '/user', 将会调用 app\controllers\UserController::run('')
SRouter::get('/user', 'app\controllers\UserController');
// 访问 '/user/profile', 将会调用 app\controllers\UserController::run('profile')
SRouter::get('/user/profile', 'app\controllers\UserController');
// 同时配置 'actionExecutor' => 'run' 和 'dynamicAction' => true,
// 访问 '/user', 将会调用 app\controllers\UserController::run('')
// 访问 '/user/profile', 将会调用 app\controllers\UserController::run('profile')
SRouter::any('/user[/{name}]', 'app\controllers\UserController');

开始路由匹配和调度

SRouter::dispatch($dispatcher);
// $router->dispatch($dispatcher);

运行示例

示例代码在 examples 下。

  • 静态版本

你可以通过 php -S 127.0.0.1:5670 examples/static.php 来运行一个测试服务器, 现在你可以访问 http://127.0.0.1:5670

  • 对象版本

你可以通过 php -S 127.0.0.1:5670 examples/object.php 来运行一个测试服务器, 现在你可以访问 http://127.0.0.1:5671

测试

phpunit

License

MIT

我的其他项目

inhere/console github git@osc

功能丰富的命令行应用,命令行工具库

inhere/redis github git@osc

简单的redis操作客户端包装库

inhere/php-validate github git@osc

一个简洁小巧且功能完善的php验证库。仅有几个文件,无依赖。

inhere/http github git@osc

http 工具库(request 请求 response 响应 curl curl请求库,有简洁、完整和并发请求三个版本的类)

About

a very lightweight and fast speed php request router

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • PHP 100.0%

AltStyle によって変換されたページ (->オリジナル) /