面试官:聊一下对框架配置加载的理解吧!

星图妙赏 2020-09-22 15:04:52

在上期聊了ThinkPHP类的自动加载,如你还不太了解可以跟这下文链接去进行查看。本文会带你一起解读ThinkPHP配置文件。

前言

想了很久终于要开始系列文章的编写了,期望是写出提升和面试都可以搞定的系列文章。

当你看到本文时,如果你发现咔咔没有编写到的面试热点问题或者技术难点,期待评论区指出,一起完善。

第一期文章:ThinkPHP自动加载Loader源码分析

第二期文章:ThinkPHP配置源码分析

一、配置文件的种类

在ThinkPHP中有四类配置文件,你知道多少呢!不知道也没关系咔咔带你在看一次。

这四种配置文件分别为惯例配置、应用配置、模块配置、动态配置。

在这里需要说明一下,一般开发中惯例配置和动态配置是不会去使用的,尤其动态配置更不会去使用。

动态配置是直接使用Config::set("设置动态配置文件")来进行设置的,所以这个东西不要去用,一定不要去用,否则项目后期怎么去管理。

关于这四类配置文件咔咔直接用一幅图来给大家展示,就不过多说明了,本文的重点不是给大家介绍这玩意。

关于ThinkPHP5.1取消了单独的config文件,将应用配置修改到config下的app.php文件中。

新增了模块配置,模块配置也是在config目录下,例如你在app目录下有两个模块,分别为index、admin这两个模块,那么就可以直接在config目录下创建admin、index目录,然后创建对应模块的配置文件即可。

这四种配置文件的优先级就是上图的排列顺序,管理配置、应用配置、模块配置、动态配置

二、学习使用ArrayAccess

在文件thinkphp/library/think/Config.php,类Config实现了一个接口为ArrayAccess。

这个时候你是不是有疑问了,这个类到底是干嘛的,为什么要去了解和学习它!带着这个疑问继续往下探寻答案吧!

到这个接口里边一探究竟。

在这个接口里边有四个接口需要实现分别为

offsetExists  检测偏移位置是否存在

offgetGet   获取一个偏移位置的值

offsetSet   设置一个偏移位置的值

offsetUnset  删除一个偏移位置的值

这几个函数放到这是不是有点懵呢!别着急,这就给你解答

这几个方法在Config中也进行实现,但是里边使用了几个方法,根据上边对方法的作用说明后。

像set、has、remove、get想想大家就知道是什么意思了。

接下来,咱们自己来实现一下这个类,给大家演示一下这个类用处到底是什么。ArrayAccess这个类不仅是TP大量使用,在laravel中也是大量存在,所以需要好好学习这个类的作用和思想。

需要在kaka目录下创建一个文件TestArrayAccess文件,并且设置一个属性,在实现ArrayAccess类。

至于我自己创建的这个文件夹kaka为什么会执行自动加载就是上一期在类的自动加载中实现的。

如若不会的话可以把文件先放置到extend目录下即可。

接着来到application/index/controller/Index.php控制器使用上图实现的方法

打印结果

这里在使用offsetGet打印的结果为kaka应该都明白怎么回事了吧!其余俩个方法就不去演示了,相信你也已经明白了。

所以说这个ArrayAccess类就是提供像访问数组一样访问对象的接口。

三、了解Yaconf

估计有同学知道Yaconf就是我们牛逼克拉斯的鸟哥写的。

咔咔了解Yaconf后,总结推出这个开源的几点原因。

配置文件过多,导致加载时间过长

配置文件可读性差,需要运行解析

配置文件与代码同属一个项目部署在一起会有安全隐患,同时如果配置文件修改时还需要走上线流程

加大运维与开发协同难度,如果运维需要修改MySQL或者其它配置也需要通知开发进行同步修改

那么在来说一下使用Yaconf的优点

不跟代码在一起,使用专属的配置目录

PHP启动时,加载完所有的配置,进行常驻内存,伴随着PHP的生命周期存亡,避免每次请求时解析配置文件,消耗时间

配置文件跟代码分离,就可以借助配置后台管理来统一管理配置信息

配置文件如有发生变化时会进行重载(这里给的建议是使用mv而非cp)

支持类型丰富,例如:字符串、数组、分节、并且还可以在PHP配置文件里边直接使用PHP的环境变量和常量

最后一点就是使用非常简单

