dzentota / router
快速灵活的安全路由器。
Requires
- ext-mbstring: *
- dzentota/typedvalue: dev-master
Requires (Dev)
- phpunit/phpunit: ^9.5
This package is auto-updated.
Last update: 2024-09-08 23:10:07 UTC
README
用法
Router的用法非常简单
// Fetch method and URI from somewhere $httpMethod = $_SERVER['REQUEST_METHOD']; $uri = $_SERVER['REQUEST_URI']; // Strip query string (?foo=bar) and decode URI if (false !== $pos = strpos($uri, '?')) { $uri = substr($uri, 0, $pos); } $uri = rawurldecode($uri); $route = (new Router()) ->get('/user/{id}', 'UserController@show', ['id' => Id::class]) ->findRoute($httpMethod, $uri);
解析后的$route
将具有以下结构(对于GET /user/42
)
array(4) {
["route"]=>
string(10) "/user/{id}"
["method"]=>
string(3) "get"
["action"]=>
string(19) "UserController@show"
["params"]=>
array(1) {
["id"]=>
object(Id)#4 (1) {
["value":protected]=>
string(2) "42"
}
}
}
定义路由
通过在Router实例上调用addRoute()
来添加路由
$r->addRoute($method, string $route, string $action, array $constraints = []);
$method是要匹配的特定路由的HTTP方法字符串。可以使用数组指定多个有效方法
// These two calls $r->addRoute('GET', '/test', 'handler'); $r->addRoute('POST', '/test', 'handler'); // Are equivalent to this one call $r->addRoute(['GET', 'POST'], '/test', 'handler');
Router使用语法,其中{foo}
指定名为foo
的占位符。路由中的每个占位符都必须是已定义的类型。从安全角度来看,在不正确验证您的领域数据的情况下接受用户数据(通过HTTP)是没有意义的。
假设我们有一个存储在数据库中的用户列表,并且可以通过自增正整数ID检索这些用户。在这种情况下,引入一个领域原语- ID
是个好主意
<?php use dzentota\TypedValue\Typed; use dzentota\TypedValue\TypedValue; use dzentota\TypedValue\ValidationResult; class Id implements Typed { use TypedValue; public static function validate($value): ValidationResult { $result = new ValidationResult(); if (!is_numeric($value) || $value <= 0) { $result->addError('Bad ID'); } return $result; } }
现在您可以在路由的占位符中使用ID作为自定义类型
$r->get('/user/{id}', 'UserController@show', ['id' => Id::class])
在{...?}
中括起来的路由参数被视为可选的,因此/foo/{bar?}
将匹配/foo
和/foo/bar
。
常见请求方法的快捷方式
对于GET
、POST
、PUT
、PATCH
、DELETE
、OPTIONS
和HEAD
请求方法,都有快捷方式。例如
$r->get('/get-route', 'get_handler'); $r->post('/post-route', 'post_handler');
等同于
$r->addRoute('GET', '/get-route', 'get_handler'); $r->addRoute('POST', '/post-route', 'post_handler');
此外,还有一个虚拟的ANY
方法,它匹配任何请求方法,因此
$r->addRoute('ANY', '/route', 'get_handler');
等同于
$r->addRoute(['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS', 'HEAD'], '/route', 'get_handler');
路由组
此外,您还可以在组内指定路由。组内定义的所有路由都将具有共同的前缀。
例如,将您的路由定义为
$r->addGroup('/admin', function (Router $r) { $r->addRoute('GET', '/do-something', 'handler'); $r->addRoute('GET', '/do-another-thing', 'handler'); $r->addRoute('GET', '/do-something-else', 'handler'); });
将产生与以下相同的结果
$r->addRoute('GET', '/admin/do-something', 'handler'); $r->addRoute('GET', '/admin/do-another-thing', 'handler'); $r->addRoute('GET', '/admin/do-something-else', 'handler');
缓存
您可以使用dump()
导出并保存路由,以便以后可以使用load()
加载它们 保存路由
file_put_contents('routes.php', sprintf('<?php return %s;', var_export($r->dump(), true)));
恢复路由
$routes = require 'routes.php'; $r->load($routes);
关于HEAD请求的注意事项
HTTP规范要求服务器支持GET和HEAD方法
GET和HEAD方法必须由所有通用服务器支持
为了避免强制用户为每个资源手动注册HEAD路由,我们回退到匹配给定资源的可用GET路由。PHP Web SAPI透明地从HEAD响应中删除实体体,因此此行为对大多数用户没有影响。当然,您始终可以指定自定义处理程序来处理HEAD方法