0%

反射reflection

面向对象编程中,对象被赋予了自省的能力,而这个自省的过程就是反射。PHP5+具有完整的反射API,添加了对类、接口、函数、方法和扩展进行反向工程的能力。此外,反射API提供了方法来取出函数、类和方法中的文档注释。

一、基础

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

分类

含义
Reflection \
ReflectionClass 报告了一个类的有关信息
ReflectionClassConstant 报告了一个类的常量信息
ReflectionZendExtension 报告Zend扩展的相关信息
ReflectionExtension 报告了一个扩展的有关信息
ReflectionFunction 报告了一个函数的有关信息
ReflectionFunctionAbstract ReflectionFunction 的父类
ReflectionMethod 报告了一个方法的有关信息
ReflectionObject 报告了一个对象的相关信息
ReflectionParameter 取回了函数或方法参数的相关信息
ReflectionProperty 报告了类的属性的相关信息
ReflectionType 获取函数、类方法的参数或者返回值的类型
ReflectionGenerator 获取生成器的信息
Reflector Reflector 是一个接口,被所有可导出的反射类所实现
ReflectionException \

二、例子

  1. ReflectionClass
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
$m          = new MongoClient(); 
$db = $m->database;
$collection = $db->table;
$document = array(
"title" => date('Y-m-d H:i:s'),
"description" => date('Y-m-d H:i:s'),
"likes" => 100,
"url" => 'www.baidu.com'
);

//$collection->insert($document);

$ref = new ReflectionClass($m);
print_r($ref->getMethods());
echo "<hr/>";

$ref = new ReflectionClass($db);
print_r($ref->getMethods());
echo "<hr/>";

