ReflectionClass实现了 Reflector 接口,使得我们可以使用该类查看另一个类的相关信息。所谓的反射,大概的意思就是将一个类的相关信息给反射 (映射、反映) 出来,转载。

无依赖的情况

要实例化一个类,获得其类名即可,实际项目中还需要结合自动加载,这里为了方便说明情况,就将所有类写在同一个文件中。这个操作很简单。

namespace Models;

class Car

{

}

namespace Framework;

class App

{

public function getInstance($className)

{

//实例化 ReflectionClass 对象

$reflector = new \ReflectionClass($className);

if (!$reflector->isInstantiable()) {

//不能被实例化的逻辑

return false;

}

//获取构造器

$constructor = $reflector->getConstructor();

//如果没有构造器,直接实例化

if (!$constructor) {

//这里用了变量来动态的实例化类

return new $className;

}

}

}

$app = new App();

$car = $app->getInstance('Models\Car');

var_dump($car); //输出 object(Models\Car)#4 (0) { }

带有多层依赖的情况

假设有一个汽车依赖底盘,底盘依赖轮胎和轴承,轮胎也依赖轴承,轴承无依赖。那么当需要实例化一个汽车类时,不友好的方式是这样的,$car = new Car(new Chassis(new Tyre(new Axle), new Axle())) ,打脑阔。

利用依赖注入是这样的。

namespace Framework;

//定义一个类,用于实现依赖注入

class App

{

public function getInstance($className)

{

//实例化 ReflectionClass 对象

$reflector = new \ReflectionClass($className);

if (!$reflector->isInstantiable()) {

//不能被实例化的逻辑,抽象类和接口不能被实例化

return false;

}

//获取构造器

$constructor = $reflector->getConstructor();

//如果没有构造器,也就是没有依赖,直接实例化

if (!$constructor) {

return new $className;

}

//如果有构造器,先把构造器中的参数获取出来

$parameters = $constructor->getParameters();

//再遍历 parameters ,找出每一个类的依赖,存到 dependencies 数组中

$dependencies = array_map(function ($parameter) {

/**

* 这里是递归的去寻找每一个类的依赖,例如第一次执行的时候,程序发现汽车 Car 类依赖底盘 Chassis

* 类,此时 $parameter 是一个ReflectionParameter 的实例,接着调用 ReflectionParameter

* 的 getClass() 方法,获得一个 ReflectionClass 的实例,再接着调用 ReflectionClass

* 的 getName() 方法,取得类名,也就是 Models\Chassis ,但此时此刻还不能直接去 new

* Models\Chassis ,因为 Models\Chassis 也有依赖,故要递归的去调用 getInstance

* 进一步去寻找该类的依赖,周而复始,直到触发上面的 if(!$constructor) ,停止递归。

*/

return $this->getInstance($parameter->getClass()->getName());

}, $parameters);

//最后,使用 ReflectionClass 类提供的 newInstanceArgs ,方法去实例化类,参数将会传入构造器中

return $reflector->newInstanceArgs($dependencies);

}

}

namespace Models;

class Car

{

protected $chassis;

//汽车依赖底盘

public function __construct(Chassis $chassis)

{

$this->chassis = $chassis;

}

}

class Chassis

{

protected $tyre;

protected $axle;

//底盘依赖轮胎和轴承

public function __construct(Tyre $tyre, Axle $axle)

{

$this->tyre = $tyre;

$this->axle = $axle;

}

}

class Tyre

{

protected $axle;

//轮胎也依赖轴承

public function __construct(Axle $axle)

{

$this->axle = $axle;

}

}

class Axle

{

//轴承无依赖

}

$app = new \Framework\App();

$car = $app->getInstance('Models\Car');

var_dump($car);

处理构造方法中的 普通参数class Car

{

protected $chassis;

protected $width;

//汽车依赖底盘

public function __construct(Chassis $chassis, $width) //

{

$this->chassis = $chassis;

$this->width = $width;

}

}

运行代码,报错 call to function getName() on null ,问题出在了 return $this->getInstance($parameter->getClass()->getName()) 这一行,原因是 $parameter->getClass() 的结果是 null,这也是必然的。查看手册发现这样的一段描述,ReflectionParameter::getClass — Get the type hinted class (获取所提示的类),上面加入的 $width ,没有做类型提示,$parameter->getClass() 得到的结果必然是 null 。

故,将有类型提示的和没有类型提示的分开处理。需要这样处理

namespace Framework;

class App

{

public function getInstance($className)

{

$reflector = new \ReflectionClass($className);

if (!$reflector->isInstantiable()) {

return false;

}

$constructor = $reflector->getConstructor();

if (!$constructor) {

return new $className;

}

$parameters = $constructor->getParameters();

$dependencies = array_map(function ($parameter) {

if (null == $parameter->getClass()) {

//处理没有类型提示的参数

return $this->processNoHinted($parameter);

} else {

//处理有类型提示的参数

return $this->processHinted($parameter);

}

}, $parameters);

return $reflector->newInstanceArgs($dependencies);

}

protected function processNoHinted(\ReflectionParameter $parameter)

{

if ($parameter->isDefaultValueAvailable()) {

return $parameter->getName();

} else {

//参数为空则抛出异常

throw new \Exception($parameter->getName() . "不能为空", 1);

}

}

protected function processHinted(\ReflectionParameter $parameter)

{

return $this->getInstance($parameter->getClass()->getName());

}

}

namespace Models;

class Car

