PHP具有完整的反射 API,提供了对类、接口、函数、方法和扩展进行逆向工程的能力。通过类的反射提供的能力我们能够知道类是如何被定义的,它有什么属性、什么方法、方法都有哪些参数,类文件的路径是什么等很重要的信息。也正式因为类的反射很多PHP框架才能实现依赖注入自动解决类与类之间的依赖关系,这给我们平时的开发带来了很大的方便。 本文主要是讲解如何利用类的反射来实现依赖注入(Dependency Injection),并不会去逐条讲述PHP Reflection里的每一个API,详细的API参考信息请查阅[官方文档][1]

再次声明这里实现的依赖注入非常简单,并不能应用到实际开发中去,可以参考后面的文章[服务容器(IocContainer)][2], 了解Laravel的服务容器是如何实现依赖注入的。

为了更好地理解,我们通过一个例子来看类的反射,以及如何实现依赖注入。
下面这个类代表了坐标系里的一个点,有两个属性横坐标x和纵坐标y。

/*** Class Point*/class Point{    public $x;    public $y;    /*** Point constructor.* @param int $x  horizontal value of point's coordinate* @param int $y  vertical value of point's coordinate*/public function __construct($x = 0, $y = 0){$this->x = $x;$this->y = $y;}
}

接下来这个类代表圆形,可以看到在它的构造函数里有一个参数是Point类的,即Circle类是依赖与Point类的。

class Circle
{    /*** @var int*/public $radius;//半径/*** @var Point*/public $center;//圆心点const PI = 3.14;public function __construct(Point $point, $radius = 1){$this->center = $point;        $this->radius = $radius;}    //打印圆点的坐标public function printCenter(){printf('center coordinate is (%d, %d)', $this->center->x, $this->center->y);}    //计算圆形的面积public function area(){        return 3.14 * pow($this->radius, 2);}
}

ReflectionClass

下面我们通过反射来对Circle这个类进行反向工程。
Circle类的名字传递给reflectionClass来实例化一个ReflectionClass类的对象。

$reflectionClass = new reflectionClass(Circle::class);
//返回值如下
object(ReflectionClass)#1 (1) {["name"]=>string(6) "Circle"
}

反射出类的常量

$reflectionClass->getConstants();

返回一个由常量名称和值构成的关联数组

array(1) {["PI"]=>float(3.14)
}

通过反射获取属性

$reflectionClass->getProperties();

返回一个由ReflectionProperty对象构成的数组

array(2) {[0]=>object(ReflectionProperty)#2 (2) {["name"]=>string(6) "radius"["class"]=>string(6) "Circle"}[1]=>object(ReflectionProperty)#3 (2) {["name"]=>string(6) "center"["class"]=>string(6) "Circle"}
}

反射出类中定义的方法

$reflectionClass->getMethods();

返回ReflectionMethod对象构成的数组

array(3) {[0]=>object(ReflectionMethod)#2 (2) {["name"]=>string(11) "__construct"["class"]=>string(6) "Circle"}[1]=>object(ReflectionMethod)#3 (2) {["name"]=>string(11) "printCenter"["class"]=>string(6) "Circle"}[2]=>object(ReflectionMethod)#4 (2) {["name"]=>string(4) "area"["class"]=>string(6) "Circle"}
}

我们还可以通过getConstructor()来单独获取类的构造方法,其返回值为一个ReflectionMethod对象。

$constructor = $reflectionClass->getConstructor();

反射出方法的参数

$parameters = $constructor->getParameters();

其返回值为ReflectionParameter对象构成的数组。

array(2) {[0]=>object(ReflectionParameter)#3 (1) {["name"]=>string(5) "point"}[1]=>object(ReflectionParameter)#4 (1) {["name"]=>string(6) "radius"}
}

依赖注入

好了接下来我们编写一个名为make的函数,传递类名称给make函数返回类的对象,在make里它会帮我们注入类的依赖,即在本例中帮我们注入Point对象给Circle类的构造方法。

