资讯专栏INFORMATION COLUMN

【modernPHP专题(2)】反射机制Reflection

mrli2016 / 2761人阅读

摘要:简介是才有的新功能,它是用来导出或提取出关于类方法属性参数等的详细信息,包括注释。

简介

PHP Reflection API是PHP5才有的新功能,它是用来导出或提取出关于类、方法、属性、参数等的详细信息,包括注释。

class Reflection { } 
interface Reflector { }
class ReflectionException extends Exception { }
class ReflectionFunction implements Reflector { }
class ReflectionParameter implements Reflector { }
class ReflectionMethod extends ReflectionFunction { }
class ReflectionClass implements Reflector { }
class ReflectionObject extends ReflectionClass { }
class ReflectionProperty implements Reflector { }
class ReflectionExtension implements Reflector { } 

用得比较多的就只有两个ReflectionClassReflectionObject,两个的用法都一样,只是前者针对类,后者针对对象,后者是继承前者的类;然后其中又有一些属性或方法能返回对应的Reflection对象(ReflectionProperty以及ReflectionMethod)

ReflectionClass

具体参考手册:http://php.net/manual/zh/clas...

通过ReflectionClass,我们可以得到Person类的以下信息:

常量 Contants

属性 Property Names

方法 Method Names

静态属性 Static Properties

命名空间 Namespace

Person类是否为final或者abstract

id;
  }

  public function setId($v){
    $this->id = $v;
  }

  public function getName(){
    return $this->name;
  }

  public function setName($v){
    $this->name = $v;
  }

  public function getBiography(){
    return $this->biography;
  }

  public function setBiography($v){
    $this->biography = $v;
  }
}

//传递类名或对象进来
$class = new ReflectionClass("appPerson");

//获取属性,不管该属性是否public
$properties = $class->getProperties();
foreach($properties as $property) {
  echo $property->getName()."
";
}
// 输出:
// _allowDynamicAttributes
// id
// name
// biography

//默认情况下,ReflectionClass会获取到所有的属性,private 和 protected的也可以。如果只想获取到private属性,就要额外传个参数:
/*
 * ReflectionProperty::IS_STATIC
 * ReflectionProperty::IS_PUBLIC
 * ReflectionProperty::IS_PROTECTED
 * ReflectionProperty::IS_PRIVATE
 */
 
 //↓↓ 注意一个|组合: 获得IS_PRIVATE或者IS_PROTECTED的属性
$private_properties = $class->getProperties(ReflectionProperty::IS_PRIVATE|ReflectionProperty::IS_PROTECTED);

foreach($private_properties as $property) {
  //↓↓如果该属性是受保护的属性;
  if($property->isProtected()) {

    // ↓↓ 获取注释
    $docblock = $property->getDocComment();
    preg_match("/ type=([a-z_]*) /", $property->getDocComment(), $matches);
    echo $matches[1]."
";
  }
}
// Output:
// primary_autoincrement
// varchar
// text

$data = array("id" => 1, "name" => "Chris", "biography" => "I am am a PHP developer");
foreach($data as $key => $value) {
  if(!$class->hasProperty($key)) {
    throw new Exception($key." is not a valid property");
  }

  if(!$class->hasMethod("get".ucfirst($key))) {
    throw new Exception($key." is missing a getter");
  }

  if(!$class->hasMethod("set".ucfirst($key))) {
    throw new Exception($key." is missing a setter");
  }

  $object = new Person();

  // http://php.net/manual/zh/class.reflectionmethod.php
  // getMethod 获得一个该方法的reflectionmethod对象,然后使用里面的invoke方法;
  $setter = $class->getMethod("set".ucfirst($key));
  $ok = $setter->invoke($object, $value);

  // Get the setter method and invoke it
  $getter = $class->getMethod("get".ucfirst($key));
  $objValue = $getter->invoke($object);

  // Now compare
  if($value == $objValue) {
    echo "Getter or Setter has modified the data.
";
  } else {
    echo "Getter and Setter does not modify the data.
";
  }
}
getMethod and invoke

