guahanweb / php-router
PHP项目的轻量级路由器
Requires
- php: >=5.2.7
Requires (Dev)
- phpunit/phpunit: 5.5.x
This package is not auto-updated.
Last update: 2024-09-28 18:16:06 UTC
README
PHP Router是一个轻量级的库,用于管理HTTP请求、响应和路由,以快速构建健壮的应用程序。此库使用了在流行的Node.js库(如Express和Hapi)中最常见的模式。
为了从实现这种路由模式中获得最大收益,您可能需要对Apache或Nginx配置进行一些调整。
安装
使用 Composer 安装
$ composer require guahanweb/php-router
用法
安装并配置项目以使用Composer的自动加载后,您就可以开始路由了。
<?php // Include the namespace use GuahanWeb\Http; // Get a router instance $router = Http\Router::instance(); // Register a route $router->get('/', function ($req, $res) { $res->send('Hello, world!'); }); // Tell the router to start $router->process();
路由器
路由支持与Express.js相同的基本模式。提供HTTP动词、路由模式和处理器,当模式与动词组合匹配已注册的路由时,将执行处理器。
注册路由
路由可以通过两种方式之一进行注册。第一种是显式动词方法
$router->get($route, $handler); $router->post($route, $handler); $router->put($route, $handler); $router->delete($route, $handler);
第二种选项是在单个注册调用中注册一个或多个动词(或使用*
注册所有动词)
// multiple verbs $router->route(['POST', 'PUT'], $route, $handler); // wildcard for all supported verbs $router->route('*', $route, $handler);
路由模式和参数
如果您想从路由URI捕获参数,您可以使用括号来命名参数。这些参数将作为请求对象的属性应用
// capture a username $router->get('/profile/{username}', function ($req, $res) { var_dump($req->username); });
您还可以选择贪婪地匹配路径块进行手动解析。例如,如果我们想区分URI内部的匹配,但不区分json
扩展,我们可以这样做
// match a path with json extension // URI: /api/v1/my_method/json $router->get('/api/{path*}/json', function ($req, $res) { $res->send($req->path); // v1/my_method });
这种匹配类型对于将请求静态文件(不由Apache或Nginx配置管理)传递给请求非常有用
// route all image requests to a handler $router->get('/img/{image*}', function ($req, $res) { ImageManager::serve($req->image); });
请求和响应对象
每个路由处理器将传递两个参数:一个请求对象和一个响应对象。这些对象可用于获取详细信息并计算适当的响应。在大多数情况下,路由处理器应最终调用$res->send()
。
请求对象
当初始化路由器时,将创建一个新的Http\Request
对象并将其传递给处理器。此对象包含许多预解析的属性,有助于管理响应。
HTTP请求类型
HTTP方法(或动词)将分配给请求对象,并可以通过属性访问
echo $req->method;
支持的有效类型有GET
、POST
、PUT
和DELETE
。
HTTP请求头
所有请求头都可通过请求对象访问。而不是使用原生的PHP getallheaders()
方法,我们使用自定义polyfill以允许在Apache和Nginx环境中检索头信息。
一些请求头将由路由器用于在响应对象上设置默认值。要访问头信息,您可以引用请求对象的headers
属性
$router->post('/api/user', function ($req, $res) { if ($req->headers['Content-Type'] == 'application/json') { // request has JSON payload } });
查询字符串
请求对象将预解析查询字符串并将其分配给query
属性。如果您正在基于预期的查询参数进行逻辑操作,请使用此属性。
$router->post('/calendar', function ($req, $res) { if (!isset($req->query['month'])) { $res->send('Please select a month!'); } else { $res->send(Calendar::render($req->query['month'])); } });
请求URI
完全匹配的URI将分配给请求对象的uri
属性。
$router->get('/profile/{username}', function ($req, $res) { $res->send($req->uri); });
在这个例子中,如果您导航到http://yourdomain.com/profile/guahanweb,您将得到响应/profile/guahanweb
。
请求体(数据负载)
我们尝试对解析请求体进行了一些优化。我们不会总是解析负载,而是仅在第一次请求时静态解析它。换句话说,只有当第一次引用 body
属性时,才会解析正文。如果您再次引用 body
属性,则不会第二次解析负载。
此外,还基于 HTTP 动词和 Content-Type 头的组合对正文做了一些假设。
GET 和 DELETE
根据 HTTP/1.1 规范,如果存在,GET
和 DELETE
请求体不应有任何意义。因此,请求对象将对这些动词使用 null
值短路任何正文解析。
PUT
PUT
请求的正文将从 STDIN
读取。如果请求的 Content-Type 是 application/json
,正文也将解析为 JSON 内容,并将返回的结果对象。
POST
POST
动词的行为类似于 PUT
,但有一个额外的注意事项。除了 JSON 负载之外,POST
动词还可以接受以 mulipart/form-data
开头的 Content-Type。在这种情况下,由于 PHP 已经将负载处理为 $_POST
变量,正文将简单地返回该变量。
在 PUT
和 POST
两种情况下,如果未指定 JSON 或表单内容,则返回正文的原生字符串。
响应对象
响应对象允许对发送到浏览器的 HTTP 响应进行细粒度控制。
方法
setHeader($k, $v)
允许您在发送响应之前设置或覆盖默认头。默认内容类型取决于要发送的数据。如果传递了一个字符串,则内容类型默认为 text/html
,但如果传递了一个数组或对象,则内容将被 JSON 编码,并以 application/json
类型发送。
setHeaders($headers)
同时设置多个头的快捷方式。此方法接受一个头数组,其中键是头名称,值是头内容。
send($content, [$code])
此方法将发送最终构建的响应回用户。可选的 $code
参数允许您指定除默认的 200 Success
响应之外的状态码。
$router->route('*', '/admin', function ($req, $res) { $res->send('What do you think you are doing?', 500); });
以下是一个端点的示例,允许您检查所有动词的所有请求的属性。
// examine request from any verbs $router->route('*', '/info', function ($req, $res) { $res->send(array( 'method' => $req->method, 'uri' => $req->uri, 'query' => $req->query, 'headers' => $req->headers, 'body' => $req->body )); });
服务器配置
为了最大限度地利用手动路由,建议您将服务器配置为将所有希望处理的请求都导入一个单独的 PHP 文件。
Nginx 示例
如果您计划在路由中使用 PUT
和 DELETE
动词,请确保您的 Nginx 已经通过 --with-webdav
构建,并已安装。然后,在您的配置中,您需要启用额外的方法
dav_methods PUT DELETE;
我还建议提供一个基本位置,从中 Nginx 可以提供所有静态内容,这样您就只需要处理受保护的或需要额外逻辑的内容的 PHP。
location ~* ^/(css|js|img|font)/ {
root /var/www/<my-project>/assets;
}
另一件事是我喜欢做的,就是只将不明确匹配磁盘上另一个文件的流量导向。因此,完整的配置可能如下所示
server {
charset UTF-8;
listen 80;
server_name <project-domain>;
access_log /var/www/logs/<project-domain>/access.log;
location ~* ^/(css|js|img|font)/ {
root /var/www/<project-domain>/assets;
}
location / {
root /var/www/<project-domain>;
index index.php index.html index.htm;
include /usr/local/etc/nginx/conf.d/php-fpm;
dav_methods PUT DELETE;
if (!-e $request_filename) {
rewrite ^(.+)$ /index.php last;
}
error_page 404 /index.php;
}
}
结论
解决这个挑战有很多不同的方法,我只是研究了最适合我个人偏好的模型。如果您有可以改进此项目或只是想让我知道它帮了您的想法,请随时联系我。