php面试实现反射注入的详细方法


Posted in PHP onSeptember 30, 2019

PHP具有完整的反射API,提供了对类、接口、函数、方法和扩展进行逆向工程的能力。通过类的反射提供的能力我们能够知道类是如何被定义的,它有什么属性、什么方法、方法都有哪些参数,类文件的路径是什么等很重要的信息。正是因为类的反射,很多PHP框架才能实现依赖注入自动解决类与类之间的依赖关系,这给我们平时的开发带来了很大的方便。

本文主要是讲解如何利用类的反射来实现依赖注入(Dependency Injection),并不会去逐条讲述PHP Reflection里的每一个API。为了更好地理解,我们通过一个例子来看类的反射,以及如何实现依赖注入。

下面这个类代表了坐标系里的一个点,有两个属性横坐标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(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面试怎么实现反射注入的详细内容,更多请关注三水点靠木其它相关文章!

PHP 相关文章推荐
让你同时上传 1000 个文件 (二)
Oct 09 PHP
常用的php ADODB使用方法集锦
Mar 25 PHP
CodeIgniter基本配置详细介绍
Nov 12 PHP
php中将一段数据存到一个txt文件中并显示其内容
Aug 15 PHP
PHP中的一些常用函数收集
May 26 PHP
php实现在线通讯录功能(附源码)
May 13 PHP
PHP获取不了React Native Fecth参数的解决办法
Aug 26 PHP
php有效防止图片盗用、盗链的两种方法
Nov 01 PHP
PHP实现的多维数组去重操作示例
Jul 21 PHP
php intval函数用法总结
Apr 14 PHP
PHP7新功能总结
Apr 14 PHP
浅谈laravel框架sql中groupBy之后排序的问题
Oct 17 PHP
laravel5表单唯一验证的实例代码
Sep 30 #PHP
Laravel模糊查询区分大小写的实例
Sep 29 #PHP
基于laravel Request的所有方法详解
Sep 29 #PHP
基于Laravel 5.2 regex验证的正确写法
Sep 29 #PHP
php实现对文件压缩简单的方法
Sep 29 #PHP
Laravel 添加多语言提示信息的方法
Sep 29 #PHP
php实现登录页面的简单实例
Sep 29 #PHP
You might like
如何将一个表单同时提交到两个地方处理
2006/10/09 PHP
PHP MSSQL 存储过程的方法
2008/12/24 PHP
TP5框架简单登录功能实现方法示例
2019/10/31 PHP
在JQuery dialog里的服务器控件 事件失效问题
2010/12/08 Javascript
JS特权方法定义作用以及与公有方法的区别
2013/03/18 Javascript
基于datagrid框架的查询
2013/04/08 Javascript
禁止页面刷新让F5快捷键及右键都无效
2014/01/22 Javascript
JS合并数组的几种方法及优劣比较
2014/09/19 Javascript
JavaScript常用脚本汇总(三)
2015/03/04 Javascript
浅谈jquery点击label触发2次的问题
2016/06/12 Javascript
Bootstrap零基础入门教程(三)
2016/07/18 Javascript
详解vue嵌套路由-params传递参数
2017/05/23 Javascript
vue的事件绑定与方法详解
2017/08/16 Javascript
Vue通过URL传参如何控制全局console.log的开关详解
2017/12/07 Javascript
nodejs调取微信收货地址的方法
2017/12/20 NodeJs
Vue.directive 自定义指令的问题小结
2018/03/04 Javascript
微信小程序中转义字符的处理方法
2019/03/28 Javascript
vue 实现input表单元素的disabled示例
2019/10/28 Javascript
vue使用原生swiper代码实例
2020/02/05 Javascript
js+canvas实现纸牌游戏
2020/03/16 Javascript
Node.JS如何实现JWT原理
2020/09/18 Javascript
Python利用QQ邮箱发送邮件的实现方法(分享)
2017/06/09 Python
对python指数、幂数拟合curve_fit详解
2018/12/29 Python
Python实现的调用C语言函数功能简单实例
2019/03/13 Python
Python Django 封装分页成通用的模块详解
2019/08/21 Python
python 装饰器重要在哪
2021/02/14 Python
c++工程师面试问题
2013/08/04 面试题
士力架广告词
2014/03/20 职场文书
银行青年文明号事迹材料
2014/05/31 职场文书
质量月口号
2014/06/20 职场文书
植树节口号
2014/06/21 职场文书
高三语文复习计划
2015/01/19 职场文书
创业方案:赚钱的烧烤店该怎样做?
2019/07/05 职场文书
Python torch.flatten()函数案例详解
2021/08/30 Python
Python+腾讯云服务器实现每日自动健康打卡
2021/12/06 Python
Python爬虫 简单介绍一下Xpath及使用
2022/04/26 Python