ReflectionClass::getMethod — 获取一个类方法的 ReflectionMethod(可以理解为获得这个类方法的控制权,不管这个类方法是否是public)。

具体的参考:

http://php.net/manual/zh/clas...

http://php.net/manual/zh/refl...

 isPublic()){
  $reflectionMethod -> setAccessible(true);
}
/*
 * public mixed ReflectionMethod::invoke ( object $object [, mixed $parameter [, mixed $... ]] )
 * 1. 获得某个类方法的ReflectionMethod
 * 2. $object 该方法所在的类实例的对象,然后第二参数起对号入座到该方法的每个参数;
 * 3. 通过invoke就可以执行这个方法了
 */
echo $reflectionMethod->invoke($obj, "GangGe","How","are you");

//也可以把参数作为数组传进来
echo $reflectionMethod -> invokeArgs($obj,array("GangGe","How","are you"));
getProperty

获得一个 ReflectionProperty 类实例 (同上,获得该属性的控制权) http://cn2.php.net/manual/zh/...

getValue获取属性值
public mixed ReflectionProperty::getValue ([ object $object ] )

如果该获得该实例的类属性不是一个static的属性,就必须传该类的实例

getProperty("staticProperty")->getValue()); //静态属性可以不加参数
var_dump($reflectionClass->getProperty("property")->getValue(new Foo)); //非静态属性必须加传一个类实例
$reflectionProperty = $reflectionClass->getProperty("privateProperty"); //受保护的属性就要通过setAccessible获得其权限
$reflectionProperty->setAccessible(true);
var_dump($reflectionProperty->getValue(new Foo));
Example 模拟YII框架中控制器调用方法的实现
hasMethod($action)) {
    exit("Method $action does not existed!");
}
 
// 取类的构造函数,返回的是ReflectionMethod对象
$constructor = $reflector->getConstructor();

// 取构造函数的参数,这是一个对象数组
$parameters = $constructor->getParameters();

// 遍历参数
foreach ($parameters as $key => $parameter) {
    // 获取参数声明的类
    $injector = new ReflectionClass($parameter->getClass()->name);
    // 实例化参数声明类并填入参数列表
    $parameters[$key] = $injector->newInstance(); //实例化$parameter->getClass()->name类
}
 
// 使用参数列表实例 controller 类
$instance = $reflector->newInstanceArgs($parameters);
// 执行
$instance->$action();
 
class HelloController
{
    private $model;
 
    public function __construct(TestModel $model)
    {
        $this->model = $model;
    }
 
    public function actionWorld()
    {
        echo $this->model->property, PHP_EOL;
    }
}
 
class TestModel
{
    public $property = "property";
}
TP框架中实现前后控制器
isPublic()) {

    $class = new ReflectionClass("BlogAction");

    // 执行前置方法
    if ($class->hasMethod("_before_detail")) {
        $beforeMethod = $class->getMethod("_before_detail");
        if ($beforeMethod->isPublic()) {
            $beforeMethod->invoke($instance);
        }
    }

    $method->invoke(new BlogAction);

    // 执行后置方法
    if ($class->hasMethod("_after_detail")) {
        $beforeMethod = $class->getMethod("_after_detail");
        if ($beforeMethod->isPublic()) {
            $beforeMethod->invoke($instance);
        }
    }
}

// 执行带参数的方法
$method = new ReflectionMethod("BlogAction", "test");
$params = $method->getParameters();
foreach ($params as $param) {
    $paramName = $param->getName();
    if (isset($_REQUEST[$paramName])) {
        $args[] = $_REQUEST[$paramName];
    } elseif ($param->isDefaultValueAvailable()) {
        $args[] = $param->getDefaultValue();
    }
}

if (count($args) == $method->getNumberOfParameters()) {
    $method->invokeArgs($instance, $args);
} else {
    echo "parameters is wrong!";
}
其他参考
/**
 * 执行App控制器
 */
