反射介绍
PHP 反射机制,对类、接口、函数、方法和扩展进行反向工程的能力。
分析类,接口,函数和方法的内部结构,方法和函数的参数,以及类的属性和方法。
反射中常用的几个类:
- ReflectionClass 解析类
- ReflectionProperty 类的属性的相关信息
- ReflectionMethod 类方法的有关信息
- ReflectionParameter 取回了函数或方法参数的相关信息
- ReflectionFunction 一个函数的相关信息
分析类:
|
class Student
{
public $id;
public $name;
const MAX_AGE = 200;
public static $likes = [];
public function __construct($id, $name = 'li')
{
$this->id = $id;
$this->name = $name;
}
public function study()
{
echo 'learning...';
}
private function _foo()
{
echo 'foo';
}
protected function bar($to, $from = 'zh')
{
echo 'bar';
}
}
|
ReflectionClass
|
$ref = new ReflectionClass('Student');
if ($ref->isInstantiable()) {
echo '可实例化';
}
$constructor = $ref->getConstructor();
print_r($constructor);
if ($ref->hasProperty('id')) {
$attr = $ref->getProperty('id');
print_r($attr);
}
$attributes = $ref->getProperties();
foreach ($attributes as $row) {
echo $row->getName() , "\n";
}
$static = $ref->getStaticProperties();
print_r($static);
if ($ref->hasConstant('MAX_AGE')) {
$const = $ref->getConstant('MAX_AGE');
echo $const;
}
$constants = $ref->getConstants();
print_r($constants);
if ($ref->hasMethod('bar')) {
$method = $ref->getMethod('bar');
print_r($method);
}
$methods = $ref->getMethods();
foreach ($methods as $key => $value) {
echo $value->getName() . "\n";
}
|
ReflectionProperty
|
if ($ref->hasProperty('name')) {
$attr = $ref->getProperty('name');
echo $attr->getName();
var_dump($attr->isDefault());
var_dump($attr->isPrivate());
var_dump($attr->isProtected());
var_dump($attr->isPublic());
var_dump($attr->isStatic());
}
|
RefleactionMethod & ReflectionParameter
|
if ($ref->hasMethod('bar')) {
$method = $ref->getMethod('bar');
echo $method->getName();
$parameters = $method->getParameters();
foreach ($parameters as $row) {
echo $row->getName();
echo $row->getClass();
if ($row->isDefaultValueAvailable()) {
echo $row->getDefaultValue();
}
if ($row->hasType()) {
echo $row->getType();
}
}
}
|
ReflectionFunction & ReflectionParameter
|
$fun = new ReflectionFunction('demo');
echo $fun->getName();
$parameters = $fun->getParameters();
foreach ($parameters as $row) {
echo $row->getName();
echo $row->getClass();
if ($row->isDefaultValueAvailable()) {
echo $row->getDefaultValue();
}
if ($row->hasType()) {
echo $row->getType();
}
}
|
综合实例
下面用一个简单的示例:如果用反射实例化类。
file: Student.php
|
class Student
{
public $id;
public $name;
public function __construct($id, $name)
{
$this->id = $id;
$this->name = $name;
}
public function study()
{
echo 'learning.....';
}
}
|
一般情况下,实例化类的时候,直接使用 new
,但是我们现在不用这种方法,我们使用反射来实现。
file: index.php
|
require 'student.php';
function make($class, $vars = [])
{
$ref = new ReflectionClass($class);
if ($ref->isInstantiable()) {
$constructor = $ref->getConstructor();
if (is_null($constructor)) {
return new $class;
}
$params = $constructor->getParameters();
$resolveParams = [];
foreach ($params as $key => $value) {
$name = $value->getName();
if (isset($vars[$name])) {
$resolveParams[] = $vars[$name];
} else {
$default = $value->isDefaultValueAvailable() ? $value->getDefaultValue() : null;
if (is_null($default)) {
if ($value->getClass()) {
$resolveParams[] = make($value->getClass()->getName(), $vars);
} else {
throw new Exception("{$name} 没有传值且没有默认值");
}
} else {
$resolveParams[] = $default;
}
}
}
return $ref->newInstanceArgs($resolveParams);
} else {
throw new Exception("类 {$class} 不存在!");
}
}
try {
$stu = make('Student', ['id' => 1]);
print_r($stu);
$stu->study();
} catch (Exception $e) {
echo $e->getMessage();
}
try {
$stu = make('Student', ['id' => 1, 'name' => 'li']);
print_r($stu);
$stu->study();
} catch (Exception $e) {
echo $e->getMessage();
}
|
上面两种情况很明显第一种,缺少参数 name
,无法实例化成功,第二种情况就可以实例化成功。
那么我们如果将类 Student
的构造函数修改为:
|
public function __construct($id, $name = 'zhang')
{
$this->id = $id;
$this->name = $name;
}
|
这样设置 name
有默认值的情况下,那么第一种情况也可以实例化成功。
情况三
第三种情况:如果在类的构造函数中有其他类为参数的情况下,那么也可以解析:
|
public function __construct($id, $name, Study $study)
{
$this->id = $id;
$this->name = $name;
$this->study = $study;
}
|
那么这种情况下,在分析类的构造函数参数的时候,如果没有传递参数的话,就会递归调用 make
方法处理 Study
类,如果类存在的话,实例化。
file: study.php
class Study
{
public function show()
{
echo 'show';
}
}
|
将 Student
类的方法 study
修改为:
1
2
3
4
|
public function study()
{
$this->name . ' ' . $this->study->show();
}
|
下面测试:
|
try {
$stu = make('Student', ['id' => 1]);
print_r($stu);
$stu->study();
} catch (Exception $e) {
echo $e->getMessage();
}
|
PHP 的反射是一个很用的功能,我这里只能很简单的讲解了一点皮毛,详细介绍和用法可参看 官方手册。