本文主要描述了门面的使用和实现过程以及源码的深度解析。php
@TOCthinkphp
前言
使用框架的伙伴应该都知道在5.1时框架新增了一个特性那就是本文将编写的门面,也就是facade这个特性。数组
使用过这个特性的都明白其中的好处,那就是方法调用能够直接静态进行调用,不用再使用关键字static来定义。app
接下来咔咔将会从如下几个方面带着你们探索属于门面的故事。框架
1、简单认识一下在框架中的门面的好处
在以前有写过配置文件加载一文,在那一文中的最后提到过配置信息获取的几种方式。函数
其中有一种方式就是Config::get(),到这篇文章应该都知道使用Config获取配置信息时,必须先得引入use think\facade\Config
,又由于在系统中注册了别名,因此直接使用use Config
便可。工具
虽然说咱们使用的是use think\facade\Config
,可是实际调用的方法倒是thinkphp/library/think/Facade.php
中的__callStatic
方法。学习
而后会执行同文件的createFacade
方法。测试
虽然说如今尚未看源码,看着知道就行了,在调用createFacade
方法时是直接从容器类里边获取的。优化
在学习容器时咱们都知道容器是使用了注册树模式,须要使用对应对象实例的时候就能够直接获取,这样就避免了一个类反复的建立。这就是其中的一个优势。利用容器的特性
对于之前使用config来讲,须要使用config的命名空间,而后进行实例化才能进行调用。
若是此时config不让使用了,须要使用本身建立的config类,若是没有使用门面模式,就须要修改大量的代码,并且是全局的。
可是若是使用了框架中的facade门面模式以后,你就只须要重写getFacadeClass
这个方法便可,只须要改变里边的返回结果诶本身定义的便可,由于对于其它文件调用的地方它们不关心实例调用的是什么,只关心方法名和返回结果。
2、学习框架中facade的使用
首先建立一个控制器Facade,而且写上如下内容。
这里只是简单的使用门面的方式来获取配置文件信息。
在这里能够看到使用的是
use Config
,这个就是config类的别名。
别名的设置是在base.php
中设置的。
在框架中如何正确的使用facade呢!
在app目录下新建一个文件夹facade,用来专门存放门面类。
这里建立了一个Sessions的类。
先作一个测试,检测代码是否写的有问题。在控制器的facade文件中进行测试。
这就是没有使用门面时的处理方式,须要引入对应的类,而后进行实例化,在用实例化的类进行方法调用。
打印结果,结果就是咱们预期的结果。
那么这份代码怎么改成门面模式呢!跟着咔咔的脚步一步一步的来。
先在kaka目录下创建俩个目录,分别为facade和util
为何要创建这俩个文件夹呢!util你们应该都知道那就是工具类,这种类文件是能够在其它项目中公用的。
也就是说咱们只须要实现一份而后在其它项目中用的时候直接拿过去就能够了。
因此就能够直接把文件复制到util目录下,记得修改命名空间便可。
而后在到facade目录下新建一个Sessions的类,而且继承Facade。而后写上一下内容。
此时咱们在来到控制器进行测试一下。
会发现结果跟以前是一致的,可是很明显的一个区别就是使用了facade模式后,能够直接使用静态方式进行调用。
还记得在以前说过门面的一个好处吗?
假设这个Sessions工具类在将来的一天中止使用了,那么咱们只须要修改getFacadeClass
方法里边的内容便可。
3、优化在框架中facade的使用
在上文中咱们从实例化类到使用门面方式实现了同一个功能。
虽然说想要实现的效果显示出来了,可是代码仍是不够简洁优美的,结构也比较混乱。
接下来咔咔会给你们提供一个可行的方案,若是你有其它的方案能够提出来哈!评论区见。
在正常开发工做中自定义的门面类不可能只有一个或者几个,在复杂的项目中门面类会有不少。
既然多,那就须要进行管理。
首先创建一个属于门面的配置类。
并将代理类和实际类对应起来,而后设置别名。
此时须要创建一个钩子文件,将门面类注册和门面类别名注册都放在里边进行执行。
还有最后一步,那就是钩子文件虽然建立了可是没有执行。
那么钩子文件应该何时执行呢!那就是在应用初始化的时候进行加载。
在TP5.1中应用初始化的配置是在application/tags.php
这个文件中。
在应用初始化的配置项里把钩子文件配置进去便可。
测试
最后一步就是测试了,依然是执行application/index/controller/Facade.php
文件中的getUserInfo
方法。
根据测试结果能够得知咱们的方案代码编写没有问题。
这里有没有发现一个问题,就是既然在钩子中定义了门面类的别名了,可是在这里并无使用。
接下来咱们使用别名来测试一下。
4、门面类源码解析
在解析源码以前先认识俩个方法。
__callStatic
:当访问不存在的静态方法时,会调用此方法。call_user_func_array
:能够直接用此函数来直接调用函数。
咱们就从获取配置文件开始解析
执行
Config::get('facade.');
会执行到文件thinkphp/library/think/facade/Config.php
中。
在这个文件中就是以前说的,若是存在getFacadeClass
方法就会直接返回对应的别名。
若是不存在的话就须要使用bind方法来进行门面绑定。
这里若是不明白就须要去文档好好看看门面那一章节哈!
在上边类中是不存在get方法的,因此就会直接调用
thinkphp/library/think/Facade.php
文件中的__callStatic
方法。
这个方法就是文章开头就直接说明的,访问不存在的静态的方法时则会调用此方法。
接着就会执行本类中的
createFacade
这个方法
在这个方法里边有一行代码是这个样子的$facadeClass = static::getFacadeClass();
这段代码会在下文作详细的说明。
由于在子类中也有一样的方法,在本类中也有一样的方法可是在本类中的方法是没有任何返回值的。
这时你有没有一丝丝的困惑,这里使用的static到底会执行哪里的方法。或者这样想,为何会执行子类的方法。
保留这些疑问将会在下文给你细细的讲来,先来把门面类的源码看完。
在这个方法中主要看我圈起来的几个地方。
第一处就是从子类的getFacadeClass
方法获取类的别名。
第二处是当子类没有getFacadeClass
方法时,从手动绑定的属性中获取。
第三处就是以前文章提到的容器了,这里就不对这里作详细说明了,若是不会的点开主页去看以前的文章。
5、static关键字
在这里不得不说明一下static这个关键字。
新学习的伙伴估计只能知道static是用来定义静态变量和静态方法的。
固然这里不会去给你们说怎么定义静态方法和静态变量,而是说一个很是很是小的细节点。
先看一个实例,这个实例也是在阅读门面源码时,咔咔根据门面源码改编过来的。
咔咔这里新建了俩个文件,分别为test和test1。
test继承test1文件,而且都有一样的方法getKaka。
test的源码
test1源码
控制器进行调用
打印结果
这个时候有没有一点点疑惑,这里怎么打印出来的是
147
,而不是456
呢!
修改test1的代码,把static改成self
打印结果
使用self的代码相信你们都看的明白,那为何使用static就出现了有可能不太明白的结果呢!
此时就是文档开始起做用了,可是当你打开PHP文档会发现,在static这一篇中并无对这类状况做出说明。
通过咔咔屡次测试和查阅资料,最终总结结果以下。
static::$test 若是有被继承的话 默认调用子类 ,不然调用的是自身
self::$test 若是有被继承的话,默认调用本类
放在本实例中来讲明就是,当test继承test1时。
在test1中使用static调用方法getKaka
时,默认调用的是test类中的getKaka
,也就是子类的方法。
在test1中使用self调用方法getKaka
时,默认调用的是test1类中的getKaka
,也就是本类的方法。
这个小小的细节也是咔咔无心中发现的,若是有什么不对的地方能够提出来,咔咔做出修改。
由于在继承这方面还有另一种状况,咔咔私下会进行测试,在这里就不说明了。
这里对这个static作出解释主要是为了解释thinkphp/library/think/Facade.php
文件中这个行代码。
由于这行代码调用的方法在子类和父类都存在,因此咔咔为了避免让你们出现迷惑就写出来作一个简单的介绍。
6、总结
先来一份门面流程图,能够更清晰的看到门面类的具体执行流程。
门面类的源码非常简单,除了几个不太常见的知识点,代码相信都看的明白。
这里主要是对阅读完门面类后,作一个小总结。
门面类主要是结合了容器来实现的一个功能,由于须要容器来返回对应的实例,关于容器的文章也已经完成了,若是对容器有不会的能够在文章的开头去看对应的文章便可。
本文中给你们介绍了门面在容器中如何使用,而且给你们提供了最优的使用方式,这里的最优是咔咔我的看法,由于这种方式咔咔使用了接近俩年了。
不管从代码的健壮性仍是扩展性都是很是实用的。
再就是关于static关键字,给你们作的一点点冷门知识得补充,当一个类继承一个类时,在父类实用static关键字时,默认调用的子类的方法。
这里的总结只是针对于本文的实例。
其实在这里咔咔还想给你们说明一个点就是return call_user_func_array([static::createFacade(), $method], $params);
由于在之前的用法的哥参数就直接是方法,可是在这里碰到了数组形式,那么这个数组中的俩个值都表明的是什么呢!
第一个值为实例,第二个值为实例中的方法。
关于call_user_func_array
这个方法的使用咔咔就不去作案例给你们看了,只须要知道它会去执行传入得方法便可。
到这里关于门面的源码解析就结束了,最重要的仍是理解容器,由于门面就是在容器的基础上实现的,这也就是咔咔先写容器在写门面的缘由。
还有就是关于门面的使用咔咔也给出了方案,如你有更好的方案能够在评论区给一个大概的思路。
坚持学习、坚持写博、坚持分享是咔咔从业以来一直所秉持的信念。但愿在偌大互联网中咔咔的文章能带给你一丝丝帮助。我是咔咔,下期见。