//构建类的对象
function make($className)
{$reflectionClass = new ReflectionClass($className);    $constructor = $reflectionClass->getConstructor();    $parameters  = $constructor->getParameters();    $dependencies = getDependencies($parameters);    return $reflectionClass->newInstanceArgs($dependencies);
}//依赖解析
function getDependencies($parameters)
{    $dependencies = [];foreach($parameters as $parameter) {$dependency = $parameter->getClass();if (is_null($dependency)) {if($parameter->isDefaultValueAvailable()) {$dependencies[] = $parameter->getDefaultValue();} else {//不是可选参数的为了简单直接赋值为字符串0//针对构造方法的必须参数这个情况//laravel是通过service provider注册closure到IocContainer,//在closure里可以通过return new Class($param1, $param2)来返回类的实例//然后在make时回调这个closure即可解析出对象//具体细节我会在另一篇文章里面描述$dependencies[] = '0';}} else {            //递归解析出依赖类的对象$dependencies[] = make($parameter->getClass()->name);}}    return $dependencies;
}

定义好make方法后我们通过它来帮我们实例化Circle类的对象:

$circle = make('Circle');$area = $circle->area();/*var_dump($circle, $area);
object(Circle)#6 (2) {["radius"]=>int(1)["center"]=>object(Point)#11 (2) {["x"]=>int(0)["y"]=>int(0)}
}
float(3.14)*/

通过上面这个实例我简单描述了一下如何利用PHP类的反射来实现依赖注入,Laravel的依赖注入也是通过这个思路来实现的,只不过设计的更精密大量地利用了闭包回调来应对各种复杂的依赖注入。

点击阅读原文可以获取文中用的代码示例。

一文看懂PHP如何实现依赖注入相关推荐

  1. 一文读懂Asp.net core 依赖注入(Dependency injection)

    一.什么是依赖注入 首先在Asp.net core中是支持依赖注入软件设计模式,或者说依赖注入是asp.net core的核心: 依赖注入(DI)和控制反转(IOC)基本是一个意思,因为说起来谁都离不 ...

  2. 一文看懂深度学习——人工智能系列学习笔记

    深度学习有很好的表现,引领了第三次人工智能的浪潮.目前大部分表现优异的应用都用到了深度学习,大红大紫的 AlphaGo 就使用到了深度学习. 本文将详细的给大家介绍深度学习的基本概念.优缺点和主流的几 ...

  3. 一文看懂async和“await”关键词是如何简化了C#中多线程的开发过程

    一文看懂"async"和"await"关键词是如何简化了C#中多线程的开发过程 当我们使用需要长时间运行的方法(即,用于读取大文件或从网络下载大量资源)时,在同 ...

  4. 「最有用」的特殊大数据:一文看懂文本信息系统的概念框架及功能

    导读:作为一种特殊的大数据,文本数据泛指各种以自然语言形式存在的数据. 目前,我们正处在一个以大数据与人工智能技术为核心的新的工业革命时代,其主要特征是大量各种可利用的数据可以视为一种特殊的生产资料, ...

  5. 一文看懂JUC之AQS机制

     作者:VectorJin juejin.cn/post/6844904041760161806 为了解决原子性的问题,Java加入了锁机制,同时保证了可见性和顺序性.JDK1.5的并发包中新增了Lo ...

  6. 无处 不在的无线智能——6g 的关键驱动与研究挑战_一文看懂什么是 6G

    原标题:一文看懂什么是 6G 2020年行将结束,随着5G网络的建设推进,以及3GPP R16版本的冻结,越来越多的人将关注焦点转移到6G身上. 7月14日,韩国三星电子发布了白皮书<下一代超连 ...

  7. 一文看懂Android APK安装的原理

    一文看懂Android APK安装的原理 前言 APK包的构成 安装APK 总结 前言 大家有没有想过一个应用的APK是怎么被安装到安卓手机上的,安装的本质是什么?我们知道,Windows应用程序的安 ...

  8. 一文看懂“业务定制智能客服”的产品设计_团员分享_@苍狼剑歌

    前言:本文作者是"AI产品经理大本营"团员@苍狼剑歌,现任某一线大厂AI产品经理.另外,文末还有2个"hanniman读者专属福利",1)优惠券 for 三节课 ...

  9. 《SOC芯片研究框架》深度科普,发展趋势、技术特点、产业链一文看懂

    片上系统SoC(System on Chip),即在一块芯片上集成一整个信息处理系统,简单来说 SoC芯片是在中央处理器CPU的基础上扩展音视频功能和专用接口的超大规模集成电路,是智能设备的" ...

最新文章

  1. AI框架精要:设计思想
  2. 从深度图到点云的构建方式
  3. 【IMOOC学习笔记】多种多样的App主界面Tab实现方法(二)
  4. LeetCode题组:第9题-回文数
  5. ADN中国团队參加微软的Kinect全国大赛获得三等奖
  6. python代码删掉了几行怎么撤回_仅78行代码实现微信撤回消息查看 | Python itchat
  7. 牛顿下山法python_一文看懂牛顿法(附Python实现)
  8. border做三角形
  9. 模糊c均值聚类_聚类算法及其数据可视化
  10. 华硕 Asus TUF B360M+INTEL i5+Sapphire AMD VEGA56黑苹果EFI引导文件
  11. centos linux7 开启桌面命令,centos7如何在桌面打开终端
  12. 论文快报-2021-10-Multi-task optimization and evolutionary multitasking
  13. php snappy,php – 使用Knp Snappy生成pdf文件时出现错误字符
  14. html设置图片切割,HTML+CSS实现合并图片的切割显示以及背景渲染
  15. yolo模型转换:pytorch -> onnx -> caffe
  16. C语言关键字浅析-enum
  17. 结构化数据和非结构化数据
  18. 文心一言眼里的Java世界
  19. java jsf 入门_JSF入门实战
  20. 幽默感七个技巧_培养幽默感的16种方法

热门文章

  1. django处理静态文件
  2. PHP语言 -- 发起流程
  3. 初识PHP变量函数语法
  4. mod_rewrite
  5. Deepin Linux修改Grub引导
  6. Python--操作数据库class
  7. Ubuntu 16.04通过Snap安装应用程序
  8. 用babel cli编译用ES6写的JSX
  9. 记录C++ Builder 6.0开发过程中的一个linker error
  10. 让我的网站变成响应式的3个简单步骤