如何在yii2框架的di容器源码中了解反射的作用
这篇文章主要介绍了关于如何在yii2框架的di容器源码中了解反射的作用,有着一定的参考价值,现在分享给大家,有需要的朋友可以参考一下
反射简介
参考官方简介的话,PHP 5 具有完整的反射 API,添加了对类、接口、函数、方法和扩展进行反向工程的能力。 此外,反射 API 提供了方法来取出函数、类和方法中的文档注释。
YII2框架中示例
对于yii2框架,应该都知道di容器,对于di容器的源码这里也主要讲明Container类,先看看平时怎么使用di,就用yii2框架中注释的示例代码来展示;
container调用示例
namespace app\models; use yii\base\BaseObject; use yii\db\Connection; use yii\di\Container; interface UserFinderInterface { function findUser(); } class UserFinder extends BaseObject implements UserFinderInterface { public $db; public function __construct(Connection $db, $config = []) { $this->db = $db; parent::__construct($config); } public function findUser() { } } class UserLister extends BaseObject { public $finder; public function __construct(UserFinderInterface $finder, $config = []) { $this->finder = $finder; parent::__construct($config); } } $container = new Container; $container->set('yii\db\Connection', [ 'dsn' => '...', ]); $container->set('app\models\UserFinderInterface', [ 'class' => 'app\models\UserFinder', ]); $container->set('userLister', 'app\models\UserLister'); $lister = $container->get('userLister'); // 上述操作相当于下列实现 $db = new \yii\db\Connection(['dsn' => '...']); $finder = new UserFinder($db); $lister = new UserLister($finder);
上面的示例代码只是实例化了Container类,然后调用set方法注入了其他对象,最后获取到了依赖与其他对象创建的lister对象,既然只调用了set方法与get方法,那就先从调用最多的set开始看Container代码。
set方法
public function set($class, $definition = [], array $params = []) { $this->_definitions[$class] = $this->normalizeDefinition($class, $definition); $this->_params[$class] = $params; unset($this->_singletons[$class]); return $this; }
上面的代码比较简洁,调用了类的normalizeDefinition方法,这个一会再说,先说明在该方法中出现的三个属性的含义
_definitions数组,保存依赖定义
_params数组,保存构造函数的参数
_singletons,保存单例
再看normalizeDefinition方法,该方法主要作用是规范类定义
protected function normalizeDefinition($class, $definition) { if (empty($definition)) { // 为空 return ['class' => $class]; } elseif (is_string($definition)) { // 为字符串 return ['class' => $definition]; } elseif (is_callable($definition, true) || is_object($definition)) { // 检验是否为可调用函数或者对象 return $definition; } elseif (is_array($definition)) { // 检测是否为数组 if (!isset($definition['class'])) { if (strpos($class, '\\') !== false) { $definition['class'] = $class; } else { throw new InvalidConfigException('A class definition requires a "class" member.'); } } return $definition; } throw new InvalidConfigException("Unsupported definition type for \"$class\": " . gettype($definition)); }
上述代码中已做了一些判断注释,不难发现最后需要返回的definition变量需要为数组格式,或者可调用函数与对象,注意回到刚开始的调用示例代码,definition变量分别有数组格式不带class键,
数组格式带class键,与字符串类型。到底set方法调用已完毕,从源码中分析基本上看不到反射的影子,也就是些传入参数格式兼容处理再写入类属性,接着来看下示例代码中的get方法吧。
get 方法
public function get($class, $params = [], $config = []) { if (isset($this->_singletons[$class])) { // 直接返回单例 return $this->_singletons[$class]; } elseif (!isset($this->_definitions[$class])) { // 调用bulid return $this->build($class, $params, $config); } $definition = $this->_definitions[$class]; if (is_callable($definition, true)) { // 可调用函数情况 $params = $this->resolveDependencies($this->mergeParams($class, $params)); $object = call_user_func($definition, $this, $params, $config); } elseif (is_array($definition)) { // 数组 $concrete = $definition['class']; unset($definition['class']); $config = array_merge($definition, $config); $params = $this->mergeParams($class, $params); if ($concrete === $class) { $object = $this->build($class, $params, $config); } else { $object = $this->get($concrete, $params, $config); } } elseif (is_object($definition)) { // 对象直接保存到单例属性集合中去 return $this->_singletons[$class] = $definition; } else { throw new InvalidConfigException('Unexpected object definition type: ' . gettype($definition)); } if (array_key_exists($class, $this->_singletons)) { // singleton $this->_singletons[$class] = $object; } return $object; }
上述代码,简要划分一下,请稍作浏览,后面会继续讲述,先说明属性_definitions集合中不存在的情况,即调用build,这个一会说明,再看如果存在相关class键的情况,下面会做几种情况的处理,
可调用函数情况下,调用resolveDependencies方法,再call_user_func调用函数
数组情况下,获取值与class比较,相等的情况去调用build方法,不想等重新调用get方法使用该值
为对象的化直接存储到_singletons属性集合中去,并直接返回对象,这个不作赘述
下面分别来简要分析一下上述调用的几个方法,bulid与resolveDependencies方法
bulid方法的调用逻辑
先看下build方法调用源码
protected function build($class, $params, $config) { // 声明变量分别存储getDependencies方法返回的数组 list($reflection, $dependencies) = $this->getDependencies($class); // 将params数组的数据mergy并覆盖入变量$dependencies foreach ($params as $index => $param) { $dependencies[$index] = $param; } // 调用resolveDependencies方法 $dependencies = $this->resolveDependencies($dependencies, $reflection); // 调用反射类方法,检测类是否可实例化 if (!$reflection->isInstantiable()) { throw new NotInstantiableException($reflection->name); } if (empty($config)) { // 创建一个类的新实例,变量$dependencies作为参数将传递到类的构造函数。 return $reflection->newInstanceArgs($dependencies); } $config = $this->resolveDependencies($config); // 如果变量$dependencies为空并且class是yii\base\Configurable接口的实现 if (!empty($dependencies) && $reflection->implementsInterface('yii\base\Configurable')) { // set $config as the last parameter (existing one will be overwritten) $dependencies[count($dependencies) - 1] = $config; return $reflection->newInstanceArgs($dependencies); } // 创建对象,注入参数 $object = $reflection->newInstanceArgs($dependencies); // 对象属性赋值 foreach ($config as $name => $value) { $object->$name = $value; } return $object; }
看了上述源码,也基本了解此方法是为了返回实例化对象,并调用了反射的一些接口函数,这里基本上可以知道反射的一些作用,第一个就是检测类的合法性,例如检测是否为接口实现,是否可实例化,
还有一个就是创造,上述可以看出根据反射创建类的实例,并注入构造函数依赖的参数。下面再了解下该方法里面调用的两个依赖方法,分别为开头的变量声明getDependencies与resolveDependencies
处理变量。
getDependencies方法调用
protected function getDependencies($class) { // 检测是否已存在该反射 if (isset($this->_reflections[$class])) { return [$this->_reflections[$class], $this->_dependencies[$class]]; } $dependencies = []; $reflection = new ReflectionClass($class); // 反射对应类的信息 $constructor = $reflection->getConstructor(); // 获取类的构造函数 if ($constructor !== null) { // 如果构造函数不为空,获取构造函数中的参数循环处理 foreach ($constructor->getParameters() as $param) { if (version_compare(PHP_VERSION, '5.6.0', '>=') && $param->isVariadic()) { // 检测php版本与构造参数检测是否为可变参数 break; } elseif ($param->isDefaultValueAvailable()) { // 检测参数是否是否有默认值,如果有数据保存默认值 $dependencies[] = $param->getDefaultValue(); } else { // 获取参数的类型提示符,查看是否为null,返回的是reflectClass对象 // 这里再举个例子,例如构造函数为这样__construct(Db $db);这里返回的就是Db类的反射 $c = $param->getClass(); // 创建Instance实例存储类名 $dependencies[] = Instance::of($c === null ? null : $c->getName()); } } } // 存储起来 $this->_reflections[$class] = $reflection; $this->_dependencies[$class] = $dependencies; return [$reflection, $dependencies]; }
该方法主要作用为解析依赖信息,主要是获取类的构造函数的信息,这样才能调用构造函数创建实例。
resilveDependencies方法调用
该方法主要是实例化依赖,也就是创建构造函数的参数对象,不作过多赘述
protected function resolveDependencies($dependencies, $reflection = null) { foreach ($dependencies as $index => $dependency) { // 在解析依赖信息的getDependencies中,有部分参数没有默认值,而是创建了Instance对象 // 这里会将这些Instance对象实例化对真正的构造函数的参数对象 if ($dependency instanceof Instance) { if ($dependency->id !== null) { $dependencies[$index] = $this->get($dependency->id); } elseif ($reflection !== null) { $name = $reflection->getConstructor()->getParameters()[$index]->getName(); $class = $reflection->getName(); throw new InvalidConfigException("Missing required parameter \"$name\" when instantiating \"$class\"."); } } } return $dependencies; }
总结
在上述源码中基本上可以看到几处反射的应用,而反射到底是什么,由什么作用呢?想必看完上文也会有一点点理解,嗯,其实意义如其名,
就是反射类的信息,其作用是获取类的信息,而php的反射类也提供了很多的接口函数以供使用,使用的时候可以去查询官网手册。
上文也看出来yii2框架中的di容器创建对象,在这里还是希望可以稍微讲述下刚开始的示例代码,其先在容器中注入了数据库连接类,finder类,listener类,而finder类构造函数依赖于
数据库连接类,listener类依赖与finder类,由获取依赖信息方法可以知道构造中会去取出依赖对象信息然后调用解析依赖信息重新去调用get方法返回实例化对象实现其中的注入关系。
以上就是本文的全部内容,希望对大家的学习有所帮助,更多相关内容请关注PHP中文网!
相关推荐:
Laravel开发环境部署之homestead 安装配置过程(windows系统)
以上是如何在yii2框架的di容器源码中了解反射的作用的详细内容。更多信息请关注PHP中文网其他相关文章!

热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

Video Face Swap
使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热门文章

热工具

记事本++7.3.1
好用且免费的代码编辑器

SublimeText3汉化版
中文版,非常好用

禅工作室 13.0.1
功能强大的PHP集成开发环境

Dreamweaver CS6
视觉化网页开发工具

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

热门话题

PHP 8.4 带来了多项新功能、安全性改进和性能改进,同时弃用和删除了大量功能。 本指南介绍了如何在 Ubuntu、Debian 或其衍生版本上安装 PHP 8.4 或升级到 PHP 8.4

如果您是一位经验丰富的 PHP 开发人员,您可能会感觉您已经在那里并且已经完成了。您已经开发了大量的应用程序,调试了数百万行代码,并调整了一堆脚本来实现操作

Visual Studio Code,也称为 VS Code,是一个免费的源代码编辑器 - 或集成开发环境 (IDE) - 可用于所有主要操作系统。 VS Code 拥有针对多种编程语言的大量扩展,可以轻松编写

JWT是一种基于JSON的开放标准,用于在各方之间安全地传输信息,主要用于身份验证和信息交换。1.JWT由Header、Payload和Signature三部分组成。2.JWT的工作原理包括生成JWT、验证JWT和解析Payload三个步骤。3.在PHP中使用JWT进行身份验证时,可以生成和验证JWT,并在高级用法中包含用户角色和权限信息。4.常见错误包括签名验证失败、令牌过期和Payload过大,调试技巧包括使用调试工具和日志记录。5.性能优化和最佳实践包括使用合适的签名算法、合理设置有效期、

本教程演示了如何使用PHP有效地处理XML文档。 XML(可扩展的标记语言)是一种用于人类可读性和机器解析的多功能文本标记语言。它通常用于数据存储

字符串是由字符组成的序列,包括字母、数字和符号。本教程将学习如何使用不同的方法在PHP中计算给定字符串中元音的数量。英语中的元音是a、e、i、o、u,它们可以是大写或小写。 什么是元音? 元音是代表特定语音的字母字符。英语中共有五个元音,包括大写和小写: a, e, i, o, u 示例 1 输入:字符串 = "Tutorialspoint" 输出:6 解释 字符串 "Tutorialspoint" 中的元音是 u、o、i、a、o、i。总共有 6 个元

静态绑定(static::)在PHP中实现晚期静态绑定(LSB),允许在静态上下文中引用调用类而非定义类。1)解析过程在运行时进行,2)在继承关系中向上查找调用类,3)可能带来性能开销。

PHP的魔法方法有哪些?PHP的魔法方法包括:1.\_\_construct,用于初始化对象;2.\_\_destruct,用于清理资源;3.\_\_call,处理不存在的方法调用;4.\_\_get,实现动态属性访问;5.\_\_set,实现动态属性设置。这些方法在特定情况下自动调用,提升代码的灵活性和效率。
