简单学习下PHP的反射

手册原文:

PHP 5, 具有完整的反射 API,添加了对类、接口、函数、方法和扩展进行反向工程的能力。 此外,反射 API 提供了方法来取出函数、类和方法中的文档注释。

在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;
    }
    private function fnPrivate(){
        echo 'I am a private function';
    }
    protected function fnProtected(){
        echo 'I am a protected function';
    }
}
$stu = new Person();
$stu->name = 'aben';
$stu->age = 18;
$stu->sex = 'male'; //动态创建的属性

上面的代码写了一个简单的类,然后实例化它,然后赋值,让它有意义.

我们现在就可以通过反射API来获取这个stu对象的方法和属性的一个列表:

$reflect = new ReflectionObject($stu);
$props = $reflect->getProperties();//获取对象的属性列表
$methods = $reflect->getMethods();//获取对象的方法列表
$arr_prop_name = [];
foreach($props AS $key=>$value){
    $arr_prop_name[] = $value->getName();
}
echo 'properties: '.implode(', ', $arr_prop_name).PHP_EOL;
$arr_method_name = [];
foreach($methods AS $key=>$value){
    $arr_method_name[] = $value->getName();
}
echo 'methods: '.implode(', ', $arr_method_name).PHP_EOL;

执行结果:

properties: name, age, sex
methods: say, set, get, fnPrivate, fnProtected

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

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

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

不过反射API功能更强大, 还可以获取这个类的原型, 包括方法的修饰符.

$reflect = new ReflectionObject($stu);
$props = $reflect->getProperties();//获取对象的属性列表
$methods = $reflect->getMethods();//获取对象的方法列表
$class_name = $reflect->getName();
$arr_method = $arr_prop = [];
foreach($props AS $key=>$value){
    $arr_prop[$value->getName()] = $value;
}
foreach($methods AS $key=>$value){
    $arr_method[$value->getName()] = $value;
}
is_array($arr_prop) && ksort($arr_prop);
is_array($arr_method) && ksort($arr_method);
echo 'class '.$class_name.' {'.PHP_EOL;
foreach($arr_prop AS $key=>$value){
    echo "\t";
    echo $value->isPublic() ? 'public':'';
    echo $value->isPrivate() ? 'private':'';
    echo $value->isProtected() ? 'protected':'';
    echo "\t".$value.PHP_EOL;
}
foreach($arr_method AS $key=>$value){
    echo "\t";
    echo $value->isPublic() ? 'public':'';
    echo $value->isPrivate() ? 'private':'';
    echo $value->isProtected() ? 'protected':'';
    echo "\t function ".$value.' (){}'.PHP_EOL;
}
echo '}';

执行结果如下:

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

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

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

那反射通常可以用来做什么呢??

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

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

class Mysql{
    public function connect($db_name){
        echo 'connect to database ';
        echo json_encode($db_name);
        echo PHP_EOL;
    }
}
class SqlProxy{
    private $target;
    public function __construct(string $tar) {
        $this->target = new $tar();
    }
    public function __call(string $name, array $args){
        echo '$name:' . $name.PHP_EOL;
        echo '$args: '.PHP_EOL;
        print_r($args);

        $reflect = new ReflectionClass($this->target);
        $arr_method = $reflect->getMethods();
        if($arr_method){
            foreach($arr_method AS $key=>$value){
                if($value->isPublic() && !$value->isAbstract()) {
                    echo '方法前拦截' . PHP_EOL;
                    //执行一个反射的方法
                    $value->invoke(new Mysql(), $args[0]); //这里暂时还不确定该如何传第一个参数
                    echo '方法后拦截' . PHP_EOL;
                }
            }
        }
    }
}
$obj = new SqlProxy('Mysql');
$obj->connect('local');

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

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

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

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

to be continued...

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值