TP5源码分析前置知识-API反射机制

1、废话稍作唠叨

反射类则是拆封类中的所有方法、成员变量,并包括私有方法等。就如“解刨”一样,所有五腹六脏一并呈现出来

  • 所有的反射API类的官方文档地址:https://www.php.net/reflection/

2、常用API类

  • 常用的反射类
类名 作用
Reflection 可以打印类的基本信息,(通过提供的静态export()函数)
ReflectionMethod 见名知意,打印类方法、得到方法的具体信息等
ReflectionClass 用于得到类信息,比如得到类包含的方法,类本的属性,是不是抽象类等
ReflectionParameter 显示参数的信息,可以动态得到已知方法的传参情况
ReflectionException 用于显示错误信息
ReflectionExtension 得到PHP的扩展信息,可以判断扩展是否存在等

3、简单使用demo

  • 创建一个Man类并实例化
class Man {
    public  $name;
    private $age;

    public function __construct($name, $age)
    {
        $this->name = $name;
        $this->age = $age;
    }

    /**
     * 获取名
     * @return string
     */
    public function getName()
    {
        return $this->name;
    }

    /**
     * 设置名
     * @param string $username
     */
    public function setName($name)
    {
        $this->name = $name;
    }

    /**
     * 获取年龄
     * @return string
     */
    private function getAge()
    {
        return $this->password;
    }

    /**
     * 设置年龄
     * @param int $age
     */
    private function setAge($age)
    {
        $this->age = $age;
    }
}


$personObj = new Man('xiaoming', 34);

  • 反射属性
$refClass = new ReflectionClass($personObj);

或者

$refClass = new ReflectionClass('Man'); // 将类名作为参数,建立反射类

$properties = $refClass->getProperties(); //反射所有属性
打印结果:
Array
(
    [0] => ReflectionProperty Object
        (
            [name] => name
            [class] => Man
        )
    [1] => ReflectionProperty Object
        (
            [name] => age
            [class] => Man
        )
)

$properties = $refClass->getProperty('age'); // 获取Man类的age属性
打印结果:
ReflectionProperty Object
(
    [name] => age
    [class] => Man
)

  • 反射方法

$methods = $refClass->getMethods(); // 获取Man类的所有方法

打印结果:
Array
(
    [0] => ReflectionMethod Object
        (
            [name] => __construct
            [class] => Man
        )
    [1] => ReflectionMethod Object
        (
            [name] => getName
            [class] => Man
        )
    [2] => ReflectionMethod Object
        (
            [name] => setName
            [class] => Man
        )
    [3] => ReflectionMethod Object
        (
            [name] => getAge
            [class] => Man
        )
    [4] => ReflectionMethod Object
        (
            [name] => setAge
            [class] => Man
        )

)

$method = $refClass->getMethod('getName');  // 获取Man类的getName方法
打印结果:
ReflectionMethod Object
(
    [name] => getName
    [class] => Man
)
  • 反射注释
$classDocComment = $refClass->getDocComment();  // 获取Man类定义在类之前的注释
打印结果:
/** * Man类 */

$methodDocComment = $refClass->getMethod('setAge')->getDocComment();  // 获取Man类中setAge方法的注释
打印结果:
/** * 设置年龄 * @param int $age */
  • 反射实例化新类
$newInstance = $refClass->newInstance('xiaoPeng', 23);  // 从指定的参数创建一个新的类实例

打印结果:
Man Object ( [name] => xiaoPeng [age:Man:private] => 23 )

$params = ['xiaoPeng2', 44];
$newInstance = $refClass->newInstanceArgs($params); // 从给出的参数创建一个新的类实例

打印结果:
Man Object ( [name] => xiaoPeng2 [age:Man:private] => 44 )
  • 反射实例化新类可访问、执行类的公有方法——public
方法一:
$newInstance->setName('xiaoPeng3'); // 调用Man类的实例调用setName方法设置名
$name = $newInstance->getName(); // 调用Man类的实例调用getName方法获取名
打印结果:xiaoPeng3

方法二:
$refClass->getProperty('name')->setValue($newInstance, 'xiaopeng3'); // 通过反射类ReflectionProperty设置指定实例的name属性值
$name = $refClass->getProperty('name')->getValue($newInstance); // 通过反射类ReflectionProperty获取name的属性值
打印结果:xiaopeng3

方法三:
$refClass->getMethod('setName')->invoke($newInstance, 'xiaopeng5'); // 通过反射类ReflectionMethod调用指定实例的方法,并且传送参数
$value = $refClass->getMethod('getName')->invoke($newInstance); // 通过反射类ReflectionMethod调用指定实例的方法
打印结果:
xiaopeng5

  • 反射实例化新类可访问、执行类的非公有方法——private、protecte
$property = $refClass->getProperty('age');
$property->setAccessible(true); // 修改 $property 对象的可访问性
$property->setValue($newInstance, 23); // 可以执行
$value = $property->getValue($newInstance); // 可以执行
echo $value;
打印结果:
23

注意:直接访问 protected 或则 private 的属性、方法,会抛出异常;必须先setAccessible(true)

3、使用场景

1、编写单元测试大量使用了反射

2、PHP各MVC大框架都有使用

  • 小demo
class Mysql
{
    function connect($db)
    {
        echo "连接到数据库{$db[0]}".PHP_EOL;
    }
}
//sql动态代理实例化类
class SqlProxy
{
    private $instance=[];

    public function __construct($obj)
    {
        $this->instance[] = new $obj;
    }
    public function __call($name,$args)
    {
        foreach($this->instance as $obj)
        {
            $r = new ReflectionClass($obj);
            if($method = $r->getMethod($name))
            {
                if($method->isPublic() && !$method->isAbstract())
                {
                    $method->invoke($obj,$args);
                }
            }
        }
    }
}


$obj = new SqlProxy('Mysql');

$obj->connect('ok');

打印结果:
连接到数据库ok

稍作总结:实际上 SqlProxy 类起到了动态代理的那么一个过程,不用直接new Mysql 直接传入要new 的类名字符串,在代理类中通过反射去帮你实例化目标类

4、稍作总结

毕竟反射类可以暴漏类的一切,故此还是比较危险的,所有用起来是方便,但是有利有弊,自己权衡使用。一般在业务应用中很少用,一般都出现在框架等中

5、亮哥多嘴

由于菜鸟记性稍差,一直秉性好记性不如烂键盘,把自己的感悟记录下来,如果有不妥之处,欢迎各位大神指正,文中借鉴了很多互联网上的作品思想,如有雷同,并不巧合,但是文中的一行行代码都是本菜鸟码出来的,阿弥陀佛

猜你喜欢

转载自blog.csdn.net/weixin_38048544/article/details/106605391