$ref = new ReflectionClass($collection);
print_r($ref->getMethods());
  1. ReflectionExtension
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ref = new ReflectionExtension('yac');
$ref = new ReflectionExtension('redis');
$ref = new ReflectionExtension('standard');
print_r($ref->getConstants());
print_r($ref->getClasses());
print_r($ref->getClassNames());
print_r($ref->getDependencies());
print_r(sizeof($ref->getFunctions()));
print_r($ref->getINIEntries());
print_r($ref->getName());
print_r($ref->getINIEntries());
print_r($ref->getVersion());
print_r($ref->info());
print_r($ref->isPersistent());
print_r($ref->isTemporary());
  1. 自定义

    • 定义User类

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70
      71
      72
      73
      74
      75
      /**
      * User
      * @author someone@163.com
      * @desc this is a test class
      */
      class User {
      public $username;
      protected $age;
      private $password;
      static $phone = '18888888888';

      public function __construct($username, $age, $password='123') {
      $this->username = $username;
      $this->age = $age;
      $this->password = $password;
      }

      /**
      * 获取用户名
      * @return string
      */
      public function getUsername(){
      return $this->username;
      }

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

      /**
      * 获取年龄
      * @return int
      */
      protected function getAge()
      {
      return $this->age;
      }

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

      /**
      * 获取密码
      * @return string
      */
      private function getPassword(){
      return $this->password;
      }

      /**
      * 设置密码
      * @param string $password
      */
      private function setPassowrd($password)
      {
      $this->password = $password;
      }

      /**
      * 设置手机号
      * @param string $phone
      */
      public function setPhone($phone){
      self::$phone = $phone;
      }
      }
    • 正常实例化类

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      $user = new User('tom', 10, '123456');

      echo $user->username,PHP_EOL;
      // echo $user->age,PHP_EOL;
      // echo $user->password,PHP_EOL;

      echo $user->getUsername(), PHP_EOL;
      // echo $user->getAge(), PHP_EOL;
      // echo $user->getPassword();

      $user->setUsername('jack');
      // $user->setAge(20);
      // $user->setPassowrd('abcdef');

      echo $user->getUsername(), PHP_EOL;
      // echo $user->getAge(), PHP_EOL;
      // echo $user->get

      print_r(get_object_vars($boy));
      print_r(get_class_vars(get_class($boy)));
      print_r(get_class_methods(get_class($boy)));
    • 反射调用

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70
      71
      72
      73
      74
      75
      76
      77
      78
      79
      80
      81
      82
      83
      84
      85
      86
      87
      // $refClass = new ReflectionClass(new User('tom', 10, '123456'));
      $refClass = new ReflectionClass('User');

      // 取文件信息
      // echo 'path: '. dirname($refClass->getFileName()) . PHP_EOL;
      // echo 'name: '. basename($refClass->getFileName()) . PHP_EOL;

      // 取所有方法
      // $methods = $refClass->getMethods();
      // foreach($methods as $method){
      // $name = $method->getName();
      // print_r($refClass->getMethod($name));
      // }


      // 取构造方法机器参数
      // $constructor = $refClass->getConstructor();
      // echo $constructor;
      // print_r($constructor);

      // $parameters = $constructor->getParameters();
      // var_export($parameters);
      // var_dump($parameters);
      // print_r($parameters);
      // $nl = "\n";
      // echo "$nl\tParameters$nl";
      // foreach($parameters as $param) {
      // echo "****** $" . $param->name . " ******$nl";
      // echo "Nullable:\t\t" . $param->allowsNull() . $nl ."Default Value:\t\t";
      // echo ($param->isDefaultValueAvailable()) ? $param->getDefaultValue() : "None";
      // echo $nl ."Is Array:\t\t";
      // echo ($param->isArray()) ? "Yes" : "No";
      // echo $nl . "Optional:\t\t";
      // echo ($param->isOptional()) ? "Yes" : "No";
      // echo $nl;
      // }

      // 实例化
      // $instance1 = $refClass->newInstance('rose', 12, 'rose');
      // $params = ['jim', 13, 'jim'];
      // $instance2 = $refClass->newInstanceArgs($params);
      // print_r($instance1);
      // print_r($instance2);

      // 取protected、private属性
      // print_r($refClass->getProperties());
      // print_r($refClass->getProperty('age'));
      // print_r($refClass->getProperty('password'));

      // 取Doc
      // print_r($refClass->getDocComment());
      // print_r($refClass->getMethod('setAge')->getDocComment());

      // 访问调用公有方法
      // $instance = $refClass->newInstance('jimi', 13, 'jimi');
      // 方式一
      // $instance->setUsername('admin_1');
      // $username = $instance->getUsername();
      // echo $username,PHP_EOL;
      // 方式二
      // $refClass->getProperty('username')->setValue($instance, 'admin_2');
      // $username = $refClass->getProperty('username')->getValue($instance);
      // echo $username,PHP_EOL;
      // 方式三
      // $refClass->getMethod('setUsername')->invoke($instance, 'admin_3');
      // $value = $refClass->getMethod('getUsername')->invoke($instance);
      // echo $value,PHP_EOL;

      // 访问调用非公有方法
      $instance = $refClass->newInstance('jimi', 13, 'jimi');
      try {
      // 正确写法
      $property = $refClass->getProperty('password');
      $property->setAccessible(true);
      $property->setValue($instance, 'root');
      $value = $property->getValue($instance);
      echo $value,PHP_EOL;

      // 错误写法
      // $refClass->getProperty('password')->setAccessible(true);
      // $refClass->getProperty('password')->setValue($instance, 'password');
      // $refClass = $refClass->getProperty('password')->getValue($instance);
      // $value = $instance->password;
      // echo $value,PHP_EOL;
      } catch (Exception $e){
      echo $e;
      }

      小结:

      • 不管反射类中定义的属性、方法是否为public都可以获取到
      • 直接访问protected、private的属性、方法会抛出异常
      • 访问非公有成员需要调用指定的ReflectionProperty或ReflectionMethod对象的setAccessible(true)方法
  1. 实际应用:获取对象私有属性

    • 反射

      1
      2
      3
      4
      5
      6
      $class = new ReflectionClass('User');
      $property = $class->getProperty("password");
      $property->setAccessible(true);
      $user = new User("tom", 1, '12345');
      echo $property->getValue($user),PHP_EOL;// 只能通过ReflectionProperty实例的getValue方法访问
      echo $user->password;
    • 强制类型转换

      1
      2
      3
      4
      5
      $user = new User("tom", 1, '12345');
      $attrs = (array)$user;
      // var_export($attrs);
      $key = "\0" . User::class . "\0" . "password";// 拼接key,注意"\0"不能改成单引号
      echo $attrs[$key], PHP_EOL;
    • 使用闭包

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      $user = new User("tom", 1, '12345');
      // 5.3+闭包(匿名函数)
      $closure = function() {
      return $this->password;
      };
      // 5.4+bindTo
      $method = $closure->bindTo($user, User::class);
      echo $method(), PHP_EOL;
      // 7.0+call
      echo $closure->call($user), PHP_EOL;

三、参考

  1. 参考一
  2. 参考二
  3. 参考三
  4. 参考四