注解路由

注解路由利用 PHP 8 属性(Attribute)在控制器类与方法上直接声明路由规则,路由定义与业务代码同处一处。框架启动时自动扫描 app/Controller 目录(含子目录),将带有类级控制器注解的类解析为路由组。

三种注解

注解作用域说明
#[Controller]声明控制器路由组,仅注册带有 #[RouteMapping] 的方法
#[AutoController]继承 Controller,自动注册该类全部 public 方法为路由
#[RouteMapping]方法定义单条路由及其文档元数据

三个注解的命名空间均为 Viswoole\Router\Annotation

php
use Viswoole\Router\Annotation\{Controller, AutoController, RouteMapping};

WARNING

框架只扫描带有类级 #[Controller]#[AutoController] 注解的类;方法上的 #[RouteMapping] 必须与类级注解配合使用。

Controller 注解

#[Controller] 将控制器类注册为路由组(Group),类内方法的路由继承组的公共配置,只有标注了 #[RouteMapping] 的方法才会注册为路由

php
<?php
namespace App\Controller;

use App\Middleware\AuthMiddleware;
use Viswoole\Router\Annotation\{Controller, RouteMapping};

#[Controller(prefix: 'user', title: '用户管理', middlewares: [AuthMiddleware::class])]
class UserController
{
  // 路由: GET /user/info(未写 paths 时默认取方法名)
  #[RouteMapping(method: 'GET', title: '用户信息')]
  public function info(): array
  {
    return ['id' => 1, 'name' => 'Viswoole'];
  }

  // 普通方法:没有 RouteMapping,不会注册路由
  public function helper(): void {}
}

AutoController 注解

#[AutoController] 自动把类中全部 public 方法注册为路由(构造方法、抽象方法与析构方法除外),方法名即路径后段,无需逐个标注。未标注 #[RouteMapping] 的方法使用默认请求方法(继承全局 router.method,默认 '*' 不限制),需要收紧方法或自定义路径时,在该方法上追加 #[RouteMapping] 即可:

php
use Viswoole\Router\Annotation\AutoController;

#[AutoController(prefix: 'admin')]
class AdminController
{
  // 路由: /admin/dashboard(默认继承 router.method,不限请求方法)
  public function dashboard(): string
  {
    return 'Dashboard';
  }

  // 方法级注解覆盖默认配置:路由 GET /admin/settings
  #[RouteMapping(method: 'GET')]
  public function settings(): array
  {
    return [];
  }
}

需要更多自定义(文档信息、中间件等),可在其上标注 #[RouteMapping],方法级注解优先生效。

参数全表

#[Controller]#[AutoController] 共享下表中除 prefix 语义外的全部参数(AutoController 完整继承 Controller 的构造签名);#[RouteMapping]paths 承担同样的路径语义,并额外支持文档维护参数。

参数类型默认值说明
prefix / pathsstring|array|nullnull路径前缀(类级)/ 路径列表(方法级);null 时类级取类短名、方法级取方法名;支持多个路径映射同一方法
idstring|nullnull路由唯一标识;null 时自动生成(类级取类完全限定名哈希,方法级取 类::方法 哈希)
parentIdstring|nullnull父级分组 id,必须是已注册的分组路由 id,用于把路由挂到指定分组
methodstring|string[]|nullnull允许的 HTTP 方法;null 时继承全局 router.method 配置(默认 '*' 不限制)
middlewaresarray|nullnull中间件列表,支持类名、[类名, 构造参数数组];注解参数必须是常量表达式,不支持闭包
patternsarray<string,string>|nullnull动态路由变量正则约束,键为变量名
metaarray|nullnull自定义元数据(关联数组)
suffixstring|array|nullnull伪静态后缀,覆盖全局配置
domainstring|array|nullnull生效域名,覆盖全局配置
hiddenboolfalse是否在 API 文档中隐藏
titlestring|nullnull标题;未声明时从 PHPDoc 提取
descriptionstring|nullnull描述;未声明时从 PHPDoc 提取
sortint0排序,数值越大越靠前