{

protected $chassis;

protected $width;

public function __construct(Chassis $chassis, $width = 2)

{

$this->chassis = $chassis;

$this->width = $width;

}

}

class Chassis

{

protected $tyre;

protected $axle;

public function __construct(Tyre $tyre, Axle $axle)

{

$this->tyre = $tyre;

$this->axle = $axle;

}

}

class Tyre

{

protected $axle;

public function __construct(Axle $axle)

{

$this->axle = $axle;

}

}

class Axle

{

}

$app = new \Framework\App();

$car = $app->getInstance('Models\Car');

var_dump($car);

php反射机制与依赖注入,利用反射机制实现基本的依赖注入相关推荐

  1. java 反射 单例类_利用反射机制破坏单例模式

    简介 利用反射机制破坏了单例模式,这里以懒汉单例模式为例子进行操作. 之前利用反射也是改变了类中的private变量. 类中的private变量真的private么? 正常的单例模式的实现 这里采用了 ...

  2. java asm jndi_GitHub - Q1ngShan/JNDI: JNDI 注入利用工具

    JNDI 注入利用工具 介绍 本项目为 JNDI 注入利用工具,生成 JNDI 连接并启动后端相关服务,可用于 Fastjson.Jackson 等相关漏洞的验证. 本项目是基于 welk1n 的 J ...

  3. java asm jndi_JNDI 注入利用工具

    JNDI 注入利用工具 介绍 本项目为 JNDI 注入利用工具,生成 JNDI 连接并启动后端相关服务,可用于 Fastjson.Jackson 等相关漏洞的验证. 本项目是基于 welk1n 的 J ...

  4. java反射什么时候用到_Java 反射最佳实践

    概要:最简单优雅的使用反射. 本文的例子都可以在示例代码中看到并下载,如果喜欢请star,如果觉得有纰漏请提交issue,如果你有更好的点子可以提交pull request.本文的示例代码主要是基于 ...

  5. android利用反射调用截屏api,Android利用反射机制调用截屏方法和获取屏幕宽高的方法...

    想要在应用中进行截屏,可以直接调用 View 的 getDrawingCache 方法,但是这个方法截图的话是没有状态栏的,想要整屏截图就要自己来实现了. 还有一个方法可以调用系统隐藏的 screen ...

  6. 利用反射机制获取未知类型的枚举的信息

    原文:利用反射机制获取未知类型的枚举的信息 开发游戏设置选项遇到一个问题,我有两个枚举,一个是屏幕分辨率,一个是语言 我需要在不知道一个枚举到底是哪一个枚举类型的情况下,获取这个枚举的值以及这个枚举类 ...

  7. java判断对象无数据_java利用反射机制判断对象的属性是否为空以及获取和设置该属性的值...

    1.java利用反射机制判断对象的属性是否为空: Map validateMap = new LinkedHashMap(); validateMap.put("serial", ...

  8. 利用反射机制创建新类的两种方式及比较

    [0]README 0.1) 本文描述+源代码均 转自 http://blog.csdn.net/fenglibing/article/details/4531033 , 旨在深入理解 如何利用反射机 ...

  9. android 巧妙利用反射机制获取控件id,避免大量冗杂的findviewbyid和butterknife注解

    android 巧妙利用反射机制获取控件id,避免大量冗杂的findviewbyid和butterknife注解 一.反射机制概述 Java 反射机制是在运行状态中,对于任意一个类,都能够获得这个类的 ...

最新文章

  1. 大厂程序员跳槽去小公司当CTO,是一种怎样的体验?
  2. php 提取登录QQ,php QQ登录
  3. c java python html_如何通俗地解释 C、C++、C#、Java、JavaScript、HTML、Python的用处
  4. phpcms v9 在当前栏目下获取父栏目与当前栏目的名称与连接
  5. python | 关键词快速匹配检索小工具 pyahocorasick / ahocorapy
  6. 在Win32中管理虚拟内存——举例
  7. C语言小案例_关于爱普生喷墨机APG复位错误(APG reset error)的最终答案: 故障案例 每日一例 【第1358篇】...
  8. 健易保获数千万元A轮融资,BV百度风投投资
  9. 公文流转 java_javaweb 公文流转系统制作
  10. 群晖(Synology)配置 NAS + 软路由 续
  11. 示例:父子关系(Parent Child Relationships)
  12. 访问图片出现403的解决办法
  13. 全球及中国电解电容器(E-Cap)供给能力与竞争状况分析报告2022~2027年
  14. java获取double类型区间随机数
  15. 前端html字体设置
  16. 基于ZigBee 的多点温度采集系统设计与实现
  17. html如何设置多级列表,如何在Word文档中设置多级列表
  18. Linux命令行技巧——使用目录栈进行导航
  19. 一文搞懂设计模式--模板模式
  20. 最近找工作时,一些杂七杂八的问题

热门文章

  1. ArchLinux安装Gnome桌面
  2. Google 面试题和详解
  3. 本地开发时同时启动多个tomcat服务器
  4. 老男孩python爬虫视频教程_python爬虫入门
  5. 地线与接地螺丝_快来看看新能源电动汽车充电时,地线的安装情况吧!
  6. KafkaProducer介绍
  7. Linux vi vim 常用快捷键操作(一)
  8. (4)ZYNQ AXI4总线协议介绍
  9. (23)FPGA面试技能提升篇(SSC接口、V35接口)
  10. html页面循环报错,wxs 脚本中 for 循环的一种写法导致 page-frame.html 报错