https://www.php.net/manual/zh/language.attributes.php
实现过程一、声明一个表明类
<?phpnamespace App\Route\Attributes;use Attribute;// 声明该Route表明可以重复利用,只能绑定在类方法上#[Attribute(Attribute::IS_REPEATABLE|Attribute::TARGET_METHOD)]class Route{ public static $all = []; public string $path = ""; // 路由地址 public string $method = "GET"; // 路由要求方法 public string $controller = ""; // 路由指向的掌握器 public string $function = ""; // 路由指向的掌握器的方法 public function __construct() { } / @description: 设置路由地址 @param string $path @return self / public function setPath (string $path) :self { $this->path = $path; return $this; } / @description: 设置掌握器 @param string $controller @return self / public function setController (string $controller) :self { $this->controller = $controller; return $this; } / @description: 设置要求方法 @param string $method @return self / public function setMethod (string $method) :self { $this->method = $method; return $this; } / @description: 设置绑定的function @param string $function @return self / public function setFunction (string $function) :self { $this->function = $function; return $this; } / @description: 添加路由 @return void / public function addRoute() :void { self::$all[] = $this; }}
二、利用表明类
<?phpnamespace App\Controller;use App\Route\Attributes\Route;class TestController{ #[Route('/test/route','GET')] #[Route('/route/test','POST')] public function testRoute() { // do something here }}
三、使表明类生效
利用反射读取表明的参数,并实行表明类里面的方法
<?phpfunction getAttributeData($reflection){ $controller = $reflection->getName(); // 如果表明是直接绑定在类上面,那么直接从类的反射获取表明 // $attributes = $reflection->getAttributes(App\Route\Attributes\Route::class); $methods = $reflection->getMethods(); // 由于表明是绑定在方法上的,因此循环方法,获取方法的表明 foreach($methods as $method){ // 方法名 $function = $method->getName(); // 指定获取某个表明的数据 $attributes = $method->getAttributes(App\Route\Attributes\Route::class); foreach ($attributes as $attribute) { // 拿到一个新的 Route 实例 $route = $attribute->newInstance(); // 拿到表明上的参数 $params = $attribute->getArguments(); / [ ['/test/route','GET'], ['/route/test','POST'] ] / // 实行路由添加 $route->setController($controller)->setFunction($function) ->setPath($params[0])->setMethod($params[1])->addRoute(); } } }// 对 TestController 利用反射,并实行表明生效的方法getAttributeData(new ReflectionClass(App\Controller\TestController::class));var_dump(App\Route\Attributes\Route::$all);// 运行结果 :// array(2) {// [0]=>// object(App\Route\Attributes\Route)#5 (4) {// ["path"]=>// string(11) "/test/route"// ["method"]=>// string(3) "GET"// ["controller"]=>// string(29) "App\Controller\TestController"// ["function"]=>// string(9) "testRoute"// }// [1]=>// object(App\Route\Attributes\Route)#6 (4) {// ["path"]=>// string(11) "/route/test"// ["method"]=>// string(4) "POST"// ["controller"]=>// string(29) "App\Controller\TestController"// ["function"]=>// string(9) "testRoute"// }// }
把稳事变1. 表明的参数是用反射读取的,可以直接反射类,而没必要利用实例
2. 表明可以在很多个位置,类、方法、属性等,在哪里利用表明就反射到哪里再利用 `getAttributes()` 方法
3. 表明利用 `newInstance()` 方法得到表明类的实例
4. 表明利用 `getArguments()` 方法得到表明上的参数
5. 表明语法参考官方文档,不外乎几种形式,用于天生表明类的参数
6. https://www.php.net/manual/zh/language.attributes.syntax.php
问题/总结在 使表明类生效 一节中,在另一个文件中写了一个方法,用于让表明生效,但是在实际利用过程中它该当放到什么位置呢?
可以看到,在该方法中,对目标类进行了反射,也便是说,要使表明生效,我们至少要提前知晓我们要反射的类(或方法)是哪一个。
以本文中的路由表明为例,一样平常的PHP框架中都会有显式路由的利用办法,例如:
<?php// laravelRoute::any('/test/route','TestController::testRoute');
也便是说,一样平常情形下,路由表是提前天生的,或者在实行之前就能拿到的。
那么至少可以利用两种办法使路由表明生效:
1. 直策应用command的遍历所有掌握器,天生这个路由文件,再在实行代码中引入,并缓存路由
2. 在路由生效之前,遍历所有的掌握器,将路由表搜集出来,动态地注册路由
如果有在路由之后生效的表明,可以在路由实行到方法之提高行反射,得到参数并实行
篇幅有限,未对各种实现办法的实行效率进行测试,想来php8中有JIT生效的时候实行效率不会低
以上仅为一些大略的实现和思考,如果有更好的实现办法欢迎互换