在了解了Yaconf可以做什么的时候之后,在去开始下一步的操作,别一上来就是安装、配置、调用,然后一顿操作结束后,不知道是干嘛的,那样就没有任何意义了。

接下来将会介绍在win、linux上安装并且使用

四、Yaconf在window上安装

点击后边的DLL,选择对应的版本

选择适用的版本

如何判断什么版本适合自己,对照着我圈起来的几个地方去下载就对了

然后将这个文件放置到PHP的扩展目录里边

修改配置文件

extension=php_yaconf.dll[yaconf]yaconf.directory="D:\phpstudy_pro\WWW\yaconf"yaconf.check_delay=60

重启环境,然后查看一下就ok了。

这里需要注意一点就是在复制的时候难免会有一些空格什么乱七八糟的东西,切记删除干净,否则你会知道什么叫难以启齿的柔弱。

五、Yaconf在window上使用

此时就直接在Yaconf的目录中创建一个ini文件,并且写上如下内容

控制器直接读取

经过打印出来的结果也是预期的值

文件如有改动,则直接重启ngixn即可

Yaconf提供的另外的一个接口为\Yaconf::has

以上就是Yaconf在window上的使用,使用起来还是相对简单的。

这里有一个注意点就是修改了Yaconf的配置文件之后我们需要重启web服务器。

六、Config源码剖析

虽说上面的Yaconf对于config的源码解析没有多大的帮助,但是也是在扩宽一下思路,以后在工作中可以尝试使用Yaconf。

进入正题,想知道Config是怎么进行加载解析的,先来画一个图。一起看一下加载config的执行流程。

回到public/index.php,在上一期说了类的自动加载是在加载base.php文件的过程中执行的。

那么config的加载是在下图框起来的这里,这里涉及到了容器,会有一个单独的专题来对容器进行剖析。