#[RouteMapping] 额外支持的文档维护参数:

参数类型默认值说明
authorstring''接口作者
createdAtstring''创建时间
updatedAtstring''更新时间
tagsstring[][]接口标签列表
statusStatusStatus::DEVELOPMENT接口状态,见 Status 枚举

路径合并规则

方法路径与类前缀(或父分组前缀)的合并规则由路径是否以 / 开头决定:

方法路径写法语义合并结果(类前缀 user
info相对路径/user/info(拼接前缀)
/health绝对路径/health忽略类前缀
/类前缀本身/user(组默认入口)
php
#[Controller(prefix: 'api/v1')]
class HomeController
{
  // 路由: GET /api/v1/index —— 相对路径,拼接类前缀
  #[RouteMapping(method: 'GET')]
  public function index(): array { return []; }

  // 路由: GET /health —— 绝对路径,脱离类前缀
  #[RouteMapping(paths: '/health', method: 'GET')]
  public function health(): array { return ['ok' => true]; }

  // 路由: GET /api/v1 —— 单独的 / 表示控制器组入口
  #[RouteMapping(paths: '/', method: 'GET')]
  public function home(): array { return []; }
}

该规则与编程式路由分组完全一致;顶层注册的路径不以 / 开头时自动补全为 /

动态参数

方法路径中可使用 {参数名}(必选)与 {参数名?}(可选,缺失时由处理器参数默认值兜底),通过 patterns 约束格式:

php
#[RouteMapping(
  paths: ['article/{category}/{id}'],
  method: 'GET',
  patterns: ['category' => '[a-z]+', 'id' => '\d+'],
  title: '文章详情',
)]
public function detail(string $category, int $id): array
{
  // GET /article/php/42 → $category = 'php', $id = 42
  return compact('category', 'id');
}

变量名必须以字母或下划线开头、仅含字母数字下划线且不超过 32 字符,同一路径内不可重复;未约束的变量使用全局 router.default_pattern_regex 校验。动态参数会注入到同名的处理器方法参数中。

从 PHPDoc 提取标题与描述

注解未声明 title / description 时,框架自动从 PHPDoc 提取:首个空行之前的内容为标题,之后到首个 @ 标签之间的正文为描述

php
#[Controller(prefix: 'order')]
class OrderController
{
  /**
   * 订单列表。
   * 分页查询当前登录用户的订单记录,
   * 支持按状态筛选。
   */
  #[RouteMapping(method: 'GET')]
  public function list(): array
  {
    // title = "订单列表。"(首行/空行前)
    // description = "分页查询当前登录用户的订单记录,支持按状态筛选。"
    return [];
  }
}

类级 #[Controller] 未声明 title 时同样从类 PHPDoc 首段提取。推荐以注解参数显式声明,PHPDoc 提取作为兜底。

Status 枚举

status 参数使用 Viswoole\Router\ApiDoc\Status 枚举(Backed Enum),默认 Status::DEVELOPMENT。API 文档输出时附带中文标签与展示颜色:

枚举项value中文标签颜色
Status::PUBLISHEDpublished已发布#28a745 绿色
Status::DEVELOPMENTdevelopment开发中#17a2b8 青色
Status::DEPRECATEDdeprecated已废弃#6c757d 灰色
Status::ERRORerror有异常#dc3545 红色
Status::TO_BE_DEPRECATEDto_be_deprecated将废弃#e9ecef 浅灰色
Status::TESTINGtesting测试中#ffc107 黄色
php
use Viswoole\Router\ApiDoc\Status;

#[RouteMapping(method: 'POST', title: '创建用户', status: Status::PUBLISHED)]
public function create(): array { return []; }

配置合并优先级

方法级 #[RouteMapping] 与类级注解按以下规则合并:

  • paths:按上文路径合并规则与类级 prefix 拼接
  • middlewares:方法级追加到类级中间件之后
  • patterns:按变量名合并,方法级同名约束覆盖类级
  • method / domain / suffix / 其他:方法级显式声明时覆盖类级,否则继承

下一步