标题:【PHP小课堂】一起学习PHP中的反射(二)

文章目录
    分类:PHP 标签:PHP

    一起学习PHP中的反射(二)

    接下来我们继续 PHP 中的反射相关的功能操作。对于反射操作来说,我们主要是要获取类或者对象中的那些已定义的数据信息,这些信息如果不通过反射的话,正常情况下我们是很难获取到的,通过反射功能,就可以方便地对一个类或对象进行剖析,从而帮助我们实现各种功能。

    获取反射类的构造函数

    首先,我们来获取一下类中的构造函数。在这之前,我们要对上篇文章中的自定义类进行一些扩展,增加一下构造函数以及我们今天需要使用到的内容。

    interface iA{
    
    }
    
    class A{
    
        /**
         * This is ONE DOC.
         */
        const ONE = 'number 1';
        
        // This is TWO DOC.
        private const TWO = 'number 2';
    
        /**
         * This is a DOC.
         */
        private $a = '1';
        
        // This is b DOC.
        protected $b = '2';
    
        /* This is c DOC. */
        public $c = '3';
    
        public $d;
    
        static $e = '5';
        static $f;
    
        public function __construct(){
            echo 'This is Class A, function Constructor.', PHP_EOL;
        }
    
    
        private function testA(){
            echo 'This is class A, function testA', PHP_EOL;
        }
    
        protected function testB(){
            echo 'This is class A, function testB', PHP_EOL;
        }
    
        public function testC(){
            echo 'This is class A, function testC', PHP_EOL;
        }
    }
    
    /**
     * This is Class B ,extends from A.
     * 
     * @author zyblog
     */
    class B extends A {
        
    }
    
    
    class C implements iA{
    
    }

    然后,我们查看一下这几个类的构造函数信息。

    $objA = new ReflectionClass('A');
    $objB = new ReflectionClass('B');
    $objC = new ReflectionClass('C');
    
    // 类的构造函数
    var_dump($objA->getConstructor());
    // object(ReflectionMethod)#2 (2) {
    //     ["name"]=>
    //     string(11) "__construct"
    //     ["class"]=>
    //     string(1) "A"
    //   }
    
    var_dump($objB->getConstructor());
    // object(ReflectionMethod)#3 (2) {
    //     ["name"]=>
    //     string(11) "__construct"
    //     ["class"]=>
    //     string(1) "A"
    //   }
    
    var_dump($objC->getConstructor()); // NULL

    在这段测试代码中,B 类是继承自 A 类的,它没有构造函数,因此,使用 getConstructor() 方法获取到的构造函数信息是 A 类的构造函数。而 C 类则是完全没有构造函数的,所以返回的是 NULL 。

    获取反射类的注释

    var_dump($objB->getDocComment());
    // string(67) "/**
    //  * This is Class B ,extends from A.
    //  * 
    //  * @author zyblog
    //  */"

    getDocComment() 可以获得类的注释信息,和上篇文章中学习到的属性的注释以及后面我们学习的方法中的注释一样,都是可以获取对应的信息的注释内容。

    获取反射类的文件名及类名称

    var_dump($objA->getFileName());
    // string(105) "/Users/zhangyue/MyDoc/博客文章/dev-blog/php/2021/05/source/2.一起学习PHP中的反射(二).php"
    
    var_dump($objA->getName()); // string(1) "A"

    getFileName() 用于获取我们反射的这个类所属的文件,可以看出,这个方法返回的是完整的绝对路径。而 getName() 方法返回的则是这个类的名字。

    获取反射类的接口及父类

    var_dump($objC->getInterfaceNames());
    // array(1) {
    //     [0]=>
    //     string(2) "iA"
    //   }
    
    var_dump($objC->getInterfaces());
    // array(1) {
    //     ["iA"]=>
    //     object(ReflectionClass)#4 (1) {
    //       ["name"]=>
    //       string(2) "iA"
    //     }
    //   }
    
    var_dump($objB->getParentClass());
    // object(ReflectionClass)#4 (1) {
    //     ["name"]=>
    //     string(1) "A"
    //   }

    getInterfaceNames() 方法返回的是字符串数组形式的类实现的接口,getInterfaces() 返回的是 ReflectionClass 对象形式的接口列表。PHP 是单继承多接口的编程语言,所以接口相关信息的内容返回的都是数组格式的数据。而对于继承来说,getParentClass() 直接就是返回的单个 ReflectionClass 对象。


    ReflectionClass 对象不用多说了吧,我们一直都在学习的就是这个对象的反射功能。

    反射类的开始和结束行信息

    // 反射类开始结束行数
    var_dump($objB->getEndLine()); // int(54)
    var_dump($objC->getStartLine()); // int(56)

    这两个方法就更简单了,它们返回的所定义的类在所在文件中的起始和结束行数。在我们的测试代码中,B 类的定义后面空了一行紧接着就是 C 类的定义,所以这个测试结果就非常明显了。而对象大多数默认类和框架来说,一个文件就一个类,对比就不是那么明显。

    获取反射类所属扩展

    在 PHP 中,很多功能我们都是通过扩展安装来使用的。之前的文章中我们也学习过很多的扩展了,这里要展示的就是对于扩展的反射相关的内容。在这里,我们就不使用上面自定义的 A 、B、C 类了,需要使用 PHP 当前运行环境下已经安装的那些扩展中相关的内容了。

    var_dump((new ReflectionClass('ReflectionClass'))->getExtension());
    // object(ReflectionExtension)#5 (1) {
    //     ["name"]=>
    //     string(10) "Reflection"
    //   }
    var_dump((new ReflectionClass('PDO'))->getExtensionName()); // string(3) "PDO"

    getExtension() 方法会返回一个 ReflectionExtension 对象,它代表的就是我们指定的类所对应的扩展信息。如果是我们自定义的类,当然就是返回空的内容了,毕竟我们自己定义的类是不属于任何扩展的。


    getExtensionName() 用于返回字符串格式的扩展名称。

    ReflectionExtension

    接下来,我们就重点看一下 ReflectionExtension 这个对象中的一些方法功能。

    $objExt = (new ReflectionClass('ReflectionClass'))->getExtension();
    
    var_dump($objExt->getClassNames());
    // array(16) {
    //     [0]=>
    //     string(19) "ReflectionException"
    //     [1]=>
    //     string(10) "Reflection"
    //     [2]=>
    //     string(9) "Reflector"
    //     [3]=>
    //     string(26) "ReflectionFunctionAbstract"
    //     [4]=>
    //     string(18) "ReflectionFunction"
    //     [5]=>
    //     string(19) "ReflectionGenerator"
    //     [6]=>
    //     string(19) "ReflectionParameter"
    //     [7]=>
    //     string(14) "ReflectionType"
    //     [8]=>
    //     string(19) "ReflectionNamedType"
    //     [9]=>
    //     string(16) "ReflectionMethod"
    //     [10]=>
    //     string(15) "ReflectionClass"
    //     [11]=>
    //     string(16) "ReflectionObject"
    //     [12]=>
    //     string(18) "ReflectionProperty"
    //     [13]=>
    //     string(23) "ReflectionClassConstant"
    //     [14]=>
    //     string(19) "ReflectionExtension"
    //     [15]=>
    //     string(23) "ReflectionZendExtension"
    //   }
    
    var_dump($objExt->getClasses());
    // array(16) {
    //     ["ReflectionException"]=>
    //     object(ReflectionClass)#5 (1) {
    //       ["name"]=>
    //       string(19) "ReflectionException"
    //     }
    //     ["Reflection"]=>
    //     object(ReflectionClass)#6 (1) {
    //       ["name"]=>
    //       string(10) "Reflection"
    //     }
    //     ["Reflector"]=>
    //     object(ReflectionClass)#7 (1) {
    //       ["name"]=>
    //       string(9) "Reflector"
    //     }
    //     ["ReflectionFunctionAbstract"]=>
    //     object(ReflectionClass)#8 (1) {
    //       ["name"]=>
    //       string(26) "ReflectionFunctionAbstract"
    //     }
    //     ["ReflectionFunction"]=>
    //     object(ReflectionClass)#9 (1) {
    //       ["name"]=>
    //       string(18) "ReflectionFunction"
    //     }
    //     ["ReflectionGenerator"]=>
    //     object(ReflectionClass)#10 (1) {
    //       ["name"]=>
    //       string(19) "ReflectionGenerator"
    //     }
    //     ["ReflectionParameter"]=>
    //     object(ReflectionClass)#11 (1) {
    //       ["name"]=>
    //       string(19) "ReflectionParameter"
    //     }
    //     ["ReflectionType"]=>
    //     object(ReflectionClass)#12 (1) {
    //       ["name"]=>
    //       string(14) "ReflectionType"
    //     }
    //     ["ReflectionNamedType"]=>
    //     object(ReflectionClass)#13 (1) {
    //       ["name"]=>
    //       string(19) "ReflectionNamedType"
    //     }
    //     ["ReflectionMethod"]=>
    //     object(ReflectionClass)#14 (1) {
    //       ["name"]=>
    //       string(16) "ReflectionMethod"
    //     }
    //     ["ReflectionClass"]=>
    //     object(ReflectionClass)#15 (1) {
    //       ["name"]=>
    //       string(15) "ReflectionClass"
    //     }
    //     ["ReflectionObject"]=>
    //     object(ReflectionClass)#16 (1) {
    //       ["name"]=>
    //       string(16) "ReflectionObject"
    //     }
    //     ["ReflectionProperty"]=>
    //     object(ReflectionClass)#17 (1) {
    //       ["name"]=>
    //       string(18) "ReflectionProperty"
    //     }
    //     ["ReflectionClassConstant"]=>
    //     object(ReflectionClass)#18 (1) {
    //       ["name"]=>
    //       string(23) "ReflectionClassConstant"
    //     }
    //     ["ReflectionExtension"]=>
    //     object(ReflectionClass)#19 (1) {
    //       ["name"]=>
    //       string(19) "ReflectionExtension"
    //     }
    //     ["ReflectionZendExtension"]=>
    //     object(ReflectionClass)#20 (1) {
    //       ["name"]=>
    //       string(23) "ReflectionZendExtension"
    //     }
    //   }

    首先我们需要获得一个 ReflectionExtension 对象,并将它保存在了 objExt 变量中。然后使用 getClassNames() 方法可以获得这个扩展中所有的方法名称,这里是字符串格式的,当然,我们也可以使用 getClasses() 方法获得 ReflectionClass 对象格式的全部类。通过这种形式,我们就可以方便地再对这个扩展下面所有的类进行反射操作了。

    var_dump($objExt->getConstants());
    // array(0) {
    // }
    
    var_dump((new ReflectionExtension('DOM'))->getConstants());
    // array(45) {
    //   ["XML_ELEMENT_NODE"]=>
    //   int(1)
    //   ["XML_ATTRIBUTE_NODE"]=>
    //   int(2)
    //   ……………………
    //   ……………………
    //   ……………………
    //   ["DOM_NAMESPACE_ERR"]=>
    //   int(14)
    //   ["DOM_INVALID_ACCESS_ERR"]=>
    //   int(15)
    //   ["DOM_VALIDATION_ERR"]=>
    //   int(16)
    // }

    getConstants() 方法用于获得扩展反射对象中所能反射出来的常量信息。可以看出,直接使用 objExt 反射的 Reflection 扩展中并没有什么常量信息返回(其实手册中是有的)。这里我们再反射另外一个 DOM 扩展中的常量信息。注意,在这里,我们直接使用 ReflectionExtension 来实例化一个扩展反射对象。也就是说,我们可以通过 ReflectionClass 对象的 getExtension() 方法获得一个 ReflectionExtension 对象,也可以直接实例化这个对象。直接实例化的时候,需要的构造参数是扩展的名称。

    var_dump($objExt->getDependencies());
    // array(0) {
    // }
    
    var_dump((new ReflectionExtension('PDO'))->getDependencies());
    // array(1) {
    //     ["spl"]=>
    //     string(8) "Required"
    //   }

    getDependencies() 返回的指定扩展所需要依赖的其它扩展组件信息。比如我们的 PDO 扩展需要依赖一个 SPL 扩展中的 Required 这个扩展才能正常运行。而反射扩展并不需要其它任何的依赖。

    var_dump($objExt->getFunctions());
    // array(0) {
    // }
    
    var_dump((new ReflectionExtension('fileinfo'))->getFunctions());
    // array(6) {
    //     ["finfo_open"]=>
    //     object(ReflectionFunction)#19 (1) {
    //       ["name"]=>
    //       string(10) "finfo_open"
    //     }
    //     ["finfo_close"]=>
    //     object(ReflectionFunction)#18 (1) {
    //       ["name"]=>
    //       string(11) "finfo_close"
    //     }
    //     ["finfo_set_flags"]=>
    //     object(ReflectionFunction)#17 (1) {
    //       ["name"]=>
    //       string(15) "finfo_set_flags"
    //     }
    //     ["finfo_file"]=>
    //     object(ReflectionFunction)#16 (1) {
    //       ["name"]=>
    //       string(10) "finfo_file"
    //     }
    //     ["finfo_buffer"]=>
    //     object(ReflectionFunction)#15 (1) {
    //       ["name"]=>
    //       string(12) "finfo_buffer"
    //     }
    //     ["mime_content_type"]=>
    //     object(ReflectionFunction)#14 (1) {
    //       ["name"]=>
    //       string(17) "mime_content_type"
    //     }
    //   }

    getFunctions() 可以获取到的是指定扩展中的所有函数。同样地,由于 ReflectionClass 对象所属的这个 Reflection 扩展中全是类,并没有直接的外部函数可以调用,所以它返回的也是空的。而我们查看 fileinfo 这样的传统扩展,就可以看到里面有很多可以直接使用的扩展函数。

    var_dump($objExt->getINIEntries());
    // array(0) {
    // }
    
    var_dump((new ReflectionExtension('mysqli'))->getINIEntries());
    // array(11) {
    //     ["mysqli.max_links"]=>
    //     string(2) "-1"
    //     ["mysqli.max_persistent"]=>
    //     string(2) "-1"
    //     ["mysqli.allow_persistent"]=>
    //     string(1) "1"
    //     ["mysqli.rollback_on_cached_plink"]=>
    //     string(1) "0"
    //     ["mysqli.default_host"]=>
    //     NULL
    //     ["mysqli.default_user"]=>
    //     NULL
    //     ["mysqli.default_pw"]=>
    //     NULL
    //     ["mysqli.default_port"]=>
    //     string(4) "3306"
    //     ["mysqli.default_socket"]=>
    //     string(21) "/var/mysql/mysql.sock"
    //     ["mysqli.reconnect"]=>
    //     string(1) "0"
    //     ["mysqli.allow_local_infile"]=>
    //     string(1) "0"
    //   }

    getINIEntries() 返回的是我们在 php.ini 文件中设置的扩展相关的属性配置信息。一般 Reflection 也不会在 php.ini 中配置什么内容,所以我们查看的是当前环境下 mysqli 扩展中我们在 php.ini 里面所定义的那些属性配置信息。

    var_dump($objExt->getName()); // string(10) "Reflection"
    var_dump($objExt->getVersion()); // string(38) "7.3.24-(to be removed in future macOS)"
    
    $objExt->info();
    // Reflection
    
    // Reflection => enabled

    getName() 返回的是扩展的名称,getVersion() 返回的是扩展的版本号信息,这两个不用过多地解释了。


    info() 返回的是扩展的详细信息,就像我们使用 php --ri Reflection 这个命令一样,当然,这也是 phpinfo() 中返回的关于扩展的相关信息的内容。

    var_dump($objExt->isPersistent()); // bool(true)
    var_dump($objExt->isTemporary()); // bool(false)

    最后,就是 isPersistent() 和 isTemporary() 这两个方法,它们是用于判断扩展是否是默认加载的或者是否是临时加载的,还记得我们在之前的文章 动态查看及加载PHP扩展https://mp.weixin.qq.com/s/LopY0_uuxhJWwggVkdMITA 中讲过的内容吗?这两个方法就是用于判断这个扩展是不是动态临时加载进来的。

    总结

    今天的内容又介绍了一些 ReflectionCalss 中相关的方法函数,主要是接口以及继承相关方面在反射中的表现。另外主要介绍的就是 ReflectionExtension 反射出来的扩展相关对象的一些方法及使用。当然,扩展这块的内容并不是反射功能的重点内容,平时使用的也比较少,大家以了解为主。


    测试代码:


    https://github.com/zhangyue0503/dev-blog/blob/master/php/2021/05/source/2.%E4%B8%80%E8%B5%B7%E5%AD%A6%E4%B9%A0PHP%E4%B8%AD%E7%9A%84%E5%8F%8D%E5%B0%84%EF%BC%88%E4%BA%8C%EF%BC%89.php


    参考文档:


    https://www.php.net/manual/zh/book.reflection.php

    搜索
    关注