反射是一种对象编程中赋予对象的一种自省能力。通过反射api,我们可以可以找到对象所属类的属性、方法以及它们的访问权限,他是一种动态获取信息以及动态调用对象方法的功能。
在这里,我们假设已经定义了一个person类,而student是这个类的实例化对象,现在要获取这个student对象的方法和属性列表。
(1)通过反射
//获取对象属性列表
$reflect = new ReflectionObject($student);
$props = $reflect->getProperties();
foreach($props as $prop) {
print $prop->getName()."\n";
}
//获取对象方法列表
$m = $reflect->getMethods();
foreach ($m as $prop) {
print $prop->getName()."\n";
}
(2)使用class函数,返回对象属性的关联数组以及更多的信息
//返回对象属性的关联数组
var_dump(get_object_vars($student));
//类属性
var_dump(get_class_vars(get_class($student)));
//返回由类组成的方法数组
var_dump(get_class_methods(get_class($student)));
*get_class()方法可以知道页面传递对象的所属类。
反射Api的功能更加强大,甚至可以还原这个类的原型,包括方法的访问权限。
//反射获取类的原型
$obj = new ReflectionObject($student);
$className = $obj->getName();
$methods = $properties = array();
foreach ($obj->getProperties() as $v) {
$properties[$v->getName()] = $v;
}
foreach ($obj->getMethods() as $v) {
$methods[$v->getName()] = $v;
}
echo "class {$classname}\n{\n";
is_array($properties)&&ksort($properties);
foreach($properties as $k => $v)
{
echo "\t";
echo $v->isPublic()?'public':'',$v->isPrivate()?'private':, $v->isProtected()?'protected':'',$v->isStatic()?'static':'';
echo "\t{$k}\n";
}
echo "\n";
if(is_array($methods)) ksort($methods);
foreach ($methods as $k => $v) {
echo "\tfunction{$k}(){}\n";
}
echo "\n";
反射的作用:使用反射原理实现动态代理
class Mysql
{
public function connect($db)
{
echo "连接到数据库${db[0]}\r\n";
}
}
class SqlProxy
{
private $target;
function __construct ($tar) {
$this->target = new $tar();
}
function __call ($name,$args) {
foreach ($this->target as $obj) {
$r = new ReflectionObject($obj);
if($method = $r->getMethod($name)) {
if($method->isPublic()&&!$method->isAbstract()) {
echo "方法前拦截记录LOG\r\n";
$method->invoke($obj,$args);
echo "方法后拦截\r\n";
}
}
}
}
}
$obj = new SqlProxy('Mysql');
$obj->connect('member');
在上面的代码中,真正的操作类是mysql类,但是sqlporxy类实现了根据动态传入参数,代替实际的类运行,并且在方法运行前后进行拦截,并且动态的改变类中的属性和方法,这就是简单的动态代理。
很多时候,善用反射能够实现代码的优雅和简洁,但是反射会破坏类的封装性,因为反射使本不应该暴露的属性或方法被强制暴露,因此,它在实际开发中不常用。