PHP的反射机制提供了一套反射API,用来访问和使用类、方法、属性、参数和注释等。
比如可以通过一个对象知道这个对象所属的类,这个类包含哪些方法,这些方法需要传入什么参数,每个参数是什么类型等等。
不用创建类的实例也可以访问类的成员和方法,就算类成员定义为 private也可以在外部访问。
官方文档 提供了诸如 ReflectionClass、ReflectionMethod、ReflectionObject、ReflectionExtension 等反射类及相应的API,用得最多的是 ReflectionClass。
ReflectionClass 反射类
通过 ReflectionClass 反射一个类,参数是类名或者类实例。
通过构造方法实例化类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
|
class Test1 { private $id; public $name;
public function __construct(int $id,string $name) { $this->id = $id; $this->name = $name; } public function getId() { return $this->id; } public function getName() { return $this->name; }
}
|
Test1 类拥有一个 private 类型的 id 和 一个 public 类型的 name 。
还有一个构造方法和两个普通方法。
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
| function getReflectObject($class) { try { return new ReflectionClass($class); } catch (ReflectionException $e) { throw new ClassNotFoundException('class not exists: ' . $class, $class, $e); } }
function invokeClass_1(string $class,array $args) { $reflect = getReflectObject($class); $object = $reflect->newInstanceArgs($args);
$props = $reflect->getProperties(); foreach ($props as $prop) { echo $prop->getName() . "=".$prop->getValue($object)."\n"; } $name = $object->getName();
return $name; }
$res = invokeClass_1('Test1',[1,'Jerry']); var_dump($res);
|
输出
1 2 3
| id=1 name=Jerry string(5) "Jerry"
|
通过单例模式实例化类
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
|
class Test2 { private $id; public $name; private static $_instance;
private function __construct(int $id,string $name) { $this->id = $id; $this->name = $name; }
public static function getInstance(int $id,string $name) { if(empty(self::$_instance)) self::$_instance = new static($id,$name); return self::$_instance; }
public function getId() { return $this->id; }
public function getName() { return $this->name; }
public static function sayHi($id,$name) { return "hello {$name}, your id={$id}."; }
public static function index() { return "hello index"; }
}
|
Test2 类的构造方法是 private 的,因此外部需要通过静态方法 getInstance 来获得类的实例。
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
| function getReflectObject($class) { try { return new ReflectionClass($class); } catch (ReflectionException $e) { throw new ClassNotFoundException('class not exists: ' . $class, $class, $e); } }
function invokeClass_2(string $class,array $args) { $reflect = getReflectObject($class); $name = null; if ($reflect->hasMethod('getInstance')) { $method = $reflect->getMethod('getInstance'); if ($method->isPublic() && $method->isStatic()) { $object = $method->invokeArgs(null, $args); $name = $object->getName(); } } return $name; }
$res = invokeClass_2('Test2',[2,'Sam']); var_dump($res);
|
输出
ReflectionMethod 反射类
ReflectionMethod 类也具有反射一个类的作用。区别是参数需要传递类名和方法名。
调用有参数的方法
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
| function getReflectMethodObject($class,$name) { try { return new ReflectionMethod($class,$name); } catch (ReflectionException $e) { throw new ClassNotFoundException('class or method not exists: ' . $class, $class, $e); } }
function invokeMethod_1(string $class,string $name,array $args) { $method = getReflectMethodObject($class,$name); $num = $method->getNumberOfParameters(); if($num > 0) $res = $method->invokeArgs(null,$args); else $res = $method->invoke(null); return $res; } $res = invokeMethod_1('Test2','sayHi',[2,'Sam']); var_dump($res);
|
输出
1
| string(21) "hello Sam, your id=2."
|
以上功能作用与
1
| $res = call_user_func_array([__NAMESPACE__.'Test2','sayHi'],[2,'Sam']);
|
相似。
调用无参数的方法
1 2
| $res = invokeMethod_1('Test2','index',[]); var_dump($res);
|
输出
1
| string(11) "hello index"
|
以上功能作用与
1
| $res = call_user_func([__NAMESPACE__.'Test2','index']);
|
相似。
ReflectionFunction 反射类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| function processUserData($name, $age, $job = "", $hobbie = "") { $msg = "Hello $name. You have $age years old"; if (!empty($job)) { $msg .= ". Your job is $job"; }
if (!empty($hobbie)) { $msg .= ". Your hobbie is $hobbie"; }
return $msg . "."; }
$refFunction = new ReflectionFunction('processUserData'); $valuesToProcess = [ 'name' => 'Anderson Lucas Silva de Oliveira', 'age' => 21, 'hobbie' => 'Play games' ]; $res = $refFunction->invoke(...$valuesToProcess); var_dump($res);
|
输出
1
| string(89) "Hello Anderson Lucas Silva de Oliveira. You have 21 years old. Your hobbie is Play games."
|
一个demo
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
| function invoke($class,$function,$args) { if(function_exists($function)) { $refFunction = new ReflectionFunction($function); $res = $refFunction->invoke(...$args); }else{ $reflect = getReflectObject($class); $constructor = $reflect->getConstructor(); if($constructor->isPublic()) { $object = $reflect->newInstanceArgs($args); }else if($reflect->hasMethod('getInstance')){ $method = $reflect->getMethod('getInstance'); if ($method->isPublic() && $method->isStatic()) $object = $method->invokeArgs(null, $args); } $res = $object->$function(); } return $res; }
$valuesToProcess = [ 'name' => 'Anderson Lucas Silva de Oliveira', 'age' => 21, 'hobbie' => 'Play games' ];
$res = invoke(null,'processUserData',$valuesToProcess);
var_dump($res);
|
invoke 函数接收3个参数,分别是:类名,方法名/函数名,传参数组。
invoke 函数会首先判断是否存在函数,存在的话则直接传递参数,执行函数;
1
| $res = invoke(null,'processUserData',$valuesToProcess);
|
若不存在对应的函数,则通过反射类反射指定的类。
首先判断构造函数的权限是否为 public ,是的话则通过构造函数实例化类,最后再调用方法;
1
| $res = invoke('Test1','getName',[2,'Sam']);
|
若构造函数的权限不为 public ,则通过判断是否存在 getInstance 方法,并且方法权限为 public 和 static , getInstance 方法体使用单例模式返回类的实例。调用 getInstance 方法得到类的实例,最后调用方法。
1
| $res = invoke('Test2','getName',[1,'Jerry']);
|