PHP反射机制简单理解 - Go语言中文社区

PHP反射机制简单理解


什么是反射呢?

在PHP的面向对象编程中的对象,它被系统赋予自省的能力,而这个自省的过程,我们把它叫做反射。

我们对反射的直观理解可以是,根据达到地,找到出发地和来源这么一个过程,通俗来讲就是,我给你一个光秃秃的对象,完事你可以根据这个对象,知道它所属的类,拥有哪些方法。

在PHP中,反射是指在PHP运行状态中,扩展分析PHP程序,导出或者提取出关于类、属性、方法、参数等的详细信息,包括注释。这种动态获取信息以及动态调用对象方法的功能,被称为反射API。

我们接下来通过一段代码来感受下:

class person{
    public $name;
    public $age;

    public function say()
    {
        echo $this->name."<br>".$this->age;
    }

    public function set($name,$value)
    {
        echo 'set name to value';
        $this->$name = $value;
    }

    public function get($name)
    {
        if(!isset($this->$name)){
            echo 'unset name';
            $this->$name = 'seting~~~';
        }

        return $this->$name;
    }
}

$stu = new person();
$stu->name = 'luyaran';
$stu->age = 26;
$stu->sex = 'girl';

上述代码是一个简单的类,我们通过实例化它,以及赋值,让它含有意义。

完事,我们就来通过反射API获取这个stu对象的方法和属性的一个列表:

//获取对象的属性列表
$reflect = new ReflectionObject($stu);
$props = $reflect->getProperties();
foreach ($props as $key_p => $value_p) {
    var_dump($value_p->getName());
}
//获取对象的方法列表
$method = $reflect->getMethods();
foreach ($method as $key_m => $value_m) {
    var_dump($value_m->getName());
}

除了反射API之外,我们还可以使用class函数来获取对象的各种属性以及方法的数据,如下:

// 获取对象的属性的关联数组
var_dump(get_object_vars($stu));
//获取类属性
var_dump(get_class_vars(get_class($stu)));
//获取类的方法名称组成的数组
var_dump(get_class_methods(get_class($stu)));

值得一说的是,这个get_class这个函数,还可以获取从其他页面传递过来的对象的属性列表以及所属的类。

不过,class函数和反射API相比较起来,个人感觉还是后者更胜一筹啊。

反射API甚至可以还原这个类的原型,包括方法的访问权限,来看代码感受下:

//实例化反射API获取类名
$obj = new ReflectionObject($stu);
$class_name = $obj->getName();
$method_arr = $props_arr = array();

//获取对象的属性列表
$props = $obj->getProperties();
foreach ($props as $key_p => $value_p) {
    $props_arr[$value_p->getName()] = $value_p;
}
//获取对象的方法列表
$method = $obj->getMethods();
foreach ($method as $key_m => $value_m) {
    $method_arr[$value_m->getName()] = $value_m;
}

//格式化输出类的属性以及方法
echo "class $class_name { n";
is_array($props_arr) && ksort($props_arr);
foreach ($props_arr as $key_o => $value_o) {
    echo "t";
    echo $value_o->isPublic() ? 'public' : ' ' ,$value_o->isPrivate() ? 'private' : ' ' ,$value_o->isProtected() ? 'protected' : ' ' ,$value_o->isStatic() ? 'static' : ' ';
    echo "t$value_on";
}
echo "n";
is_array($method_arr) && ksort($method_arr);
foreach ($method_arr as $key_e => $value_e) {
    echo "t";
    echo $value_e->isPublic() ? 'public' : ' ' ,$value_e->isPrivate() ? 'private' : ' ' ,$value_e->isProtected() ? 'protected' : ' ';
    echo "tfunction $value_e () {} n";
}
echo '}';

根据上述代码,输出结果如下: 

我们可以看到,上图很详细的输出了这个类的构造。

不仅如此哦,PHP手册中关于反射API的数量,多达几十个,可以这么说,反射完整的描述了一个类或者对象的原型。

同时呢,反射不仅可以用作类和对象,还可以用于函数,扩展模块,异常等。

咱们呢,在这里就不赘述了,最后一点篇幅,就来聊聊反射的一些作用。

首先,它可以用作文档生成,所以,我们可以用它对文档中的类进行扫描,逐个生成扫描文档。

反射可以探知类的内部结构,也可以用作hook来实现插件功能,还有就是可以做动态代理。

咱们来看段MySQL的动态代理的代码感受下:

class mysql{
    public function connect($db_name)
    {
        echo "we will connect database $db_name rn";
    }
}

class sql_proxy{
    private $target;

    public function __construct($tar)
    {
        $this->target = new $tar();
    }

    public function __call($name,$args)
    {
        $reflect = new ReflectionClass($this->target);
        $method = $reflect->getMethods();
        if ($method) {
            foreach ($method as $key_method => $value_method) {
                if($value_method->isPublic() && !$value_method->isAbstract()){
                    echo "方法前拦截记录LOGrn";
                    $value_method->invoke();
                    echo "方法后拦截记录LOGrn";
                }
            }
        }
    }
}

$obj = new sql_proxy('mysql');
$obj->coonect('luyaran');

上述代码真正的操作类是mysql,下面的sql_proxy只是根据动态传入的参数,来代替了实际运行的类,并且可以在方法运行的前后进行拦截,还可以动态地改变类中的方法和属性,这个可以叫做简单的动态代理类。

在我们平常的开发中,用到反射的地方不多,一般是用来对对象进行调试,还有就是获取类的信息,但是在MVC和插件中,比较常见,并且反射的消耗也是不小的,我们在有另外一种方案的时候,尽量不要选择反射。

我们还可以通过PHP中的token函数来实现简单的反射功能,不过,从简单灵活的角度来看,还是使用已有的反射API比较好。

很多时候,善用某个东西,会使得我们的代码,简洁又优雅,但是不能贪多,比如这个反射API,用的多了,会破坏我们类的封装性,使得本不应该暴露的方法暴露了出来,这是优点也是缺点,我们要搞搞清楚。

好啦,本次记录就到这里了。

如果感觉不错的话,请多多点赞支持哦。。。

版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/luyaran/article/details/84339363
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。
  • 发表于 2021-12-11 17:35:23
  • 阅读 ( 1262 )
  • 分类:

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