PHP核心技术——反射

反射:

  • 反射指在PHP运行状态中,扩展分析PHP程序,导出或提取出关于类、方法、属性、参数等的详细信息,包括注释。这种动态获取信息以及动态调用对象方法的功能称为反射API
class person{
    public $name;
    public $gender;
    public function say(){
        echo $this->name,"\tis",$this->gender,"\r\n";
    }
    public function __set($name,$value){
        echo "Setting $name to $value \r\n";
        $this->$name=$value;
    }
    public function __get($name){
        if (!isset($this->$name)) {
            echo '末设置';
            $this->$name="正在为你设置默认值";
        }
        return $this->$name;
    }
}
$student = new person();
$student -> name='Tom';
$student -> gender='male';
$student ->age=24;

//获取对象属性列表
$reflect = new ReflectionObject($student);
$props = $reflect -> getProperties();
foreach ($props as $prop) {
    print $prop -> getName()."\n";
}
//获取对象方法列表
$m = $reflect -> getMethods();
foreach ($m as $prop) {
    print $prop -> getName()."\n";
}
//返回对象属性的关联数组
var_dump(get_object_vars($student));
//类属性
var_dump(get_class_vars(get_class($student)));
//返回由类的方法名组成的数组
var_dump(get_class_methods(get_class($student)));
//获取对象属性列表所属的类
echo get_class($student);
  • 反射API的功能显然更强大,甚至能还原这个类的原型,包括方法的访问权限,代码如下
//反射获取类的原型
$obj = new ReflectionClass('person');
$className = $obj -> getName();
$Methods = $Properties = array();
foreach ($obj -> getProperties() as $v) {
    $Properties[$v -> getName()] = $v;
}
foreach ($obj ->getMethods() as $v) {
    $Methods[$v -> getName()] = $v;
}

echo "class {$className}\n{\n";
is_array($Properties)&&ksort($Properties);

foreach ($Properties as $k => $v) {
    echo "\t";
    echo $v -> isPublic()?'public':'',$v -> isPrivate()?'private':'',
    $v -> isProtected()?'protected':'',
    $v -> isStatic()?'static':'';
    echo "\t{$k}\n";
}

echo "\n";
if (is_array($Methods)) ksort($Methods);
foreach ($Methods as $k => $v) {
    echo "\tfunction {$k}(){}\n";
}
echo "}\n";
  • 反射可以探知类的内部结构,可以用它做hook实现插件功能或者是做动态代理
class mysql{
    function connect($db){
        echo "连接到数据库${db[0]}\r\n";
    }
}
class sqlproxy{
    private $target;
    function __construct($tar){
        $this -> target[] = new $tar();
    }
    function __call($name,$args){
        foreach ($this -> target as $obj) {
            $r = new ReflectionClass($obj);
            if ($method = $r -> getMethod($name)) {
                if ($method -> isPublic() && !$method -> isAbstract()) {
                    echo "方法前拦截记录LOG\r\n";
                    $method -> invoke($obj,$args);
                    echo "方法前拦截\r\n";
                }
            }
        }
    }
}
$obj = new sqlproxy('mysql');
$obj -> connect('member');
  • 真正的操作类是mysql类,但是sqlproxy类实现了根据动态传入参数,代替实际的类运行,并且在方法运行前后进行拦截,并且动态地改变类中的方法和属性。这就是简单的动态代理。

参考资料

  • [PHP核心技术与最佳实践]
posted @ 2019-03-27 15:15  高级生命体  阅读(164)  评论(0编辑  收藏  举报