面向对象编程中,对象被赋予了自省的能力,而这个自省的过程就是反射。
PHP5+
具有完整的反射API
,添加了对类、接口、函数、方法和扩展进行反向工程的能力。此外,反射API
提供了方法来取出函数、类和方法中的文档注释。
一、基础
反射指在PHP
运行状态中,扩展分析PHP
程序,导出或提取出关于类、方法、属性、参数等详细信息(包括注释),这种动态获取信息以及动态调用对象方法的功能称为反射API
。
分类
类 | 含义 |
---|---|
Reflection | \ |
ReflectionClass | 报告了一个类的有关信息 |
ReflectionClassConstant | 报告了一个类的常量信息 |
ReflectionZendExtension | 报告Zend扩展的相关信息 |
ReflectionExtension | 报告了一个扩展的有关信息 |
ReflectionFunction | 报告了一个函数的有关信息 |
ReflectionFunctionAbstract | ReflectionFunction 的父类 |
ReflectionMethod | 报告了一个方法的有关信息 |
ReflectionObject | 报告了一个对象的相关信息 |
ReflectionParameter | 取回了函数或方法参数的相关信息 |
ReflectionProperty | 报告了类的属性的相关信息 |
ReflectionType | 获取函数、类方法的参数或者返回值的类型 |
ReflectionGenerator | 获取生成器的信息 |
Reflector | Reflector 是一个接口,被所有可导出的反射类所实现 |
ReflectionException | \ |
二、例子
- ReflectionClass
1 | $m = new MongoClient(); |
- ReflectionExtension
1 | $ref = new ReflectionExtension('yac'); |
自定义
定义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
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;