1、废话稍作唠叨
反射类则是拆封类中的所有方法、成员变量,并包括私有方法等。就如“解刨”一样,所有五腹六脏一并呈现出来
- 所有的反射API类的官方文档地址:https://www.php.net/reflection/
2、常用API类
- 常用的反射类
类名 | 作用 |
---|---|
Reflection | 可以打印类的基本信息,(通过提供的静态export()函数) |
ReflectionMethod | 见名知意,打印类方法、得到方法的具体信息等 |
ReflectionClass | 用于得到类信息,比如得到类包含的方法,类本的属性,是不是抽象类等 |
ReflectionParameter | 显示参数的信息,可以动态得到已知方法的传参情况 |
ReflectionException | 用于显示错误信息 |
ReflectionExtension | 得到PHP的扩展信息,可以判断扩展是否存在等 |
3、简单使用demo
- 创建一个Man类并实例化
class Man {
public $name;
private $age;
public function __construct($name, $age)
{
$this->name = $name;
$this->age = $age;
}
/**
* 获取名
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* 设置名
* @param string $username
*/
public function setName($name)
{
$this->name = $name;
}
/**
* 获取年龄
* @return string
*/
private function getAge()
{
return $this->password;
}
/**
* 设置年龄
* @param int $age
*/
private function setAge($age)
{
$this->age = $age;
}
}
$personObj = new Man('xiaoming', 34);
- 反射属性
$refClass = new ReflectionClass($personObj);
或者
$refClass = new ReflectionClass('Man'); // 将类名作为参数,建立反射类
$properties = $refClass->getProperties(); //反射所有属性
打印结果:
Array
(
[0] => ReflectionProperty Object
(
[name] => name
[class] => Man
)
[1] => ReflectionProperty Object
(
[name] => age
[class] => Man
)
)
$properties = $refClass->getProperty('age'); // 获取Man类的age属性
打印结果:
ReflectionProperty Object
(
[name] => age
[class] => Man
)
- 反射方法
$methods = $refClass->getMethods(); // 获取Man类的所有方法
打印结果:
Array
(
[0] => ReflectionMethod Object
(
[name] => __construct
[class] => Man
)
[1] => ReflectionMethod Object
(
[name] => getName
[class] => Man
)
[2] => ReflectionMethod Object
(
[name] => setName
[class] => Man
)
[3] => ReflectionMethod Object
(
[name] => getAge
[class] => Man
)
[4] => ReflectionMethod Object
(
[name] => setAge
[class] => Man
)
)
$method = $refClass->getMethod('getName'); // 获取Man类的getName方法
打印结果:
ReflectionMethod Object
(
[name] => getName
[class] => Man
)
- 反射注释
$classDocComment = $refClass->getDocComment(); // 获取Man类定义在类之前的注释
打印结果:
/** * Man类 */
$methodDocComment = $refClass->getMethod('setAge')->getDocComment(); // 获取Man类中setAge方法的注释
打印结果:
/** * 设置年龄 * @param int $age */
- 反射实例化新类
$newInstance = $refClass->newInstance('xiaoPeng', 23); // 从指定的参数创建一个新的类实例
打印结果:
Man Object ( [name] => xiaoPeng [age:Man:private] => 23 )
$params = ['xiaoPeng2', 44];
$newInstance = $refClass->newInstanceArgs($params); // 从给出的参数创建一个新的类实例
打印结果:
Man Object ( [name] => xiaoPeng2 [age:Man:private] => 44 )
- 反射实例化新类可访问、执行类的公有方法——public
方法一:
$newInstance->setName('xiaoPeng3'); // 调用Man类的实例调用setName方法设置名
$name = $newInstance->getName(); // 调用Man类的实例调用getName方法获取名
打印结果:xiaoPeng3
方法二:
$refClass->getProperty('name')->setValue($newInstance, 'xiaopeng3'); // 通过反射类ReflectionProperty设置指定实例的name属性值
$name = $refClass->getProperty('name')->getValue($newInstance); // 通过反射类ReflectionProperty获取name的属性值
打印结果:xiaopeng3
方法三:
$refClass->getMethod('setName')->invoke($newInstance, 'xiaopeng5'); // 通过反射类ReflectionMethod调用指定实例的方法,并且传送参数
$value = $refClass->getMethod('getName')->invoke($newInstance); // 通过反射类ReflectionMethod调用指定实例的方法
打印结果:
xiaopeng5
- 反射实例化新类可访问、执行类的非公有方法——private、protecte
$property = $refClass->getProperty('age');
$property->setAccessible(true); // 修改 $property 对象的可访问性
$property->setValue($newInstance, 23); // 可以执行
$value = $property->getValue($newInstance); // 可以执行
echo $value;
打印结果:
23
注意:直接访问 protected 或则 private 的属性、方法,会抛出异常;必须先setAccessible(true)
3、使用场景
1、编写单元测试大量使用了反射
2、PHP各MVC大框架都有使用
- 小demo
class Mysql
{
function connect($db)
{
echo "连接到数据库{$db[0]}".PHP_EOL;
}
}
//sql动态代理实例化类
class SqlProxy
{
private $instance=[];
public function __construct($obj)
{
$this->instance[] = new $obj;
}
public function __call($name,$args)
{
foreach($this->instance as $obj)
{
$r = new ReflectionClass($obj);
if($method = $r->getMethod($name))
{
if($method->isPublic() && !$method->isAbstract())
{
$method->invoke($obj,$args);
}
}
}
}
}
$obj = new SqlProxy('Mysql');
$obj->connect('ok');
打印结果:
连接到数据库ok
稍作总结:实际上 SqlProxy 类起到了动态代理的那么一个过程,不用直接new Mysql 直接传入要new 的类名字符串,在代理类中通过反射去帮你实例化目标类
4、稍作总结
毕竟反射类可以暴漏类的一切,故此还是比较危险的,所有用起来是方便,但是有利有弊,自己权衡使用。一般在业务应用中很少用,一般都出现在框架等中
5、亮哥多嘴
由于菜鸟记性稍差,一直秉性好记性不如烂键盘,把自己的感悟记录下来,如果有不妥之处,欢迎各位大神指正,文中借鉴了很多互联网上的作品思想,如有雷同,并不巧合,但是文中的一行行代码都是本菜鸟码出来的,阿弥陀佛