public function execApp() {

    // 创建action控制器实例
    $className = MODULE_NAME . "Controller";
    $namespaceClassName = "apps" . APP_NAME . "controller" . $className;
    load_class($namespaceClassName, false);

    if (!class_exists($namespaceClassName)) {
        throw new Exception("Oops! Module not found : " . $namespaceClassName);
    }

    $controller = new $namespaceClassName();

    // 获取当前操作名
    $action = ACTION_NAME;

    // 执行当前操作
    //call_user_func(array(&$controller, $action)); // 其实吧,用这个函数足够啦!!!
    try {
        $methodInfo = new ReflectionMethod($namespaceClassName, $action);
        if ($methodInfo->isPublic() && !$methodInfo->isStatic()) {
            $methodInfo->invoke($controller);
        } else { // 操作方法不是public类型,抛出异常
            throw new ReflectionException();
        }
    } catch (ReflectionException $e) {
        // 方法调用发生异常后,引导到__call方法处理
        $methodInfo = new ReflectionMethod($namespaceClassName, "__call");
        $methodInfo->invokeArgs($controller, array($action, ""));
    }
    return;
}

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/31355.html

相关文章

  • modernPHP专题(3)】依赖注入与服务容器

    摘要:而依赖倒置原则的思想是,上层不应该依赖下层,应依赖接口。上面通过构造函数注入对象的方式,就是最简单的依赖注入当然注入不仅可以通过构造函数注入,也可以通过属性注入,上面你可以通过一个来动态为这个属性赋值。 依赖倒置和控制反转是一种编程思想,而依赖注入就是通过服务容器实现这种面向接口或者是面向抽象编程的思想 概念理解 依赖倒置原则 依赖倒置是一种软件设计思想,在传统软件中,上层代码依赖于下...

    terro 评论0 收藏0
  • modernPHP专题(13)】ZendOpcache字节码缓存

    摘要:概述字节码缓存不是的新特性,有很多独立的扩展可以实现,比如和等,但是截至目前这些扩展都没有集成到内核,从开始,内置了字节码缓存功能,名为。开始之前,我们先来看看什么是字节码缓存,以及字节码缓存的作用是什么。该函数将重置整个字节码缓存。 概述 字节码缓存不是PHP的新特性,有很多独立的扩展可以实现,比如APC、eAccelerator和Xache等,但是截至目前这些扩展都没有集成到PHP...

    KevinYan 评论0 收藏0
  • modernPHP专题(7)】生成器语法

    摘要:通过生成器来生成关联数组下面每一行是用分号分割的字段组合,第一个字段将被用作键名。正常来说,产生的是一个,它的成员变量与函数不存在别名引用关系。关键字在的版本,生成器允许从其他生成器,可迭代对象或数组通过关键字来生成对应的值输出 一般你在迭代一组数据的时候,需要创建一个数据,假设数组很大,则会消耗很大性能,甚至造成内存不足。 //Fatal error: Allowed memory ...

    jackwang 评论0 收藏0
  • modernPHP专题(6)】类的延迟绑定(后期绑定)

    摘要:实例化对象里面的所在哪个类就返回哪个类。访问的是当前类有点像因为其是继承了的方法,而调用的。由此可见,他们的区别只有在继承中才能体现出来,如果没有任何继承,那么这两者是没有区别的。但如果是在静态方法内本类的话,最好还是用 以static::来调用,是在运行的那个时刻才执行绑定操作; 父类中有个方法是延迟绑定的,在子类::调用这个方法的时候它又回到调用的子类开始向上找; exampl...

    adam1q84 评论0 收藏0
  • modernPHP专题(11)】断言ASSERT

    摘要:可以在任何时候启用和禁用断言验证,因此可以在测试时启用断言,而在部署时禁用断言。会检查指定的并在结果为时采取适当的行动视而定。中的断言向后兼用并增强之前的的方法。它使得在生产环境中启用断言为零成本,并且提供当断言失败时抛出特定异常的能力。 简述 编写代码时,我们总是会做出一些假设,断言就是用于在代码中捕捉这些假设,可以将断言看作是异常处理的一种高级形式。程序员断言在程序中的某个特定点该...

    WalkerXu 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<