PHP Reflection与依赖注入
PHP具有完整的反射 API,提供了对类、接口、函数、方法和扩展进行逆向工程的能力。通过类的反射提供的能力我们能够知道类是如何被定义的,它有什么属性、什么方法、方法都有哪些参数,类文件的路径是什么等很重要的信息。也正式因为类的反射很多PHP框架才能实现依赖注入自动解决类与类之间的依赖关系,这给我们平时的开发带来了很大的方便。 本文主要是讲解如何利用类的反射来实现依赖注入(Dependency Injection),并不会去逐条讲述PHP Reflection里的每一个API,详细的API参考信息请查阅官方文档
再次声明这里实现的依赖注入非常简单,并不能应用到实际开发中去,可以参考我的另一篇文章来了解laravel框架的服务容器(IocContainer), 在那里会介绍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的依赖注入也是通过这个思路来实现的,只不过设计的更精密大量地利用了闭包回调来应对各种复杂的依赖注入,详情可以参考我的另一篇介绍Laravel服务容器的文章。
本文的示例代码的下载链接
PHP Reflection与依赖注入相关推荐
- 框架依赖注入和普通依赖注入_依赖注入快速入门:它是什么,以及何时使用它...
框架依赖注入和普通依赖注入 by Bhavya Karia 通过Bhavya Karia 介绍 (Introduction) In software engineering, dependency i ...
- laravel mysql注入_laravel中如何利用反射实现依赖注入
依赖注入 在一个类中经常会依赖于其他的对象,先看一下经典的写法 class Foo { public $bar; public function __construct() { $this->b ...
- Android 依赖注入: Dagger 2 实例解说(一)
本文原创,转载请注明出处:http://blog.csdn.net/zjbpku [Duplicated] link to Dagger on Android - Dagger2具体解释 关于D ...
- 【C#|.NET】从控制反转(依赖注入)想到事件注入 (非AOP)
前文 事件注入的想法是由依赖注入所联想到 依赖注入不算什么吸引人的话题 本篇就不详说了 不过有闲暇时间的机会不妨按照自己的兴趣去摸索.研究一些东西,也是一种乐子. 在抓虫系列里简单的描述一下依赖注入在 ...
- 抽象工厂+反射+依赖注入 实现对数据访问层和业务逻辑层的优化
分层思想的一个核心就是部件化,各个层之间是相互独立的,每一层可以随便抽取换成一个其他语言的版本,但只要与相应的接口吻合就行. 我用的三层架构大致是这样的,基本的三层就不说了,然后分别为业务逻辑层和数据 ...
- 利用ASP.netCore自带DI(DependencyInjection)实现批量依赖注入
转载来源 http://www.cnblogs.com/xiaoliangge/p/7642372.html ASP.net Core自带DI(依赖注入),用法如下: services.AddScop ...
- ASP.NET MVC3 + Ninject.Mvc3 依赖注入原来可以这么简单
第一步.新创建一个 ASP.NET MVC3 工程. 第二步.通过 NuGet 控制台直接输入命令:install-package Ninject.Mvc3 安装完这个源码包之后,所有的依赖注入框架已 ...
- 依赖注入 这样的坑游戏编程要谨慎
1 IGame游戏公司的故事 1.1 讨论会 话说有一个叫IGame的游戏公司,正在开发一款ARPG游戏(动作&角色扮演类游戏,如魔兽世界.梦幻西游这一类的游戏).一般这类游戏都有一个基本的功 ...
- C#中的依赖注入那些事儿
目录 目录 1 IGame游戏公司的故事 1.1 讨论会 1.2 实习生小李的实现方法 1.3 架构师的建议 1.4 小李的小结 2 探究依赖注入 2.1 故事的启迪 2.2 正式定义依赖注入 3 依 ...
最新文章
- 高并发场景下更新数据库报错,记录一次 MySQL 死锁问题的解决
- 重磅!GitHub发布开源负载均衡组件GLB
- 数模学习笔记——微分方程
- Windows x64内核学习笔记(五)—— KPTI(未完待续)
- Mysql的存储过程修改表的数据:项目上一个小练习
- 使用BeetleX在Linux下部署.NET多站点服务
- java铝轮_为速度而生 JAVA Fuoco铝合金气动公路
- Spring bean作用范围
- Linux常用到的指令汇总
- 02.C(数据类型与运算符)
- Leap手心发射线,碰撞点用小球表示,并用Line Renderer画出来
- [转]小D课堂 - 零基础入门SpringBoot2.X到实战_汇总
- 统计学和算法相关的基础知识(持续更新)
- Flutter状态管理Provider的简单使用
- 腾讯云游戏多媒体解决方案
- 课程笔记-三维点云处理01 ——Introduction and Basic Algorithms
- WPS广告投放的优势!WPS广告投放的展现形式!
- 自定义注解实现RBAC权限校验,不要再说你不会了
- 市场周刊杂志市场周刊杂志社市场周刊编辑部2022年第6期目录
- java写入html,java如何写入文件