divineniiquaye/flight-routing

Flight routing 是一个简单、快速且易于与其他路由器集成的 PHP 路由器。

v2.1.0 2022-12-07 21:55 UTC

README

PHP HTTP 路由

PHP Version Latest Version Workflow Status Code Maintainability Coverage Status Psalm Type Coverage Quality Score

Flight routing 是为 PHP 提供的高性能 HTTP 路由器。它简单易用、可扩展且快速。此库依赖于 PSR-7 进行路由匹配,并支持使用 PSR-15 在渲染之前拦截路由。

此库的早期版本受到了 Sunrise Http RouterSymfony RoutingFastRoute 的启发,现在已完全重写以提高性能。

🏆 特性

  • 支持所有 HTTP 请求方法(例如 GETPOSTDELETE 等)。
  • 参数的正则表达式约束。
  • 将命名路由路径反转到完整 URL,并带有严格的参数检查。
  • 路由分组和合并。
  • 支持路由缓存以提高性能。
  • PSR-15 中间件(拦截路由渲染之前的类)。
  • 域名和子域名路由。
  • RESTful 路由。
  • 支持 PHP 8 属性 #[Route] 和 doctrine 注解 @Route 路由。
  • 支持使用自定义路由匹配器类或编译器类使用自定义匹配策略。

📦 安装

此项目需要 PHP 8.0 或更高版本。推荐使用 Composer 进行安装。只需运行

$ composer require divineniiquaye/flight-routing

建议阅读我的博客文章设置 Apache、Nginx、IIS 服务器配置,以适应您的 PHP 项目。

📍 快速入门

默认编译器接受以下约束在路由模式中

  • {name} - 必需占位符。
  • {name=foo} - 带有默认值的占位符。
  • {name:regex} - 带有正则定义的占位符。
  • {name:regex=foo} - 带有正则定义和默认值的占位符。
  • [{name}] - 可选占位符。

占位符变量的名称仅是可接受的 PHP 函数/方法参数名称,预期是唯一的,而正则定义和默认值可以是任何字符串(即 [^/]+)。

  • /foo/ - 匹配 /foo//foo。匹配之前删除尾随斜杠。
  • /user/{id} - 匹配 /user/bob/user/1234/user/23/
  • /user/{id:[^/]+} - 与前面的示例相同。
  • /user[/{id}] - 与前面的示例相同,但也可以匹配 /user/user/
  • /user[/{id}]/ - 与前面的示例相同,匹配之前删除尾随斜杠。
  • /user/{id:[0-9a-fA-F]{1,8}} - 只有当 id 参数由 1 到 8 个十六进制数字组成时才匹配。
  • /files/{path:.*} - 匹配以 /files/ 开头的任何URL,并将路径的其余部分捕获到参数 path 中。
  • /[{lang:[a-z]{2}}[-{sublang}]/]{name}[/page-{page=0}] - 匹配 /cs/hello/en-us/hello/hello/hello/page-12/ru/hello/page-23

路由模式接受以 //domain.comhttps://domain.com 开头。路由路径还支持在路由路径的末尾直接添加控制器(即 *<controller@handler>)。

  • *<App\Controller\BlogController@indexAction> - 翻译为 BlogController 类的 indexAction 方法的可调用对象。
  • *<phpinfo> - 翻译为一个函数,如果路由中定义了处理器类,则它变为可调用对象。

以下是如何使用此库的示例。

use Flight\Routing\{Router, RouteCollection};

$router = new Router();
$router->setCollection(static function (RouteCollection $routes) {
    $routes->add('/blog/[{slug}]', handler: [BlogController::class, 'indexAction'])->bind('blog_show');
    //... You can add more routes here.
});

如果您更喜欢在闭包作用域外声明路由,请尝试以下示例。

use Flight\Routing\{Router, RouteCollection};

$routes = new RouteCollection();
$routes->get('/blog/{slug}*<indexAction>', handler: BlogController::class)->bind('blog_show');

$router = Router::withCollection($routes);

注意:如果启用了缓存,使用路由器的 setCollection() 方法比使用 withCollection() 方法具有更高的性能。

默认情况下,Flight 路由不提供 PSR-7 HTTP 库或发送响应头和体到浏览器的库。如果您想安装这些库,我建议安装 biurad/http-galaxynyholm/psr7laminas/laminas-httphandlerrunner

$request = ... // A PSR-7 server request initialized from global request

