在阅读这篇文章以前,我但愿您对 Laravel 的容器具备必定的使用和了解,若是不熟悉的话,请阅读Laravel 容器,这方面的知识对于理解我今天要讲的东西很是有必要,再次提醒一下各位,这篇博文容量很大,仔细体会消化,但愿能有所收获。php
若是你使用过第三方的 composer 包,它会提醒你,把它的 ServiceProvider 和 Facade 写入到 config/app.php 文件中,以下:laravel
固然了,注册 Facade 和 ServiceProvider 不止这么一种方式,你感兴趣的话,能够看看官方的文档有很清楚的描述,开发第三方 Laravel 包。面试
这些东西都没啥可说的,若是我就说这些,也许兄弟们会说,我都知道,你还说个啥?也确实,若是就说这个,我也很差意思了,但是,后面的内容就不那么容易了。sql
为了给兄弟们讲 Facade,我仍是大体的给你们讲解一下 Laravel 的引导过程(只关注与 Facade 有关的部分),你们都知道 Laravel 的入口文件为 public/index.php,下面的这行代码很关键。shell
那么这个文件干啥了呢?如今咱们只关心下面这两行:json
上面建立了一个 Application 类的对象,这个类表明了咱们当前的应用程序,也是整个 Laravel 最为核心的类,注意了 Application 类继承自 Container 类(这个类就是 Laravel 容器的核心),因此咱们能够在 Application 类的对象上操做容器的方法,这就是为啥我在这篇博文的开头,提醒你们须要必定的容器的知识。bootstrap
上面的这段代码,向容器中添加了一个单例,因此当咱们建立 Illuminate\Contracts\Http\Kernel::class 对象的时候,其实是建立的 App\Http\Kernel::class 类的对象,这一点极其重要,但愿你们必定要记住,这在后面会使用到。数组
回到 index.php 文件中,继续看下面这行代码:服务器
Illuminate\Contracts\Http\Kernel::class 指向的是谁啊?不就是咱们上面说的 App\Http\Kernel::class,这个文件的位置以下:架构
这个 Kernel 就是咱们的目标了,咱们打开看一下这个文件:
这个 Kernel 类继承自了 Illuminate\Foundation\Http\Kernel 类,兄弟们记住这一点,后面会使用到,下面咱们回到 index.php 文件中,laravel 调用:
$kernel 的 handle 方法被调用,经过上面的分析,咱们知道这个 handle 方法属于 Illuminate\Foundation\Http\Kernel 类的,对于咱们分析 Facade 来讲,只有一行代码是关键的,就是下面:
咱们看一下这个方法 sendRequestThroughRouter,咱们没必要关心它的参数 $request 是啥,它对咱们分析当前的目标一点儿关系都没有,这个函数中也只有一行是咱们关心的,以下:
bootstrap 方法很简单,以下:
这里首先调用 bootstrappers 方法,这个方法的返回值是一个数组,它的内容以下:
上面这个咱们只须要关心 RegisterFacades 类,回到 bootstrap 方法中,它调用 app->bootstrapWith 方法,这里的 app 是谁呢?他就是 Application 类的对象,这个对象在整个 Laravel 的生命周期中是惟一的,由于他是单例的,既然知道这个了,咱们看 Application 对象的 bootstrapWith 方法。
还记得咱们的 Application 类继承自 Container 类么?因此它可使用 make 方法,上面说了,咱们当前只关心 RegisterFacades 这个 bootstrapper,因此,咱们进入到这个类的 bootstrap 方法:
由于这篇博文主要给你们讲解 Facade 的整个实现的,因此我会忽略掉一些细节,关于这些细节,之后我会给你们讲解,可是在这里我会先说明他们的做用。上面这张图,我已经标注了序号,序号 1 这行代码返回了 config/app.php 文件的 aliases 字段值,咱们本身的 Facade 就注册在了这个地方,在这篇博文的开头,我已经给你们说过了。序号 2 的做用是干啥呢?还记得我开始说的么?当咱们开发 laravel 包的时候,可让 laravel 自动加载咱们的 ServiceProvider 和 Facade,咱们所要作的就是在咱们的 composer.json 中,加入下面的这段:
上面这个截图,你们应该能够看的很清楚,我就再也不详述了,PackageManifest 类的做用就是负责自动加载咱们在 composer.json 文件中的 ServiceProvider 和 Facade。这么说你们应该明白了吧。
回到 RegisterFacades 类的 bootstrap 方法中,array_merge 方法合并 1 和 2 的 Facade,并把它传递给 AliasLoader 的 getInstance 静态方法。
这个 getInstance 方法返回了一个 AliasLoader 类的对象,下面咱们看它的 register 方法:
这个方法很简单,直接调用方法 prependToLoaderStack,以下:
spl_autoload_register 这个方法可能不少人不知道,由于如今都是使用成熟的框架了,简单来讲,它的做用就是负责加载咱们的类文件的,你有没有好奇过,php 是如何找到并加载咱们的 php 类文件的,这当中的功臣就是 spl_autoload_register 了,若是你不知道它,请参考 php 的官方文档spl_autoload_register,它的第一个参数是一个回调方法,做用就是负责加载类文件的,咱们的程序中能够屡次调用 spl_autoload_register 方法,也就是说能够注册多个加载函数,关于 spl_autoload_register 的介绍就这么多了,回到当前的代码中,laravel 注册的自动加载函数为 AliasLoader 对象的 load 方法,咱们看哈:
这个方法咱们只须要看标注出来的部分,aliases 属性存储着以前解析的全部的 Facade,部分截图以下:
之因此我会把部分标出来,是由于我后面会用到,上面的代码中使用到了 class_alias 方法,这个方法是给一个类取个别名,好比说对于 Illuminate\Support\Facades\Route::class 这个类,它的别名为 Route,为了证明这一点,咱们来测试一下,在咱们的路由文件中,咱们常常这么作:
注意了咱们并无引入 Route 这么一个东西,可是为啥 php 没报错呢?这就是咱们上面给 Illuminate\Support\Facades\Route::class 取了 Route 这个别名的缘由,你能够把 class_alias 这段代码删除掉,确定会报错的:
你再刷新一下页面,页面报错了,哈哈,就是这么刺激:
上面分析了 Laravel 的整个 Facade 注册的过程,是否是有点儿懵?不要慌,后面还有,任重而道远啊。
在 Facade 注册一节中,咱们标注了 Route 这个 Facade,因此这一节,就以它为例来进行讲解,Route 类以下:
全部的 Facade 都继承自 Illuminate\Support\Facades\Facade 类,而且都必须实现 getFacadeAccessor 这个方法,否则会抛出异常的,咱们看 Route 的 getFacadeAccessor 方法以下,他返回字符串”router”,至于它的做用,咱们后面会讲到:
为了给你们讲解后面的问题,我写了一个很简单的例子,以下:
在路由文件中,我用 Route 注册了一个路由,这里调用了 get 方法,可是咱们打开 Illuminate\Support\Facades\Facade\Route 类,这个方法是不存在的,它的父类也没有,然而咱们注意到了 Illuminate\Support\Facades\Facade 类实现了__callStatic 方法,以下:
callStatic 简单来讲就是若是你调用某个类的静态方法,可是这个静态方法不存在的话,就会调用这个类的 callStatic 方法,若是你仍是不清楚,能够网上查阅相关资料,这里再也不阐述。好了,废话很少说了,咱们回到 Facade 的__callStatic 方法中,这个方法首先调用 getFacadeRoot 方法,以下:
看到没,这里就是我上面说的 Facade 为啥必须实现 getFacadeAccessor 方法,在当前的实例中,它返回的是 “router“.。
resolveFacadeInstance 方法是啥呢?很简单,可是我仍是准备贴出来:
由于 Illuminate\Support\Facades\Facade 类是全部的 Facade 的父类,因此任何的 Facade 调用静态方法,都会进入到这个方法中,静态属性 $resolvedInstance 存储着当前全部被解析的 Facade 对应的实例对象,你要记住任何的 Facade 后面都有一个对象的,并且这个对象在整个 Laravel 程序的生命周期中是惟一的,只有这么一个实例,上面标注的 1 首先检查以前是否已经解析过这么一个对象,若是解析过了,直接返回就是了,这是单例的常见手法。若是以前没有解析过的话,那么代码就会走到 2 整个地方了,咱们知道 $app 就是全局惟一的 Application 类对象,它继承了 Illuminate\Container\Container,而 Illuminate\Container\Container 又实现了接口 ArrayAccess,对于 ArrayAccess 接口不熟悉的同窗,能够查阅相关的资料,简单来讲,若是你的类实现了 ArrayAccess 接口,那么你就能够像获取数组元素同样,获取对象的内容而不会出错。
这个接口有几个方法必须实现,offsetGet 方法是其中之一,当你采用数组的写法做用在对象上时,offsetGet 会被调用,咱们看 Illuminate\Container\Container 类的 offsetGet 方法,以下
在当前状况下咱们获取的是 $app \[‘router’\],因此这里的参数 $key 就是”router”,关于容器的 make 方法,请你们参考文档,很是简单:
make 是容器暴露给咱们获取容器注册内容的少有几个方法,好了,如今咱们的疑问是咱们何时注册了一个”router” 这么一个东西,你们若是使用的是 phpstorm 的话,能够这么作:
搜索内容为”router”,以下:
经过搜索咱们知道,在 Illuminate\Routing\RoutingServiceProvider 这个类中,注册了 router 的单例,你可能会问,这段代码是啥时候调用的,也就是 registerRouter 方法是啥时候被调用的,当前的 RoutingServiceProvider 中的 register 方法以下:
那么 register 方法是怎么被调用的呢?不知道不要紧,我细细道来,在以前的 laravel 框架引导过程当中,建立 Application 类实例的时候,它的构造函数以下:
registerBaseServiceProviders 方法以下:
看见没,这个地方出现了 RoutingServiceProvider 类对象,咱们再进入到 Application 类的 register 方法中,以下:
啊哈,register 方法被调用了,这个时候名为 router 的单例就被注册了,分析了这些,咱们回到 RoutingServiceProvider 类的 registerRouter 方法中。
这里直接返回了 Illuminate\Routing\Router 类的实例,这个实际上就是 Laravel 全局惟一的路由器对象,路由就是靠它来实现的,分析了这些,咱们再一次回到 Illuminate\Support\Facades\Facade 类的 resolveFacadeInstance 方法中。
这里把解析的实例存储到 $resolvedInstance 属性中,这样下次就不须要解析了,resolveFacadeInstance 方法调用完毕以后,返回到 Facade 的 getFacadeRoot 方法中。
上面也是直接返回刚才获取到的对象 Router 实例对象,getFacadeRoot 方法调用完毕以后,继续返回到__callStatic 方法中。
红色的代码就是翻译一下就是:
$router->get('/',function () { echo "Hello World"; })
使人欣喜的是奇迹出现了,Illuminate\Routing\Router 类有以下的代码:
Laravel 的源代码错综复杂,理解起来不是那么容易,上面给你们标注出了主要的脉络,但愿你们仔细体会和理解。
更多学习内容能够访问【对标大厂】精品PHP架构师教程目录大全,只要你能看完保证薪资上升一个台阶(持续更新)
以上内容但愿帮助到你们,不少PHPer在进阶的时候总会遇到一些问题和瓶颈,业务代码写多了没有方向感,不知道该从那里入手去提高,对此我整理了一些资料,包括但不限于:分布式架构、高可扩展、高性能、高并发、服务器性能调优、TP6,laravel,YII2,Redis,Swoole、Swoft、Kafka、Mysql优化、shell脚本、Docker、微服务、Nginx等多个知识点高级进阶干货须要的能够免费分享给你们,须要的能够点击下方连接领取进阶PHP月薪30k>>>架构师成长路线【视频、面试文档免费获取】