PHP反射机制
简介
-
PHP自5.0版本以后添加了反射机制,它提供了一套强大的反射API,允许你在PHP运行环境中,访问和使用类、方法、属性、参数和注释等,其功能十分强大,经常用于高扩展的PHP框架,自动加载插件,自动生成文档,甚至可以用来扩展PHP语言。
-
PHP反射api由若干类组成,可帮助我们用来访问程序的元数据或者同相关的注释交互。借助反射我们可以获取诸如类实现了那些方法,创建一个类的实例(不同于用new创建),调用一个方法(也不同于常规调用),传递参数,动态调用类的静态方法。
-
反射api是PHP内建的OOP技术扩展,为语言本身自带的特性,不需要额外添加扩展或者配置就可以使用;包括一些类,异常和接口,综合使用他们可用来帮助我们分析其它类,接口,方法,属性,方法和扩展。这些OOP扩展被称为反射。
反射类型
PHP反射API会基于类,方法,属性,参数等维护相应的反射类,已提供相应的调用API。
类型 说明
Reflector Reflector 是一个接口,被所有可导出的反射类所实现(implement)
Reflection 反射(reflection)类
ReflectionClass 报告了一个类的有关信息
ReflectionZendExtension 报告Zend扩展的相关信息
ReflectionExtension 报告了PHP扩展的有关信息
ReflectionFunction 报告了一个函数的有关信息
ReflectionFunctionAbstract ReflectionFunction 的父类
ReflectionMethod 报告了一个方法的有关信息
ReflectionObject 报告了一个对象(object)的相关信息
ReflectionParameter 取回了函数或方法参数的相关信息
ReflectionProperty 报告了类的属性的相关信息
示例
<?php
class Person {
/**
* For the sake of demonstration, we"re setting this private
*/
private $_allowDynamicAttributes = false;
/**
* type=primary_autoincrement
*/
protected $id = 0;
/**
* type=varchar length=255 null
*/
protected $name;
/**
* type=text null
*/
protected $biography;
public function getId() {
return $this->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;
}
}
ReflectionClass类
通过ReflectionClass,我们可以得到Person类的以下信息:
1.常量 Contants
2.属性 Property Names
3.方法 Method Names静态
4.属性 Static Properties
5.命名空间 Namespace
6.Person类是否为final或者abstract
7.Person类是否有某个方法
接下来反射它,只要把类名"Person"传递给ReflectionClass就可以了:
$class = new ReflectionClass('Person'); // 建立 Person这个类的反射类
$instance = $class->newInstanceArgs($args); // 相当于实例化Person 类
- 获取属性
$properties = $class->getProperties();
foreach ($properties as $property) {
echo $property->getName() . "\n";
}
// 输出:
// _allowDynamicAttributes
// id
// name
// biography
默认情况下,ReflectionClass会获取到所有的属性,private 和 protected的也可以。如果只想获取到private属性,就要额外传个参数:
$private_properties = $class->getProperties(ReflectionProperty::IS_PRIVATE);
可用参数列表:
ReflectionProperty::IS_STATIC
ReflectionProperty::IS_PUBLIC
ReflectionProperty::IS_PROTECTED
ReflectionProperty::IS_PRIVATE
- 获取注释
通过getDocComment可以得到写给property的注释:
foreach ($properties as $property) {
if ($property->isProtected()) {
$docblock = $property->getDocComment();
preg_match('/ type\=([a-z_]*) /', $property->getDocComment(), $matches);
echo $matches[1] . "\n";
}
}
// Output:
// primary_autoincrement
// varchar
// text
- 获取类的方法
getMethods() 来获取到类的所有methods。
hasMethod(string) 是否存在某个方法
getMethod(string) 获取方法
- 执行类的方法
$instance->getName(); // 执行Person 里的方法getName
// 或者:
$method = $class->getmethod('getName'); // 获取Person 类中的getName方法
$method->invoke($instance); // 执行getName 方法
// 或者:
$method = $class->getmethod('setName'); // 获取Person 类中的setName方法
$method->invokeArgs($instance, array('snsgou.com'));
ReflectionMethod类
通过ReflectionMethod,我们可以得到Person类的某个方法的信息:
1.是否“public”、“protected”、“private” 、“static”类型
2.方法的参数列表
3.方法的参数个数
4.反调用类的方法
// 执行detail方法
$method = new ReflectionMethod('Person', 'test');
if ($method->isPublic() && !$method->isStatic()) {
echo 'Action is right';
}
echo $method->getNumberOfParameters(); // 参数个数
echo $method->getParameters(); // 参数对象数组
补充
$class = new ReflectionClass('Extend\User'); // 将类名User作为参数,即可建立User类的反射类
$properties = $class->getProperties(); // 获取User类的所有属性,返回ReflectionProperty的数组
$property = $class->getProperty('password'); // 获取User类的password属性ReflectionProperty
$methods = $class->getMethods(); // 获取User类的所有方法,返回ReflectionMethod数组
$method = $class->getMethod('getUsername'); // 获取User类的getUsername方法的ReflectionMethod
$constants = $class->getConstants(); // 获取所有常量,返回常量定义数组
$constant = $class->getConstant('ROLE'); // 获取ROLE常量
$namespace = $class->getNamespaceName(); // 获取类的命名空间
$comment_class = $class->getDocComment(); // 获取User类的注释文档,即定义在类之前的注释
$comment_method = $class->getMethod('getUsername')->getDocComment(); // 获取User类中getUsername方法的注释文档
注意:
创建反射类时传送的类名,必须包含完整的命名空间,即使使用了 use 关键字。否则找不到类名会抛出异常。
一旦创建了反射类的实例,我们不仅可以通过反射类访问原来类的方法和属性,还能创建原来类的实例或则直接调用类里面的方法。