美文网首页
PHP8 - 注解的使用

PHP8 - 注解的使用

作者: 思跃喵 | 来源:发表于2022-10-16 11:52 被阅读0次

    概述

    php8 中新增了注解特性,官方文档地址如下:

    https://www.php.net/manual/zh/language.attributes.php

    本文将使用这个新特性实现一个路由注解

    实现过程

    声明一个注解类

    <?php
    namespace 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;
        }
    }
    

    使用注解类

    <?php
    namespace App\Controller;
    
    use App\Route\Attributes\Route;
    
    class TestController{
        #[Route('/test/route','GET')]
        #[Route('/route/test','POST')]
        public function testRoute() {
            // do something here
        }
    }
    

    使注解类生效

    利用反射读取注解的参数,并执行注解类里面的方法

    <?php
    function 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
    // laravel
    Route::any('/test/route','TestController::testRoute');
    

    也就是说,一般情况下,路由表是提前生成的,或者在执行之前就能拿到的。

    那么至少可以使用两种方式使路由注解生效:

    1. 直接使用command的遍历所有控制器,生成这个路由文件,再在执行代码中引入,并缓存路由
    2. 在路由生效之前,遍历所有的控制器,将路由表搜集出来,动态地注册路由

    ** 如果有在路由之后生效的注解,可以在路由执行到方法之前进行反射,得到参数并执行 **

    篇幅有限,未对各种实现方式的执行效率进行测试,想来php8中有JIT生效的时候执行效率不会低

    以上仅为一些简单的实现和思考,如果有更好的实现方式欢迎交流👏🏻

    相关文章

      网友评论

          本文标题:PHP8 - 注解的使用

          本文链接:https://www.haomeiwen.com/subject/wttdzrtx.html