这里就不过多说明了,这段代码回去执行`D:\phpstudy_pro\WWW\ThinkPHPSourceCodeAnalysis\thinkphp\library\think\App.php这个文件的run方法。

并且在run方法中需要追踪的是initialize应用初始化这个函数

接着在initialize这个方法中就会看到配置文件的蛛丝马迹,随之而来就是一个初始化应用init

直到走到init方法中,才算是进入了主题。

开始了自动加载配置文件,并且还调用了config类中的load方法,也是需要一起阅读的。

来到这里之后需要简单的进行解读一下

这段代码会直接走到elseif中,因为在app目录下没有设置config目录

并且这里有个configPath这个属性熟悉吧!这个值最终就是D:\phpstudy_pro\WWW\ThinkPHPSourceCodeAnalysis\config\

在接这就是把config目录下的文件全部拿出来。

传递给config类的load方法

在这里有几个知识点提一下,就当回顾了

scandir  :以升序的方式返回一个目录下的所有文件,还有第二个参数1,表示降序的方式返回一个目录下的所有文件。

pathinfo:以数组的形式返回文件信息,分别为目录名、文件名、扩展名,其中的几个参数代码中有提到,可以看下图即可。

在这段代码中有一个属性configExt,这个值是在环境变量读出来的,给的值是php

紧接着就需要来到thinkphp/library/think/Config.php这个文件了,在app文件中最后调用了config类中的load。

一起来看看都经历了什么

在load这个方法中,流程的最终走向会到loadFile这个方法中,至于elseif的代码为什么不会走,或者说这段代码就是多余的。

因为当Yaconf安装后在PHP启动后就会直接去加载对应的配置文件

来到loadFile这个文件后

我们都知道在ThinkPHP框架中,config目录下的所有文件都是PHP类型的

所以在判断了类型后就直接进入到set里边进行数据的处理

include直接引入的就是config目录下的配置文件,并且所有的配置文件都是直接return返回一个数组

在set方法中,这块代码就是核心了

一直循环合并数组,最终把所有的配置信息都返回给了config这个属性

截止到这里config目录下的所有配置就加载完成了。

以上就是config的加载流程,其实当你阅读完之后感觉也没有那么的难,就是编码技巧和思想。

而我们阅读源码不是看它代码怎么写的,是学习的它的编码思想,最终落地到我们自己的项目中。

七、工厂模式加载其它类型的配置文件

在之前看到的loadFile方法中,文件类型为PHP或者yaml程序就打断了,就不会在去执行后边的pares方法。

那么这个parse方法是做什么的呢!

进入到paras这个方法后,首先看注释

随后使用了一个工厂模式去加载think/config/driver下的文件

进入到factory这个方法后,这块内容属于容器的就不过多解释了。

只需要知道这里最终会返回一个实例给paras方法的$object变量

最终还是使用在上文中提到的set方法,而内部的object->parse()就是执行返回对象的内部方法,例如下图展示三个类型。ini、json、xm三个类型都存在同样的方法

这是ini类型,其它俩个的类型也是一毛一样的,就是会有同样的方法来实现对应的功能

简单梳理一下工厂模式加载不同类型的配置

把对应的类型传给一个方法

然后这个方法返回对应的实例

在用这个实例去调用类里边的方法

所有实例里的方法名都是一致的

这里咔咔后期会单独出一篇文章模仿这个实现另一个功能,导图会有所有的文章链接哦!

在ini.php中看到了一个方法parse_ini_file,其实这个方法根据之前的学习就大概能了解到时把ini类型的配置文件转为数组形式。这里就不做演示了,很是简单哈!

那么其它俩种类型都是为了把文件数据转为数组的。

八、yaml初体验

yaml就是一个类似于xml、json数据通信方式,但是yaml是以数据为中心,而非标记语言为重点。

这里提到yaml是因为在框架源码中提到了这个,后边的流程也是需要走yaml,所以还是需要简单的了解一下哈!

安装yaml,直接到pcel里搜索yaml,下载对应的版本即可。下载方式跟之前yaconf安装一样的。

安装成功后就会在PHP中存在这个扩展。

这里需要注意的是需要在php.ini中把yaml扩展文件加进去哈!

如果不会就去Yaconf在window上安装那一栏去看是怎么安装的,安装那个流程就可以了。

安装完成后就是简单的使用了,在config中新建一个kaka.yaml文件。

并且写上如下的内容,切记在yaml中冒号后边需要空一格,yaml写法就不过多说了,毕竟平时不怎么用。

这里说明只是为了阅读框架代码而做的工作。

测试yaml数据读取

读取出来的数据就是一个数组,也就是说把yaml格式的数据转化为数组形式。

这个方法是从哪里知道的呢!

还记得在config类下loadFile方法中,根据文件扩展的不同加载不同形式的方法。

PHP类型的直接就走了set,yaml类型的把数据处理为数组后执行set方法

如果是其它类型的话就会在上边说的工厂模式返回对应类名的实例,并执行对应类里边的方法将格式都转化为数组形式。最终还是使用set方法

其它类型的配置文件最终都会走到set方法里边。

九、如何让框架加载其它类型的配置文件

在初始化应用和模块中有一个属性是configExt,这个属性就是文件的扩展

去找一下这个属性是在哪里设置的。

根据config配置文件加载流程,可以很清晰的看到init方法的上一层是初始化应用,也就是initialize方法。

那么这个属性肯定是在init方法之前就已经提前设定好了的。

返回到init方法的上一层initialize就直接可以看到这个值的设定。

这个值是从env的环境变量中获取的,如果没有则默认为php,所以就需要创建一个env的文件。

并且给一个默认值为yaml

这里给大家看一下变化,在5.1.34 LTS版本的时候存在一个bug,咔咔目前使用的是5.1.39 LTS  这个问题已经修复了。

这段代码相信都可以看出来,它是先对configExt进行了设置默认值,然后在去加载环境变量配置文件。

那么加载这个环境变量的这段代码就毫无任何意义,configExt的值永远都是.php

就算在env文件里边做了配置也不会获取得到。

在之前在config配置文件中添加了一个yaml的配置文件

那么这个时候就可以使用config类来获取yaml类型文件的配置了。

测试一下没有任何问题,数据是可以出来的。

但是实际项目中可不敢这么整啊!如果要把configExt这个值配置到环境变量,配置的是什么类型就需要把config目录下的所有配置文件全部转化为对应类型。

例如configExt设置的为.ini ,则就需要把config目录下的所有文件都改为ini文件形式

这块内容只是针对阅读源码后一个运用而已,实际项目中不要这样使用,因为在tp框架中所有的配置文件都是PHP类型的。

如果改为其它类型的话,就需要修改框架中所有的配置文件,这种事情能不干就不哈!

十、框架底层配置加载代码优化

在thinkphp/library/think/Config.php中方法loadFile这里看起来是不是有点不太优雅了。

既然在方法最后使用parse方法,也就是之前提到的工厂模式加载其它类型的配置文件。

那么为何不让这个工厂模式也加载PHP和yaml类型的配置文件。

此时就需要在thinkphp/library/think/config/driver这个目录建立php文件和yaml文件了。

首先建立一个php文件。

并且仿照其它三个类型文件,在php文件类型中只需要判断是否为文件,然后把文件引入进来即可。

在框架中PHP类型的配置文件都是数组形式的,所以在Php.php文件中parse方法直接返回config属性即可。

开始开心的测试吧!之前在env的文件中配置了CONFIG_EXT这个值为yaml。

也就是说读取config目录下的配置文件时,只能读取扩展为yaml类型的文件。

所以需要先把这个值给改回来,等后面把yaml类型的工厂类写好之后就可以使用了。

为了测试方便在config目录下添加一个新的配置

然后把config类中的lodeFile方法中判断php和yaml类型的代码注释掉

在控制器读取config/app.php配置

打印结果

打印出来的结果没有任何瑕疵,也就说我们进行简单优化的代码并没有不适之处。

至于yaml也是一样的道理,只需要把最终的数据转为数组返回就行了。

以上就是咔咔对框架配置文件加载底层源码优化的解析过程,如有不适之处,可以评论区指出来。

十一、解析如何获取config如何获取配置

都知道在获取配置信息的时候直接使用\Config::get()就可以获取到配置文件的信息。

接下来咔咔就来剖析一下获取配置的流程。

框架给提供了几个方法来获取配置信息。

\Config::get('配置参数');

\Config::get('配置文件');

\Config::pull('配置文件');

这其中估计使用第一种的就很少了,第一种的方式就是直接获取所有配置文件中的对应的配置。

例如:想获取config目录下的应用名称配置

就可以直接用\Config::get('app_name');来直接获取

那么这个流程是怎么样的呢!

当直接获取配置参数时,走的代码流程就只有这俩个。

第一段是给加上前缀app

第二段是循环在config文件中获取数据。

这段代码如果你直接断点调试的话是看不到什么效果的,咔咔把这段代码给大家移到外面去执行,就会看的很清楚了。

咔咔将这段代码给移到了index控制器中,这样就可以看到的很清晰了

先看打印结果,确认没啥问题

其实这里的代码如果放在源码中执行你会看到很多其它的信息,会很影响信息的解读的。

但是咱们移植出来后,就可以确保代码的运行时没有其它的杂乱信息,有利于对信息的正确解读。

然后紧接着看这段代码,这段代码之前咔咔看的时候感觉没什么,但是越看你会越发现这块代码的设计很是优秀。

为什么会这样说呢!

首先这段代码会走第一次循环就是执行app,这次执行会在全部的config中获取出键值为app的配置信息。

然后把值再次赋值给config变量,执行第二次循环为app_name。

这里循环获取数据就是在第一次循环获取数据的基础上得到的。也就是第二次是在$config['app']下获取的数据。

由此可见这段代码设计的是多好啊!

至于其它俩个方法就交给你们了,可以简单的试着跟着咔咔一样把代码移植出来,然后一步一步的解读。

你就会发现代码的优美之处,看的多了,对于以后自己写代码也会提供很多的思路的。

十二、总结

对于框架中config源码的解析就到这里结束了,其实源码的解析并不是很多,而是用了大量的篇幅来介绍了间接使用的一些技术。

虽说这些技术在这个已经成型的框架中不能再进行好好的利用,但是最起码让我们知道了他们每一个扩展的作用。

例如Yaconf对于项目配置这块会有很大的帮助,可以让配置文件跟项目分离,确保项目安全和跟运维之间的协同。

在例如开篇说的ArrayAccess,这个就是提供像访问数组一样访问对象的接口而已,这个也就是一种好的思想,同理在以后得开发中也可以借鉴这种思想。

在配置文件这一篇中,咔咔认为最重要的就是使用工厂模式加载的不同类型配置文件,在这一节中咔咔也说了后期会在出一篇文章在进行解析的,这一节点的文章如果没事的话真的可以好好的阅读一下。

这个也是目前在阅读源码的过程中直接碰到的第一个设计模式,后边会遇到越来越多的设计模式,遇到在进行解析

之前跟着咔咔一起实现的优化框架源码的过程中,这个配置一定要改过来,否则你需要把框架所有的配置类型都需要改为对应的。

坚持学习、坚持写博、坚持分享是咔咔从业以来一直所秉持的信念。希望在偌大互联网中咔咔的文章能带给你一丝丝帮助。我是咔咔,下期见。

0 阅读:328