linfly / annotation
Webman plugin linfly/annotation
Requires
- php: ^8.0
- php-di/php-di: ^7.0
This package is auto-updated.
Last update: 2024-12-30 02:37:19 UTC
README
本插件使用 doctrine/annotation 对注释解析
PHP版本要求 >= 8.0
支持注释注解 和 PHP8注解方式
目前支持的功能
- 路由注解
- 中间件注解
- 验证器注解
- 自定义注解
- 注解继承
v1.0.5
版本开始支持 - 依赖注入
v1.0.7
版本开始支持
安装
composer require linfly/annotation:^1.*
配置
配置文件路径:/plugin/linfly/annotation/annotation.php
<?php return [ // 注解扫描路径 'include_paths' => [ // 应用目录 支持通配符: * , 例如: app/*, app/*.php 'app', ], // 扫描排除的路径 支持通配符: * 'exclude_paths' => [ 'app/model', ], // 路由设置 'route' => [ // 如果注解路由 @Route() 未传参则默认使用方法名作为path 'use_default_method' => true, ], // 验证器注解 'validate' => [ // 验证器验证处理类 (该功能需要自行安装对应的验证器扩展包),目前只支持 think-validate 'handle' => LinFly\Annotation\Validate\Handle\ThinkValidate::class, // 验证失败处理方法 'fail_handle' => function (Webman\Http\Request $request, string $message) { return json(['code' => 500, 'msg' => $message]); } ], ];
使用
注解路由
支持的注解方式:
@Route
@GetRoute
@PostRoute
@PutRoute
@DeleteRoute
@PatchRoute
@HeadRoute
@OptionsRoute
除
@Route
注解外,其他注解都是v1.0.5版本开始支持除
@Route
注解外,其他注解都是@Route
的别名,使用方式一致
use LinFly\Annotation\Route\Controller; use LinFly\Annotation\Route\Route; /** * 控制器注解 * @param string|array $prefix 路由分组路径 * * @Controller(prefix="/api") */ // PHP8注解方式 #[Controller(prefix: '/api')] class ApiController { /** * 注解路由 * @param string|array $path 路由路径 使用"/"开始则忽略控制器分组路径 * @param array $methods 请求方法 例:GET 或 ['GET', 'POST'],默认为所有方法 * @param string $name 路由名称 用于生成url的别名 * @param array $params 路由参数 * * @Route(path="get", methods="get") */ // PHP8注解方式 #[Route(path: 'get', methods: 'get')] public function get() { return 'get'; } }
注解中间件
use LinFly\Annotation\Route\Controller; use LinFly\Annotation\Route\Middleware; use LinFly\Annotation\Route\Route; /** * @Controller(prefix="/api") * * * 注解中间件 需要和注解路由一起使用 * @param string|array $middlewares 路由中间件 支持多个中间件 * @param array $only 指定需要走中间件的方法, 不指定则全部走中间件, 与except互斥 只支持在控制器上使用 * @param array $except 指定不需要走中间件的方法, 不指定则全部走中间件, 与only互斥 只支持在控制器上使用 * * @Middleware(middlewares=AuthMiddleware::class, only={"get"}) */ // PHP8注解方式 #[Middleware(middlewares=AuthMiddleware::class, only: ['get'])] class ApiController { /** * @Route(path="get", methods="get") * @Middleware(middlewares={TokenCheckMiddleware::class}) */ // PHP8注解方式 #[Middleware(middlewares=[TokenCheckMiddleware::class])] public function get() { return 'get'; } }
注解验证器
验证器注解需要配合验证器扩展包使用,目前只支持 think-validate 验证器验证成功会继续执行,验证失败则调用配置的 fail_handle 方法终止执行
use app\validate\UserValidate; use LinFly\Annotation\Validate\Validate; use support\Request; use support\Response; class IndexController { /** * @access public * * 注解验证器参数说明 * @param string|array $params 验证器参数 支持多个,例如: {"$get.id", "$post.name", "$post.title"} * 验证器参数 支持: * $post 获取所有 POST 参数 * $get 获取所有 GET 参数 * $all 获取所有 REQUEST 参数 * $post.xx 自定义 POST 参数名称 xx为实际的参数名称 * $get.xx 自定义 GET 参数名称 xx为实际的参数名称 * xx 自定义 REQUEST 参数名称 xx为实际的参数名称 * @param string $validate 验证器类名 * @param string $scene 验证场景 * * * @Validate(validate=UserValidate::class) * @param Request $request * @return Response */ // PHP8注解方式 //#[Validate(validate: UserValidate::class)] public function index(Request $request) { return response('hello webman'); } }
绑定原生路由
绑定Webman原生路由后,在不使用注解路由的情况下,也能使用注解中间件、验证器等功能
注解类
@BindRoute
绑定原生路由
v1.0.7
版本开始支持
use LinFly\Annotation\Route\BindRoute; use LinFly\Annotation\Route\Middleware; use LinFly\Annotation\Validate\Validate; use support\Request; use support\Response; use app\validate\UserValidate; class IndexController { /** * 绑定原生路由注解参数说明 * @param array $params 路由参数 * @param string $name 路由名称 用于生成url的别名 */ #[BindRoute(params: ['info' => 'test_bind_route'], name: 'test_bind_route')] #[Validate(validate: UserValidate::class)] #[Middleware(middlewares=[TokenCheckMiddleware::class])] public function index(Request $request) { $name = $request->route->getName(); $params = $request->route->param(); return response('hello webman'); } }
注解继承
注解继承可以让你的注解更加简洁,减少重复代码
参数说明
/** * 指定需要继承的方法, 不指定则全部继承, 与except互斥; 如果为false, 则不继承任何注解 * @param array|false $only * 指定不需要继承的方法, 不指定则全部继承, 与only互斥 * @param array $except * 参数为true时, 则合并父类的注解, 参数为false时, 则覆盖父类的注解 * @param bool $merge */ public function __construct(public array|false $only = [], public array $except = [], public bool $merge = true);
使用例子
父类:UserAuthController.php
<?php namespace app\controller; use app\middleware\UserAuthMiddleware; use app\validate\UserValidate; use LinFly\Annotation\Annotation\Inherit; use LinFly\Annotation\Route\Controller; use LinFly\Annotation\Route\Middleware; use LinFly\Annotation\Route\Route; use LinFly\Annotation\Validate\Validate; use support\Request; #[ // 用户授权中间件 Middleware(middlewares: UserAuthMiddleware::class), Validate(validate: UserValidate::class), // 让所有子类都继承父类的注解(父类使用了继承注解, 子类可选使用继承注解) Inherit, // 让所有子类只继承父类的中间件注解 // Inherit(only: [Middleware::class]), ] abstract class UserAuthController { }
子类:TestController.php
<?php namespace app\controller; use LinFly\Annotation\Route\Controller; use LinFly\Annotation\Route\GetRoute; #[ Controller(prefix: 'user'), // 不继承父类的注解 // Inherit(only: false), // 继承父类的所有注解 // Inherit(only: []), // 只继承父类的中间件注解 // Inherit(only: [Middleware::class]), // 不继承父类的验证器注解 // Inherit(except: [Validate::class]), ] class TestController extends UserAuthController { #[GetRoute] public function info() { return json([ 'user_id' => 1, 'username' => 'test', ]); } }
根据命名空间注解
根据命名空间的名称自动设置控制器路径,配合"注解继承"来使用更方便。
注解类
@NamespaceController
绑定原生路由
v1.0.7
版本开始支持
参数说明
/** * @param string|array $path * 自定义控制器路径 变量{$className}值为删除命名空间前缀后转小驼峰的名称 * * @param string $namespace * 需要删除的命名空间前缀 * * @param null|callable $filter * 自定义过滤器 */ public function __construct(string|array $path = '/{$className}', public string $namespace = '', ?callable $filter = null);
使用例子
use LinFly\Annotation\Route\NamespaceController; use LinFly\Annotation\Route\GetRoute; // 自动设置控制器路径,通过注解继承来使用则只需要在父类上使用命名空间注解即可 #[NamespaceController(namespace: 'app\controller')] class IndexController { #[GetRoute] public function index() { return 'hello webman'; } }
依赖注入
配置
修改config/container.php
文件,修改后内容如下:
$container = \LinFly\FacadeContainer::getInstance(); $container->definition(config('dependence', [])); return $container;
参数说明
/** * @param string|array $name 实例或者别名 * @param array $parameters 参数 */ public function __construct(protected string $name = '', protected array $parameters = []);
使用例子
需要注入的类:TestService.php
<?php namespace app\service; use LinFly\Annotation\Annotation\Inject; class TestService { public function test() { return true; } }
调用类:TestController.php
<?php namespace app\controller; use LinFly\Annotation\Annotation\Inject; use app\service\TestService; class TestController { /** * @Inject() * * 可以使用var注解指定注入的类, 或者声明属性类型来指定注入的类 * 更推荐使用声明属性类型的方式,优先级高于var注解 * @var TestService */ // PHP8注解方式 #[Inject] protected TestService $testService; public function test() { var_dump($this->testService->test()); // true } }
实验性功能
1. 循环依赖
依赖注入支持循环依赖,即A依赖B,B依赖A,但是需要注意的是,依赖注入的类必须是单例模式,否则会报错。
构造函数支持注入自身实例,注入自身的实例是单例实例,不会重复创建。
自定义注解类
第一步:创建注解类
注解类需要继承
LinFly\Annotation\Annotation\Annotation
类注解类需要声明
@Annotation
、@Target
、#[\Attribute()]
注解
测试代码:annotation-example
控制器注解类
use Doctrine\Common\Annotations\Annotation\Target; use LinFly\Annotation\AbstractAnnotation; /** * @Annotation * @Target({"CLASS"}) */ #[\Attribute(\Attribute::TARGET_CLASS)] class TestControllerParams extends AbstractAnnotation { /** * 第一个参数必须包含array类型,用来接收注解的参数 * @param string|array $controller */ public function __construct(public string|array $controller = '') { $this->paresArgs(func_get_args(), 'controller'); } }
方法注解类
use Doctrine\Common\Annotations\Annotation\Target; use LinFly\Annotation\AbstractAnnotation; /** * @Annotation * @Target({"METHOD"}) */ #[\Attribute(\Attribute::TARGET_METHOD)] class TestMethodParams extends AbstractAnnotation { /** * 第一个参数必须包含array类型,用来接收注解的参数 * @param string|array $method */ public function __construct(public string|array $method = '') { $this->paresArgs(func_get_args(), 'method'); } }
第二步:使用注解类
use app\annotation\params\TestControllerParams; use app\annotation\params\TestMethodParams; use support\Request; /** * 自定义类的注解类 * @TestControllerParams(controller=IndexController::class) */ // PHP8原生注解 // #[TestControllerParams(controller: IndexController::class)] class IndexController { /** * 自定义方法的注解类 * @TestMethodParams(method="index") */ // PHP8原生注解 // #[TestMethodParams(method: __METHOD__)] public function index(Request $request) { // 业务代码 ... } }
第三步:获取注解类的参数
方法一:通过 LinFly\Annotation\Annotation\Annotation::yieldParseClassAnnotations() 方法获取
use app\annotation\params\TestControllerParams; use app\annotation\params\TestMethodParams; use LinFly\Annotation\Annotation; // 获取指定类的注解列表 包括:类注解、属性注解、方法注解、方法参数注解 $generator = Annotation::yieldParseClassAnnotations(IndexController::class); /** * @var string $annotationName 注解类类名 * @var array $items 注解类参数列表 */ foreach ($generator as $annotationName => $items) { switch ($annotationName) { case TestControllerParams::class: case TestMethodParams::class: foreach ($items as $item) { var_dump($item['type'] . ' -> ' . $item['annotation']); } break; } }
方法二:通过 AnnotationHandle 获取
程序启动时会扫描所有的注解类,扫描完成后会调用已绑定注解类的回调事件
- 新建Handle类
注意:Handle类需要继承
LinFly\Annotation\Handle\Handle
类
namespace app\annotation\handle; use LinFly\Annotation\Interfaces\IAnnotationHandle; class TestHandle implements IAnnotationHandle { public static function handle(array $item): void { switch ($item['type']) { case 'class': case 'method': var_dump($item['type'] . ' -> ' . $item['annotation']); break; } } }
- 新建Bootstrap类 参考资料:webman业务初始化
namespace app\bootstrap; use app\annotation\handle\TestHandle; use app\annotation\params\TestControllerParams; use app\annotation\params\TestMethodParams; use LinFly\Annotation\Annotation; use Webman\Bootstrap; class CreateAnnotationHandle implements Bootstrap { /** * start * @access public * @param $worker * @return void */ public static function start($worker) { // monitor进程不执行 if ($worker?->name === 'monitor') { return; } // 添加测试控制器注解类处理器 Annotation::addHandle(TestControllerParams::class, TestHandle::class); // 添加测试控制器方法注解类处理器 Annotation::addHandle(TestMethodParams::class, TestHandle::class); } }
- 配置随进程启动
打开config/bootstrap.php
将CreateAnnotationHandle
类加入到启动项中。
return [ // ...这里省略了其它配置... app\bootstrap\CreateAnnotationHandle::class, ];
更新日志
v1.0.9
- 新增根据命名空间自动设置控制器路径的注解
v1.0.8
- 新增绑定原生路由注解支持绑定路由参数和路由命名
v1.0.7
- 新增依赖注入功能
- 新增绑定原生路由注解
v1.0.6
- 修复部署模式调用STDOUT报错。
v1.0.5
- 修正注解路由请求方法大小写问题。
- 新增注解继承功能。
- 新增
@GetRoute
@PostRoute
等注解路由类。 - 新增
include_paths
注解扫描路径参数支持通配符。 - 新增支持扫描单个文件中写入多个类的文件。
v1.0.4
- 修复注解路由可选参数解析问题。
v1.0.3
- 修复中间件注解和验证器注解执行顺序问题。
- 新增中间件only和except参数, 用于指定中间件只在指定的方法执行。
- 新增在控制器上使用验证器注解。