• 路由
    • 路由解析
      • Laravel 5
      • ThinkPHP 5
      • FastRoute
      • Swagger-php 注解路由
    • 路由匹配过程
    • 自动 MVC 路由
    • 自动 restful 路由
    • Swagger-php 注解路由
      • 基础
      • 单条路由

    路由

    对于一个框架来说路由是一件非常重要的事情,可以说是框架的核心之一吧。路由的使用便捷和理解复杂度以及性能对整个框架来说至关重要。

    路由解析

    QueryPHP 有一个非常独特的地方就是路由设计与其它框架有点出入,我们不需要像 Laravel 5、Thinkphp 5 等框架一样定义路由。

    Laravel 5

    1. Route::middleware(['first', 'second'])->group(function () {
    2. Route::get('/', function () {
    3. });
    4. Route::get('user/profile', function () {
    5. });
    6. });

    ThinkPHP 5

    1. Route::group('blog', function () {
    2. Route::rule(':id', 'blog/read');
    3. Route::rule(':name', 'blog/read');
    4. })->ext('html')->pattern(['id' => '\d+', 'name' => '\w+']);

    FastRoute

    1. $r->addRoute('GET', '/user/{id:\d+}', 'handler');
    2. $r->addRoute('GET', '/user/{name}', 'handler');
    3. $r->addRoute('GET', '/user/{name:.+}', 'handler');

    其中 FastRoute 中提供一个路由 10 条路由合并匹配算法非常地搞笑,QueryPHP 已经吸收。 合并路由匹配算法

    Swagger-php 注解路由

    在工作中我们大量使用 swagger-php 来生成 API 文档来定义后台的数据结构,这样子前端和后台也可以同时进行,最后 可以在一起进行联调。

    随着时间的推移,我发现 swagger-php 生成的 OpenApi 规范的数据结构就是一个标准的路由系统。我像可不可以直接 在这个基础上加入自定义的标签实现整个框架的路由呢,经过不断的完善终于搞定,后面会接着讲。

    QueryPHP 框架提供 MVC 自动路由 并能够智能解析 Restful 请求和基于 OpenApi 3.0 规范的 swagger-php 注解 路由,文档路由一步搞定。所以我们不需要定义一个 router.php 来注册我们的路由,写好文档路由就自动生成好了,并且 能够缓存起来加速解析。

    路由匹配过程

    QueryPHP 会优先进行MVC 自动路由匹配,也包含 Restful 路由匹配,如果匹配失败就会进行注解路由匹配阶段,如果还是 匹配失败则会抛出一个路由无法找到的异常。

    自动 MVC 路由

    很多时候我们不是特别关心它是 GET、POST 还是 Put,我们就想简单输入一个地址就可以访问到我们的控制器。

    路径 匹配控制器 备注
    / App\App\Controller\Home::index()
    /controller/action App\App\Controller\Controller::action()
    /:blog/controller/action Blog\App\Controller\Controller::action() : 表示应用
    /dir1/dir2/dir3/controller/action App\App\Controller\Dir1\Dir2\Dir3\Controller::action()
    /he_llo-wor/Bar/foo/xYY-ac/controller_xx-yy/action-xxx_Yzs App\App\Controller\HeLloWor\Bar\Foo\XYYAc\ControllerXxYy::actionXxxYzs()

    如果方法单独成为一个类,则起对应的请求入口为 handle,我们推荐为每一个方法定义一个类,避免与其它方法冲突,而且路由匹配性能最佳。 框架底层会优先匹配方法单独成类,匹配失败则匹配控制器类对应方法操作,如果还是匹配失败进入注解路由匹配阶段。

    例如访问地址 http://queryphp.cn/api/test:

    1. <?php
    2. declare(strict_types=1);
    3. /*
    4. * This file is part of the forcodepoem package.
    5. *
    6. * The PHP Application Created By Code Poem. <Query Yet Simple>
    7. * (c) 2018-2099 http://forcodepoem.com All rights reserved.
    8. *
    9. * For the full copyright and license information, please view the LICENSE
    10. * file that was distributed with this source code.
    11. */
    12. namespace App\App\Controller\Api;
    13. /**
    14. * api tests.
    15. *
    16. * @author Name Your <your@mail.com>
    17. *
    18. * @since 2018.08.31
    19. *
    20. * @version 1.0
    21. */
    22. class Test
    23. {
    24. /**
    25. * 默认方法.
    26. *
    27. * @return array
    28. */
    29. public function handle(): array
    30. {
    31. return ['hello' => 'world'];
    32. }
    33. }

    上面这种就是一个简单粗暴的路由,简单有效,大多数时候可以满足我们系统开发的需求。

    自动 restful 路由

    Restful 已经是一种开发主流,前后端分离的场景我们通常会定义 Restful 路由来向前端提供接口服务。

    我们访问同一个 URL 的时候,根据不同的请求类型访问不同的后台。

    路径 请求类型 匹配控制器 备注
    /car GET App\App\Controller\Car::index() 没有参数则请求列表
    /car/5 GET App\App\Controller\Car::show()
    /car/5 POST App\App\Controller\Car::store()
    /car/5 DELETE App\App\Controller\Car::destroy()
    /car/5 PUT App\App\Controller\Car::update()

    我们系统会分析 pathInfo,会将数字类数据扔进 params,其它字符将会合并进行上面的 自动 MVC 路由,这个时候没有方法,系统根据 请求类型自动补全方法完成 Restful 请求.

    我们可以通过 Request 中的 params 来访问参数。

    1. \Leevel::app('request')->params->get('_param0'); // 5

    数字类数据支持多个和跨目录

    • /car/5/10
    • /car/5/foo/bar/10/20

    Swagger-php 注解路由

    上面是一种预热,我们的框架路由设计是这样,优先进行 pathInfo 解析,如果解析失败将进入注解路由高级解析阶段。

    基础

    路径 匹配控制器 备注
    http://127.0.0.1:9527/api OpenApi 3 JSON JSON 结构
    http://127.0.0.1:9527/apis/ Swagger-ui Swagger-ui 入口
    http://127.0.0.1:9527/api/v1/petLeevelForApi/helloworld 路由 注解路由

    访问 http://127.0.0.1:9527/api/v1/petLeevelForApi/helloworld:

    1. Hi you,i am petLeevelForApi and it petId is helloworld

    在工作大量使用 Swagger-php 来生成注释文档,它其实是一个标准的路由。

    1. /**
    2. * @OA\Get(
    3. * path="/api/v1/petLeevelForApi/{petId:[A-Za-z]+}/",
    4. * tags={"pet"},
    5. * summary="Just test the router",
    6. * operationId="petLeevelForApi",
    7. * @OA\Parameter(
    8. * in="path",
    9. * description="ID of pet to return",
    10. * required=true,
    11. * @OA\Schema(
    12. * type="integer",
    13. * format="int64"
    14. * )
    15. * ),
    16. * @OA\Response(
    17. * response=405,
    18. * description="Invalid input"
    19. * ),
    20. * security={
    21. * {"petstore_auth": {"write:pets", "read:pets"}}
    22. * },
    23. * leevelParams={"args1": "hello", "args2": "world"}
    24. * )
    25. *
    26. * @param mixed $petId
    27. */
    28. public function petLeevelForApi($petId)
    29. {
    30. return sprintf('Hi you,i am petLeevelForApi and it petId is %s', $petId);
    31. }

    VS Laravel:

    1. Route::get('/', function () {
    2. });

    看起来我们的路由复杂很多,实际上我们只是定义 Leevel 开头的属性才是我们的扩展配置。 QueryPHP 的注解路由,在标准 Swagger-php 的基础上增加了自定义属性扩展功能。

    单条路由

    系统支持一些自定义属性,可以扩展看路由的功能。

    1. leevelScheme="https",
    2. leevelDomain="{subdomain:[A-Za-z]+}-vip.{domain}",
    3. leevelParams={"args1": "hello", "args2": "world"},
    4. leevelMiddlewares="api"
    5. leevelBind="\XXX\XXX\class@method"
    • leevelBind 未设置自动绑定当前注释的控制器和方法
    • 文档注释未写到控制器上,这个时候没有上下文控制器,需要使用 leevelBind 绑定
    • leevelBind 未设置 @ 则绑定到类的 handle 方法,@ 可以自定义绑定方法

    路由地址 path 支持正则参数

    1. /api/v1/petLeevelForApi/{petId:[A-Za-z]+}/