kuathambu / php-router
PHP项目的轻量级路由器(FORKED)
Requires
- php: ^7.3
- sebastian/type: ^2.0@dev
Requires (Dev)
- phpunit/phpunit: ^9
README
PHP Router是一个轻量级库,帮助管理HTTP请求、响应和路由,以快速构建健壮的应用程序。这个库使用在Express和Hapi等流行的Node.js库中最常见的模式。
为了最大限度地发挥实现此路由模式的好处,您可能需要调整Apache或Nginx配置。
安装
使用Composer安装
$ composer require kudathambo/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;
}
}
结论
解决这个挑战有几种不同的方法,我只是研究了最适合我个人偏好的模型。如果您有改进这个项目的想法,或者只是想让我知道它帮到了您,请随时联系我。