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 相关文章推荐
社区(php&&mysql)一
Oct 09 PHP
一个很不错的PHP翻页类
Jun 01 PHP
一些 PHP 管理系统程序中的后门
Aug 05 PHP
Linux下将excel数据导入到mssql数据库中的方法
Feb 08 PHP
PHP 数组遍历方法大全(foreach,list,each)
Jun 30 PHP
PHP中foreach循环中使用引用要注意的地方
Jan 02 PHP
PHP计划任务、定时执行任务的实现代码
Apr 23 PHP
PHP实现异步调用方法研究与分享
Oct 27 PHP
thinkphp路由规则使用示例详解和伪静态功能实现(apache重写)
Feb 24 PHP
php实现支持中文的文件下载功能示例
Aug 30 PHP
yii框架redis结合php实现秒杀效果(实例代码)
Oct 26 PHP
浅谈PHP7中的一些小技巧
May 29 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
可以在线执行PHP代码包装修正版
2008/03/15 PHP
smarty基础之拼接字符串的详解
2013/06/18 PHP
PHP中Header使用的HTTP协议及常用方法小结
2014/11/04 PHP
如何用PHP来实现一个动态Web服务器
2015/07/29 PHP
thinkPHP5实现的查询数据库并返回json数据实例
2017/10/23 PHP
利用js(jquery)操作Cookie的方法说明
2013/12/19 Javascript
jquery ajax跨域解决方法(json方式)
2014/02/04 Javascript
jQuery常用数据处理方法小结
2015/02/20 Javascript
JS实现当前页居中分页效果的方法
2015/06/18 Javascript
javascript实现的网站访问量统计代码
2015/12/20 Javascript
jQuery插件DataTable使用方法详解(.Net平台)
2016/12/22 Javascript
深入剖析Node.js cluster模块
2018/05/23 Javascript
vue实现拖拽的简单案例 不超出可视区域
2019/07/25 Javascript
js实现随机点名程序
2020/09/17 Javascript
微信小程序接入腾讯云验证码的方法步骤
2020/01/07 Javascript
Ant Design Pro 之 ProTable使用操作
2020/10/31 Javascript
Pyinstaller将py打包成exe的实例
2018/03/31 Python
Python占用的内存优化教程
2019/07/28 Python
Python单元测试工具doctest和unittest使用解析
2019/09/02 Python
Django框架 Pagination分页实现代码实例
2019/09/04 Python
pytorch的梯度计算以及backward方法详解
2020/01/10 Python
Python爬虫实现selenium处理iframe作用域问题
2021/01/27 Python
斯凯奇新西兰官网:SKECHERS新西兰
2018/02/22 全球购物
线程问题:wait()方法是定义在哪个类里面
2015/07/07 面试题
日语系毕业生推荐信
2013/11/11 职场文书
校园文化建设方案
2014/02/03 职场文书
《台湾的蝴蝶谷》教学反思
2014/02/20 职场文书
授权委托书怎么写
2014/04/03 职场文书
全运会口号
2014/06/20 职场文书
预备党员思想汇报1000字
2014/10/07 职场文书
加强作风建设心得体会
2014/10/22 职场文书
护理实习生带教计划
2015/01/16 职场文书
学校通报表扬范文
2015/05/04 职场文书
消夏晚会主持词
2015/06/30 职场文书
vue实现锚点定位功能
2021/06/29 Vue.js
Oracle 临时表空间SQL语句的实现
2021/09/25 Oracle