PHP反射
反射就是让你拥有剖析类、函数的能力
反射并不会对你实现业务有任何影响
但是你如果想写出结构优雅的程序,想写出维护性和扩展性都很高的程序
学习反射是 必不可少 的。
目录
PHP内置了一组反射类来实现类、方法以及参数的解析
常用的有:
- ReflectionClass 解析类
- ReflectionProperty 类的属性的相关信息
- ReflectionMethod 类方法的有关信息
- ReflectionParameter 取回了函数或方法参数的相关信息
这些类大多数都继承于 ReflectionFunctionAbstract
类,该类常用的方法有:
getFileName()
:获取文件名称getName()
: 获取函数名称getNamespaceName()
:获取命名空间getNumberOfParameters()
:获取参数数目getNumberOfRequiredParameters()
: 获取必须输入的参数个数getParameters()
: 获取参数,返回一个 ReflectionParameter 类,参数为空时返回空数组getStaticVariables()
:获取静态变量inNamespace()
:检查是否处于命名空间isUserDefined()
: 检查是否是用户定义
详情参考 : http://php.net/manual/zh/class.reflectionfunctionabstract.php
ReflectionClass
传入实例化的类或者类名都可以。
常用方法:
- is 开头的函数用于判断
isInstantiable()
:判断是否是类的实例,也就是是否可以实例化
- get 开头的函数获取类相关信息
getConstructor()
: 或许构造函数相关信息,返回 ReflectionMethod 对象,没有则返回空getProerty($name)
: 获取某一个属性值,返回 ReflectionProperty 对象【可用has先判断是否含有该属性值】getProperties()
:获取属性列表,返回一组 ReflectionProperty 对象getMethod($method_name)
:获取某一个方法,返回 ReflectionMethod 对象getMethods()
:获取方法列表,返回一组 ReflectionMethod 对象
- new 创建一个新的实例
newInstanceArgs()
: 从给定的参数实例化一个类
ReflectionParameter
常用方法:
isDefaultValueAvailable()
:该参数是否拥有默认值getDefaultValue()
: 获取参数的默认值,参数没有默认值时该方法会抛出异常getClass()
: 获得参数类型提示
详情参考 : http://php.net/manual/zh/class.reflectionparameter.php
ReflectionMethod
传入类名和方法名
常用方法:
详情参考 :http://php.net/manual/zh/class.reflectionmethod.php
实例化类
传入类名和初始化参数,使用相关的反射类实现类的实例化,包括参数依赖注入。
示例:
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
| class People { protected $name; protected $phone; public function __construct(Phone $phone, $name = '小明') { $this->phone = $phone; $this->name = $name; } public function has() { echo $this->name . '拥有' . $phone->name; } }
class Phone { protected $name; public function __construct($phoneName = 'iphoneX') { $this->name = $phoneName; } }
$phone = new Phone('小米手机'); $people = new People($phone); $people->has();
|
使用反射类相关知识封装方法 make()
用来实例化类,
1 2 3
| $phone = new Phone('小米手机'); $people = new People($phone); $people->has();
|
则上面实例化类的代码只需要一行代码就可以。
1
| make('People', ['phoneName' => '小米手机','name' => '爱学习的小明']);
|
make() 方法代码如下:
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
|
function make($class, $vars = []) { if (!class_exists($class)) { echo '类不存在'; exit; } $ref = new ReflectionClass($class); if (!$ref->isInstantiable()) { echo $class . '不可以实例化'; exit; } $construct = $ref->getConstructor(); if (is_null($construct)) { return new $class; } $parmeters = $construct->getParameters(); $resolveParams = is_null($parmeters) ? [] : injectionParameter($parmeters, $vars); return $ref->newInstanceArgs($resolveParams); }
|
依赖注入
injectionParameters() :解析参数,拼凑所需的参数
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
|
function injectionParameter(array $parmeters, $vars = []) { $resolveParams = []; foreach ($parmeters as $k => $v) { $name = $v->getName(); if (isset($vars[$name])) { $resolveParams[] = $vars[$name]; continue; } $default = $v->isDefaultValueAvailable() ? $v->getDefaultValue() : null; if (!is_null($default)) { $resolveParams[] = $default; continue; } if ($v->getClass()) { $resolveParams[] = make($v->getClass()->getName(), $vars); continue; } echo $name . "不能为空"; exit; }
return $resolveParams; }
|
注入依赖最核心方法就是通过getClass 获取参数类型提示
ReflectionParamters->getClass() :获取参数类型提示
在该参数没有默认值且有设置类型提示时
递归去调用 make() 方法将类实例化出来后注入到参数里
调用类方法
action():传入实例化的类,对应的方法名之后执行该方法
1 2 3 4 5 6 7 8 9 10 11 12 13
| function action($class, $method, $vars = []) { if (!method_exists($class, $method)) { echo '方法不存在'; exit; } $ref = new ReflectionMethod($class, $method); $parameters = $ref->getParameters(); $resolveParams = is_null($parameters) ? [] : injectionParameter($parameters, $vars);
return $ref->invoke($class, ...$resolveParams); }
|
代码示例
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 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
| <?php class Bag { public $name;
public function __construct($bagName = '书包') { $this->name = $bagName; } }
class Pencil { public $name;
public function __construct($pencilName = '铅笔') { $this->name = $pencilName; } }
class People { public $bag;
public $name;
public function __construct(Bag $bag, $peopleName = 'test') { $this->bag = $bag; $this->name = $peopleName; }
public function bag() { echo $this->name . ' has a ' . $this->bag->bag; }
public function has(Pencil $pencil, $say = '') { echo "hello, I'm is {$this->name}." . "<br />"; echo "I has {$pencil->name}." . "<br />"; echo "I has {$this->bag->name}." . "<br />"; if ($say) { echo $this->name . "say {$say}"; } } }
function make($class, $vars = []) { if (!class_exists($class)) { echo '类不存在'; exit; } $ref = new ReflectionClass($class); if (!$ref->isInstantiable()) { echo $class . '不可以实例化'; exit; } $construct = $ref->getConstructor(); if (is_null($construct)) { return new $class; } $parmeters = $construct->getParameters(); $resolveParams = is_null($parmeters) ? [] : injectionParameter($parmeters, $vars); return $ref->newInstanceArgs($resolveParams); }
function action($class, $method, $vars = []) { if (!method_exists($class, $method)) { echo '方法不存在'; exit; } $ref = new ReflectionMethod($class, $method); $parameters = $ref->getParameters(); $resolveParams = is_null($parameters) ? [] : injectionParameter($parameters, $vars);
return $ref->invoke($class, ...$resolveParams); }
function injectionParameter(array $parmeters, $vars = []) { $resolveParams = []; foreach ($parmeters as $k => $v) { $name = $v->getName(); if (isset($vars[$name])) { $resolveParams[] = $vars[$name]; continue; } $default = $v->isDefaultValueAvailable() ? $v->getDefaultValue() : null; if (!is_null($default)) { $resolveParams[] = $default; continue; } if ($v->getClass()) { $resolveParams[] = make($v->getClass()->getName(), $vars); continue; } echo $name . "不能为空"; exit; }
return $resolveParams; }
$people = make('People', [ 'peopleName' => '小明', 'pencilName' => '2B铅笔', 'bagName' => '自定义书包', ]); action($people, 'has');
|
总结 :
make() : 实例化一个类,假如构造函数有依赖的时候实例化并将实例注入到类里
injectionParameter() :解析参数,当参数没有传值,也没有默认值,但是有类型提示的情况下。实例化该类实例并注入到参数中返回。
action():执行类里面的一个方法,假如方法参数里依赖于其他类的话将类实例化并注入到方法的参数中。
该示例模拟了框架常用的一个依赖注入的功能。
通过路由解析到实际执行的类和方法,然后执行该方法。
上面的示例就大概模仿了去执行路由对应的动作这个流程。