// Routing can match routes with incoming request
$route = $router->matchRequest($request);
// Should return an array, if request is made on a a configured route path (i.e /blog/lorem-ipsum)

// Routing can also generate URLs for a given route
$url = $router->generateUri('blog_show', ['slug' => 'my-blog-post']);
// $url = '/blog/my-blog-post' if stringified else return a GeneratedUri class object

在下面的示例中,我假设您已安装了 nyholm/psr7laminas/laminas-httphandlerrunner,因此我们可以使用 PSR-15 来拦截路由并在匹配之前,使用 PSR-17 将路由响应渲染到浏览器。

use Flight\Routing\Handlers\RouteHandler;
use Laminas\HttpHandlerRunner\Emitter\SapiStreamEmitter;

$router->pipe(...); # Add PSR-15 middlewares ...

$handlerResolver = ... // You can add your own route handler resolver else default is null
$responseFactory = ... // Add PSR-17 response factory
$request = ... // A PSR-7 server request initialized from global request

// Default route handler, a custom request route handler can be used also.
$handler = new RouteHandler($responseFactory, $handlerResolver);

// Match routes with incoming request and return a response
$response = $router->process($request, $handler);

// Send response to the browser ...
(new SapiStreamEmitter())->emit($response);

要使用 PHP 8 属性支持,我强烈建议安装 biurad/annotations,如果您出于某种原因决定使用 doctrine/annotations,我建议您安装 spiral/attributes 以使用其中一个或两个。

以下是一个使用注解/属性的示例。

use Biurad\Annotations\AnnotationLoader;
use Doctrine\Common\Annotations\AnnotationRegistry;
use Flight\Routing\Annotation\Listener;
use Spiral\Attributes\{AnnotationReader, AttributeReader};
use Spiral\Attributes\Composite\MergeReader;

$reader = new AttributeReader();

// If you only want to use PHP 8 attribute support, you can skip this step and set reader to null.
if (\class_exists(AnnotationRegistry::class)) $reader = new MergeReader([new AnnotationReader(), $reader]);

$loader = new AnnotationLoader($reader);
$loader->listener(new Listener(), 'my_routes');
$loader->resource('src/Controller', 'src/Bundle/BundleName/Controller');

$annotation = $loader->load('my_routes'); // Returns a Flight\Routing\RouteCollection class instance

您可以为注解加载器类添加更多监听器,以从同一位置加载所有注解/属性。还可以使用 populate() 路由收集方法或 group() 将注解的路由收集合并到默认路由收集中,或者简单地使用注解的路由收集作为默认路由器的路由收集。

最后,使用 RESTful 路由,如下例所示,使用 Flight\Routing\RouteCollection::resource 方法,这意味着路由对标准请求方法 Flight\Routing\Router::HTTP_METHODS_STANDARD 可用。

namespace Demo\Controller;

class UserController {
    public function getUser(int $id): string {
        return "get {$id}";
    }

    public function postUser(int $id): string {
        return "post {$id}";
    }

    public function deleteUser(int $id): string {
        return "delete {$id}";
    }
}

使用 Flight\Routing\Handlers\ResourceHandler 添加路由。

use Flight\Routing\Handlers\ResourceHandler;

$routes->add('/user/{id:\d+}', ['GET', 'POST'], new ResourceHandler(Demo\UserController::class, 'user'));

自版本 2.0 以来,Flight 路由非常稳定,可用于生产。请随意为项目做出贡献,报告错误,请求功能等。

在使用之前,请注意以下事项。

  • 避免多次声明相同的动态路由模式(例如,/hello/{name}),如果选择使用相同路由路径和多个配置,则使用静态路径。
  • 如果选择使用默认处理器类的 RouteInvoker 类以外的不同解析器,应避免使用以 \ 为前缀的路由处理器(例如,\HelloClass['\HelloClass', 'handle'])。
  • 如果您再次决定使用自定义路由的处理程序解析器,我建议您包含默认路由的RouteInvoker类的静态resolveRoute方法。

📓 文档

关于如何使用这个库的详细文档,请查阅这个库的文档。还建议浏览测试目录中的单元测试。

🙌 赞助商

如果这个库已经进入您的项目,或者您有兴趣支持我们,请考虑捐赠以支持未来的开发。

👥 致谢

📄 许可证

Flight Routing完全免费,并发布在BSD 3许可证下。