adinan-cenci / router
一个小型的PHP路由库。
Requires
- php: >=7.4
- adinan-cenci/psr-17: ^1.0.0
- psr/http-server-handler: 1.0.x-dev
- psr/http-server-middleware: 1.0.x-dev
README
一个简单的PHP路由,用于处理HTTP请求。
此库符合PSR-15规范,因此可以与PSR-7消息一起使用,并利用PSR-17工厂。
实例化
use AdinanCenci\Router\Router; $r = new Router();
添加路由
您可以通过通知HTTP方法、要匹配的路径的正则表达式模式和关联的控制器来添加路由
$r->add('get', '#home$#', 'controller');
HTTP方法
// You may inform a single http method: $r->add('get', '#home#', 'controller') // Or several inside an array ... $r->add(['get', 'post'], '#home#', 'controller') // Or in a pipe separated string $r->add('get|post', '#home#', 'controller') // Or use an sterisk to match all methods. $r->add('*', '#home#', 'controller')
正则表达式模式
一个简单的正则表达式模式。捕获组将被作为属性传递给控制器。
注意:路由接受多个模式作为数组。
$r->add('*', '#products/(?<category>\d+)/(?<id>\d+)#', function($request, $handler) { $category = $request->getAttribute('category', null); $productId = $request->getAttribute('id', null); });
控制器
控制器将接收两个参数:分别是Psr\Http\Message\ServerRequestInterface
和Psr\Http\Server\RequestHandlerInterface
的实例。
路由接受各种参数作为控制器
$r->add('get', '#anonymous-function$#', function($request, $handler) { echo 'Anonymous function'; }) //------------- ->add('get', '#named-function$#', 'namedFunction') //------------- ->add('get', '#static-methods$#', ['MyClass', 'staticMethod']) // A single string also works: ->add('get', '#static-methods$#', 'MyClass::staticMethod') //------------- // Of course, it also accepts instances of Psr\Http\Server\MiddlewareInterfac // ( see the PSR-15 specification for more information ) ->add('get', '#psr-15$#', $middleware) //------------- ->add('get', '#object-and-method$#', [$object, 'methodName']) //------------- ->add('get', '#object$#', $object) // The ::__invoke() magic method will be called. //------------- ->add('get', '#class-and-method$#', ['MyClass', 'methodName']) // It will attempt to instantiate the class first. // A single string also works: ->add('get', '#class-and-method$#', 'MyClass::methodName') //------------- ->add('get', '#class$#', ['MyClass']) // It will attempt to instantiate the class and call the ::__invoke() magic method.
注意:如果控制器不存在或由于某些原因无法调用,将抛出异常。
请参阅"examples"目录中的内容以获取更多详细信息。
::add() 简写
// Examples $r->get('#home#', $call); /* is the same as */ $r->add('get', '#home#', $call); $r->post('#home#', $call); /* is the same as */ $r->add('post', '#home#', $call); $r->put('#home#', $call); /* is the same as */ $r->add('put', '#home#', $call); $r->delete('#home#', $call); /* is the same as */ $r->add('delete', '#home#', $call); $r->options('#home#', $call); /* is the same as */ $r->add('options', '#home#', $call); $r->patch('#home#', $call); /* is the same as */ $r->add('patch', '#home#', $call);
执行
调用::run()
将执行路由并返回响应。
$r->run();
PSR合规性
此库符合PSR-15规范,因此您的控制器可以根据PSR-7中指定的详细信息定制响应。处理器提供了PSR-17工厂以供使用。
use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\ResponseInterface; use Psr\Http\Server\RequestHandlerInterface; $r->add('get', '#home$#', function(ServerRequestInterface $request, RequestHandlerInterface $handler) { // Psr\Http\Message\ResponseFactoryInterface instance. $responseFactory = $handler->responseFactory; // Returns an instance of ResponseInterface with code 200. return $responseFactory->createResponse(200, 'OK'); });
重要
如果您的控制器没有返回ResponseInterface
的实例,路由器将基于通过echo
和print
输出的任何内容创建一个。
便利之处
除了符合PSR-17规范外,响应工厂还提供了一些方法以简化操作
$responseFactory = $handler->responseFactory; // Response with code 200 $responseFactory->ok('your html here'); // Response with code 201 $responseFactory->created('your html here'); // Response with code 301 $responseFactory->movedPermanently('https://redirect.here.com'); // Response with code 302 $responseFactory->movedTemporarily('https://redirect.here.com'); // Response with code 400 $responseFactory->badRequest('your html here'); // Response with code 401 $responseFactory->unauthorized('your html here'); // Response with code 403 $responseFactory->forbidden('your html here'); // Response with code 404 $responseFactory->notFound('your html here'); // Response with code 500 $responseFactory->internalServerError('your html here'); // Response with code 501 $responseFactory->notImplemented('your html here'); // Response with code 502 $responseFactory->badGateway('your html here'); // Response with code 503 $responseFactory->serviceUnavailable('your html here');
通过::withAddedCookie()
向响应对象添加cookie变得更容易
$response = $responseFactory->ok('your html here'); $expires = null; // optional $path = ''; // optional $domain = ''; // optional $secure = false; // optional $httpOnly = false; // optional $response = $response->withAddedCookie('cookieName', 'cookieValue', $expires, $path, $domain, $secure, $httpOnly);
中间件
中间件将在路由之前处理。中间件与路由非常相似,但与路由不同,可以执行多个中间件。
// Example $r->before('*', '#restricted-area#', function($request, $handler) { if (! userIsLogged()) { return $handler->responseFactory->movedTemporarily('/login-page'); } });
错误
异常
默认情况下,捕获的异常将渲染在500响应对象中,您可以通过设置自己的处理程序来自定义它。
$r->setExceptionHandler(function($request, $handler, $path, $exception) { return $handler->responseFactory ->internalServerError('<h1>Error 500 (' . $path . ')</h1><p>' . $exception->getMessage() . '</p>'); });
未找到
默认情况下,当找不到路由时,路由器将渲染一个404响应对象,您可以通过设置自己的处理程序来自定义它。
$r->setNotFoundHandler(function($request, $handler, $path) { return $handler->notFound ->internalServerError('<h1>Error 404</h1><p>Nothing found related to "' . $path . '"</p>'); });
::setDefaultNamespace($namespace)
设置默认命名空间,因此在定义路由时无需编写完整的控制器类名。
// Example $r->setDefaultNamespace('MyProject'); $r->add('get', '#home#', 'MyClass::method'); // If MyClass does not exist, the router will assume it refers to // MyProject\MyClass::method()
在子目录中工作
路由器将自动在子目录中工作。
考虑以下示例:您的URL:http://yourwebsite.com/foobar/about
您的文档根目录是
/var/www/html/
,并且您的路由器位于
/var/www/html/foobar/
.
路由器将匹配about
路由,而不是foobar/about
。
尽管如此,如果您确实需要与foobar/about
一起工作,则必须将/var/www/html/
作为基本目录传递给路由器类的构造函数。
// /var/www/html/foobar/index.php $r = new Router('/var/www/html/');
服务器配置
为了让它工作,我们需要重写对包含我们路由器的文件的请求。下面是一些示例:
Apache
以下是为Apache的 .htaccess 示例
RewriteEngine on
# Condition: Requested resource does not exist
RewriteCond %{SCRIPT_FILENAME} !-f
RewriteCond %{SCRIPT_FILENAME} !-d
# Rewrite to index.php
RewriteRule ^.{1,}$ index.php [QSA]
Nginx
以下是为nginx的示例
location / {
if ($script_filename !~ "-f") {
rewrite "^/.{1,}$" /index.php;
}
}
IIS
以下是为Microsoft IIS的web.config示例
<?xml version="1.0" encoding="UTF-8"?> <configuration> <system.webServer> <rewrite> <rules> <rule name="RewriteNonExistingFiles"> <match url="^.{1,}$" /> <conditions> <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" /> <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" /> </conditions> <action type="Rewrite" url="/index.php" appendQueryString="true" /> </rule> </rules> </rewrite> </system.webServer> </configuration>
安装
使用composer
composer require adinan-cenci/router
许可
MIT