• PHP 反射


    参考文档

    官网:
    https://www.php.net/manual/zh/book.reflection.php
    https://www.php.net/manual/zh/class.reflection.php
    https://www.qycn.com/xzx/article/405.html

    Laravel 通过类的反射, 对类的私有属性赋值

    TestController 类

    
    
    namespace App\Http\Controllers\Services;
    
    use App\CommentModel;
    use App\Http\Controllers\Controller;
    
    class TestController extends Controller
    {
        public function getList()
        {
            $data = ["good_id"=>1,"name"=>"商品名"];
            $good = $this->ins(TwoTestController::class ,$data);
            dd($good);
        }
        /**
         * @param $class
         * @param Array $data
         * @return object
         * @throws \ReflectionException
         */
        public function ins($class , $data)
        {
            $classObj = new \ReflectionClass($class);// 反射 TwoTestController 类
            $classMethods = $classObj->getMethods(\ReflectionMethod::IS_PUBLIC); // 获取公共方法
            $class_instantce = $classObj->newInstance(); // 通过反射对象创建类的实例
    
            foreach ($classMethods as $method){
    //            echo $method->getName().PHP_EOL; // 获取公共方法的方法名
                # 过滤出set开头的公共方法(我这里只有一个setGoodId方法)
                if (preg_match("/^set(\w+)/" ,$method->getName() ,$matches)) {
    //                echo $matches[1].PHP_EOL; // 输出的是GoodId
    //                echo $this->filterMethod($matches[1]).PHP_EOL; // 输出的是good_id
                    $this->filterMethod($matches[1] ,$classObj ,$data, $class_instantce);
                }
            }
            return $class_instantce;
        }
    
        function filterMethod($name ,\ReflectionClass $classObj ,$data , &$class_instance){
            // GoodId 转换成 Good_Id , 左边是小写才添加_ , aGoodId 转换成a_Good_Id
            $filter_name =  strtolower(preg_replace("/(?<=[a-z])([A-Z])/","_$1" ,$name));
            $props = $classObj->getProperties(\ReflectionProperty::IS_PRIVATE); //获取私有属性
            foreach ($props as $prop){
                if ($prop->getName() == $filter_name){
                    $method = $classObj->getMethod("set".$name);
                    $args = $method->getParameters(); // 获取方法的参数
                    // 参数数量为1 , 并且数据中存在对应的值
                    if (count($args)==1 && isset($data[$filter_name])){
                        // 反射执行方法
                        $method->invoke($class_instance, $data[$filter_name]);
                    }
                }
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56

    TwoTestController 类

    
    
    namespace App\Http\Controllers\Services;
    
    use Illuminate\Http\Request;
    use App\Http\Controllers\Controller;
    
    class TwoTestController extends Controller
    {
        private $good_id;
        private $name;
    
        public function setGoodId($good_id)
        {
            $this->good_id = $good_id;
        }
    
        public function getGoodId()
        {
            return $this->good_id;
        }
    
        public function setName($name)
        {
            $this->name = $name;
        }
    
        public function getName()
        {
            return $this->name;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    预览

    在这里插入图片描述

    PHP中的反射理解

    从一个简单的例子理解反射:
    人有五官四肢,但鲜有人清楚人体内部的经脉走向、骨骼构造。
    如果你修仙顺利,在丹田深处练出元婴,那么就通过元婴透析身体内部的构造。
    理解内部构造后,还可以让元婴指引体内真气在经脉的流向,早日修成正果。

    如其名,反射是(从镜子里)照出自身。
    我们写代码,告诉代码怎么运行,事件发生在编译期。
    代码运行期间,代码如何知道自己的结构以及能力呢?
    反射机制相当于代码的元婴,使代码能够感知自身结构,并可(部分)改变运行行为。

    与运行时类型信息(Runtime Type Informatiion, RTTI)不同,反射重点在运行时检测、感知、改变自身的结构和行为。

    反射是元编程(metaprogramming)的重要组成部分。

    PHP反射API均以 Reflection 开头

    反射不是语法分析,不操作表达式、代码语句。

    反射获取的是代码的结构,即函数、类这些构件的结构。

    PHP中的反射API均以 Reflection 开头(接口 Reflector 除外),重点在函数和类两种结构。

    而函数可以看成类的成员函数(多一个隐式的 this 参数)或者静态成员函数(public类型),所以了解反射API可从类信息的 ReflectionClass 开始。

    ReflectionClass 提供了以下获取类基本信息的接口

    getProperties:获取成员变量/属性,返回一个 ReflectionProperty 数组;

    ReflectionProperty 类中有对属性详细说明的API:
    是否默认属性(isDefault),
    是否私有属性(isPrivate)等。

    同时 ReflectionClass 还提供获取特定类别属性的API:

    • getDefaultProperties
    • getStaticProperties;

    getConstants:获取类中定义的常量;
    getMethods:获取类中定义的方法,返回一个 ReflectionMethod 数组;

    ReflectionMethod 将在下文讲解;
    getInterfaces:获取类实现的接口;
    getParentClass:获取父类的 ReflectionClass实例。

    在反射中,类、接口、特性不分家,所以 ReflectionClass 提供类型判定API:isInterface、isTrait

    除了以上基本信息,ReflectionClass(包括ReflectionMethod/ReflectionFunction)还提供了一些不可思议的能力:

    • getDocComment:获取类的文档注释信息;
    • getFilename:获取类定义的文件;
    • getStartLine: 获取类定义的起始行号;
    • getEndLine: 获取类定义的结束行号;
    • getModifiers:获取类定义的修饰符,其意义名字可通过 Reflection::getModifierNames 得到,例如:abstract,final

    如果说前述的类结构信息可以通过现有的API获取(method_exits/property_exits等),上面列出的功能基本上只能通过反射API获取(PHP文件中定义的类并且知道定义文件,可以利用token_get_all 得到相同结果,但是实现非常复杂)。

    这些行为发生在运行期间,由此可见反射API在分析类结构信息功能上的强大。

    除了 ReflectionClass,ReflectionMethod 和 ReflectionFunction是另外反射中另外两个重要的类。

    函数(function)定义在类外部,方法(method)定义在类内部,两者其实同源,在反射API中有共同的父类:ReflectionFunctionAbstract

    ReflectionFunctionAbstract 有两者的大部分API,并且基本上是最重要的API。

    其中最值得关注的是其参数信息的API:getParameters

    其获取函数的参数信息,返回一个 ReflectionParameter 数组。
    结合 getParameters 和 ReflectionParameter,函数(方法)的结构基本上就清晰了。

    API操作

    知道人体构造和体内真气分布,你可以引导真气到手指,练成一阳指、六脉神剑、弹指神通、九阴白骨爪等;

    也可以让真气汇聚,冲破任督二脉,开辟洞天;还可以逆转全身经脉,练成蛤蟆功…内省的好处可见一斑。

    反射让代码感知自身结构,有什么好处呢?

    反射API提供了三种在运行时对代码操作的能力:

    设置访问控制权:setAccessible,可获取私有的方法/属性。
    注意:setAccessible 只是让方法/成员变量可以 invoke/getValue/setValue,并不代表类定义的访问存取权限改变;

    调用函数/方法:invoke/invokeArgs
    配合获取函数参数的API,可以安全的传参和调用函数call_user_func(_array)的增强版;

    不依赖构造函数生成实例:newInstanceWithoutConstructor

    单例来说一下反射API的功能,单例类代码如下:

    #foo.php
    <?php
    
    class Foo
    {
        private static $id;
        private static $instance;
    
        private function __construct()
        {
            ++self::$id;
            fwrite(STDOUT, "construct, instance id: " . self::$id . "\n");
        }
    
        public static function getSingleton()
        {
            if (self::$instance === null) {
                self::$instance = new self();
            }
            return self::$instance;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    在 Foo 类中,构造函数是私有,获取实例只能通过 getSingleton 方法,并且获取到的是单例。

    但在反射API加持下,能获取多个实例:

    $instance1 = Foo::getSingleton();
    var_dump($instance1);
    
    PS E:\PDF\test> php .\foo.php
    construct, instance id: 1
    object(Foo)#1 (0) {
    }
    PS E:\PDF\test>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    # ReflectionClass 类报告了一个类的有关信息,建立Foo这个类的反射类
    $class = new ReflectionClass("Foo");
    # 获取类的构造函数
    $constructor = $class->getConstructor();
    
    // var_dump(ReflectionProperty::IS_PUBLIC);exit;int(1)
    // var_dump($constructor->getModifiers());
    // PS E:\PDF\test> php .\foo.php
    // int(4)
    // construct, instance id: 1
    // object(Foo)#3 (0) {
    // }
    
    // var_dump(ReflectionProperty::IS_PUBLIC & $constructor->getModifiers());exit;//0
    
    # ReflectionProperty::IS_PUBLIC,检查属性是否为公共
    # Gets the property modifiers,获取属性修饰符
    if ((ReflectionProperty::IS_PUBLIC & $constructor->getModifiers()) === 0) {
        # 设置访问控制权:setAccessible 可获取私有的方法/属性。
        # 注意:setAccessible 只是让方法/成员变量可以 invoke/getValue/setValue,并不代表类定义的访问存取权限改变;
        $constructor->setAccessible(true);
    }
    # 创建一个新的类的实例而不调用它的构造函数。
    $instance2 = $class->newInstanceWithoutConstructor();
    # ReflectionMethod::invoke()函数是PHP中的内置函数,用于调用指定的反射方法并返回方法的结果。
    $constructor->invoke($instance2);
    var_dump($instance2);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    # 脚本执行结果
    construct, instance id: 1
    object(Foo)#1 (0) {
    }
    construct, instance id: 2
    object(Foo)#4 (0) {
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    我们成功的生成了两个实例,并调用构造函数完成对象初始化。
    如果没有反射API,这几乎是不可能完成的工作。

    反射API和函数式API在功能上的差异

    功能函数式API反射API
    函数是否存在function_existsReflectionFunction
    类是否存在class_exitsReflectionClass
    方法是否存在method_exitsReflectionMethod
    变量/属性是否存在property_exitsReflectionProperty
    获取类变量get_class_varsReflectionClass::getProperties
    获取类方法get_class_methodsReflectionClass::getMethods
    获取类常量ReflectionClass::RegetReflectionConstant(s)
    获取函数/方法参数信息ReflectionFunction/Method::getParameters
    获取函数/方法返回值ReflectionFunction/Method::getReturnType
    类使用的特性class_usesReflectionClass::getTraits
    获取父类class_parentsReflectionClass::getParentClass
    获取类实现的接口class_implementsReflectionClass::getInterfaceNames
    获取类所在名字空间__NAMESPACE__ReflectionClass::getNamespaceName
    函数调用call_user_func(_array)ReflectionMethod(Function)::invoke(Args)
    获取类名__CLASS__/::classReflectionClass::getName
    获取函数名__METHOD__/__FUNCTION__ReflectionFunction/Method::getName
    获取类/常量/变量/方法修饰符ReflectionClass/Constant/Property/Method::getModifiers
    获取所在文件__FILE__ReflectionClass/Constant/Function/Method::getFileName
    获取所在行(范围)ReflectionClass/Function/Method::getStartLine/getEndLine
    获取文档ReflectionClass/Function/Method::getDocComment
    extension_loadedReflectionZendExtension
    拓展get_loaded_extensionsReflectionExtension
    get_extension_funcs

    函数式API和反射API在功能上的差异

    功能函数式API反射API
    类型判断is_int/is_bool/is_array等
    获取对象的类名get_classReflectionObject::getName
    获取对象父类get_parent_classReflectionObject::getParentClass
    类型/继承检测instanceof/is_a/is_subclass_ofReflectionObject::isInstance/isSubclassOf
    生成器ReflectionGenerator

    php 反射类 ReflectionClass

    
    
    class fuc
    {
        //定义一个类
        public static function ec()
        {
            echo'我是一个类';
        }
    
    }
    
    $class = new ReflectionClass('fuc'); //建立 fuc这个类的反射类
    
    echo $class; //输出这反射类
    
    //相当于实例化 fuc 类
    $fuc=$class->newInstance();  
    //执行 fuc 里的方法ec
    $fuc->ec(); 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    PS E:\PDF\test> php .\foo.php
    Class [ <user> class fuc ] {
      @@ E:\PDF\test\foo.php 3-11
    
      - Constants [0] {
      }
    
      - Static properties [0] {
      }
    
      - Static methods [1] {
        Method [ <user> static public method ec ] {
          @@ E:\PDF\test\foo.php 6 - 9
        }
      }
    
      - Properties [0] {
      }
    
      - Methods [0] {
      }
    }
    我是一个类
    PS E:\PDF\test>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    示例1

    
    /**
     * Created by PhpStorm.
     * User:  author
     * Date: 2017/6/12
     * Time: 14:34
     * 关于反射类的理解
     */
    class Person {
        /**
         * The attributes that are mass assignable.
         * @type int
         * 属性注释
         */
        public  $datang = '123';
        public  $datang1 = '1234';
     
        private function getName(){
            echo  $this->datang;
        }
     
        public function getName1(){
            echo $this->datang1;
        }
     
    }
     
    //建立 Person这个类的反射类
    $class = new ReflectionClass('Person');
     
    //打印所有属性名,包含private,protected,public
    $properties = $class->getProperties();
    foreach ($properties as $key => $value) {
        var_dump($value->getName());
    }
    
    /*exit;
    PS E:\PDF\test> php .\foo.php
    string(6) "datang"
    string(7) "datang1"
    */
    
    //打印所有属性的注释并正则,包含private,protected,public
    foreach($properties as $property) {
        $docblock = $property->getDocComment();
        //只能打印多行注释
        var_dump($docblock);
        preg_match('/ type\=([a-z_]*) /', $docblock, $matches);
        var_dump($matches);
    }
    
    
    /* exit;
    string(100) "
         * The attributes that are mass assignable.
         * @type int
         * 属性注释
         *
    array(0) {
    }
    bool(false)
    array(0) {
    }
    */
     
    //打印所有方法名
    foreach ($class->getMethods() as $key => $value) {
        var_dump($value->getName());
    }
    
    /*exit;
    string(7) "getName"
    string(8) "getName1"
    */
     
    /** 如何执行方法 */
    //相当于实例化Person 类
    $instance  = $class->newInstanceArgs();
    //执行getName1方法,方法必须是public属性的,否则会报fatal error
    $instance->getName1();
     
    //获取Person getName1方法
    $ec=$class->getmethod('getName1');
    //执行getName1方法,方法必须是public属性的,否则会报fatal error
    $ec->invoke($instance);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85

    简单的学习反射

    
    /*
     *   简单的学习反射
     */
    class Reflectionstudy
    {
        /** name 姓名*/
        private $name;
    
        /*
         * 反射
         */
        public static function ref1()
        {
            $ref   = new ReflectionClass('Redis'); //获得Redis的一个反射实例
            $const = $ref->getConstants(); //返回所有常量名和值
            echo "----------consts---" . PHP_EOL;
            array_walk($const, function ($val, $key) {
                echo $key, ':', $val, PHP_EOL;
            });
            $props = $ref->getDefaultProperties(); //返回类中所有属性
            echo "----------props---" . PHP_EOL;
            array_walk($props, function ($val, $key) {
                echo $key, ':', $val, PHP_EOL;
            });
            $methods = $ref->getMethods(); //返回类中所有方法
            echo "----------methods---" . PHP_EOL;
            array_walk($methods, function ($val) {
                echo $val, PHP_EOL;
            });
    
            //当想获取具体的方案有哪些参数时,我们需要进一步反射
            echo "----------params---" . PHP_EOL;
            echo $ref->getMethod('bitpos'); //传入方法名,会调用$reflectMetho                                                                                        d->__tostring() 返回可打印的数组
        }
        /**
         * get name of person
         *@param $v string name
         *@return $name
         */
        public function getName()
        {
            return self::$name;
        }
        public function setName($v)
        {
            self::$name = $v;
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    1、用户获取对象相关信息 ReflectionObject

    /*
    1、用户获取对象相关信息ReflectionObject
    */
    $ro = new ReflectionObject(new Reflectionstudy);
    print_r($ro->getMethods());
    
    • 1
    • 2
    • 3
    • 4
    • 5
    PS E:\PDF\test> php .\foo.php
    Array
    (
        [0] => ReflectionMethod Object
            (
                [name] => ref1
                [class] => Reflectionstudy
            )
    
        [1] => ReflectionMethod Object
            (
                [name] => getName
                [class] => Reflectionstudy
            )
    
        [2] => ReflectionMethod Object
            (
                [name] => setName
                [class] => Reflectionstudy
            )
    
    )
    PS E:\PDF\test>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    2、反射某个方法以及注释信息

    /*
    2、反射某个方法以及注释信息
    */
    $rm = new ReflectionMethod('Reflectionstudy','getName');
    echo $rm->isPublic(),PHP_EOL;//确认方法是否公有,返回BOOL值
    echo $rm->getDocComment(),PHP_EOL;//返回文档注释
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    PS E:\PDF\test> php .\foo.php
    1
    /**
         * get name of person
         *@param $v string name
         *@return $name
         */
    PS E:\PDF\test>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    3、ReflectionParameter获取函数或方法参数的相关信息

    /*
    3、ReflectionParameter获取函数或方法参数的相关信息
    */
    
    $p = new ReflectionParameter(['Reflectionstudy','setName'],0);
    echo $p->getPosition(),PHP_EOL;//0,从左向右获得参数,以0开始
    echo $p->getName(),PHP_EOL;//v
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    PS E:\PDF\test> php .\foo.php
    0
    v
    PS E:\PDF\test>
    
    • 1
    • 2
    • 3
    • 4

    4、获取类内属性注释

    /*
    4、ReflectionProperty获取类的属性的相关信息
    */
    $p = new ReflectionProperty('Reflectionstudy','name');
    echo $p->getDocComment(),PHP_EOL;//获取类内属性注释
    
    • 1
    • 2
    • 3
    • 4
    • 5
    PS E:\PDF\test> php .\foo.php
    /** name 姓名*/
    PS E:\PDF\test>
    
    • 1
    • 2
    • 3

    5、反射类大致内容

    //反射类大致内容
    Reflectionstudy::ref1();
    
    • 1
    • 2

    反射的机制直接将 private 方法设置成外部可访问

    
    class Foo {
      private function myPrivateMethod() {
        return 7;
      }
    }
    
    $method = new ReflectionMethod('Foo', 'myPrivateMethod');
    
    //该反射功能直接将原本是private权限的方法设置成可访问
    $method->setAccessible(true);
    
    echo $method->invoke(new Foo);
    // echos "7"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
  • 相关阅读:
    ~LVS的nat模式+ipvsadm命令的使用+lvs的dr模式实现+lvs火墙标记
    SpringBoot的创建与使用
    常用Docker命令
    vue之组件传值---父传子(属性)---子传父(emit,sync,v-model)
    [附源码]SSM计算机毕业设计基于实时定位的超市配送业务管理JAVA
    解决Tomcat中文乱码问题
    自定义Dynamics 365实施和发布业务解决方案 - 6. Azure集成
    RocketMQ 安装和启动
    Linux内存状态监测工具smem命令 | 如何在#linux OS下找到特定进程的交换(swap)空间使用情况?
    Linux定时任务详解
  • 原文地址:https://blog.csdn.net/weiguang102/article/details/127535021