衷心感谢改版期间大家给予的帮助和体谅

微信登录

微信登录

因恶意注册过多,目前只支持微信模式

2.1 Laravel 生命周期和网络请求生命周期还真不是一回事儿

人呐,就是很奇怪,我们都知道 Laravel 源代码蕴含的巨大价值,但是很多使用 Laravel 或者 PHP 作为吃饭工具的码农们以及学习者,很少有人真正的去研究一下 Laravel 源码,说到原因呢,无非就是 “懒” 和 “恐惧”,看似“懒”是主要原因,仔细想想的话,似乎不是那么回事儿, “恐惧” 才是,人如果无法克服自己的心里障碍,就很难行动起来,而 “懒” 通常只会导致拖延而已,一件事情如果你拖了太久,就不再只是简单的 “懒” 的问题了。“舒适区” 很舒服,但是舒适区面积会随着时间加速缩小,我们又能舒服多久。好了,不废话了,咱们开始看看代码吧,真的没多少,也没必要了解得太具体,太八股了也不好。

laravel request and response

上回书咱们跟着网络请求已经进入到了 public\index.php 入口文件,入口文件中虽然只有 10 来行代码,但是把流程却交代得很清楚,作者作为一个强迫症,代码写的一丝不苟,注释的缩进都一模一样,光是这一点,没有多少人能做到,作者如此用心良苦,减轻了学习者很多痛苦。用户的网络请求就是传递给 Laravel 应用创建的 HTTP 内核 进行网络请求处理,并把处理后的网络响应结果返回给用户的。网络请求就是在第三步被 HTTP $Kernal 对象的 handle 函数接收并完成处理的,handle函数接收的参数就是用户传递过来的网络请求 由于上述 $kernel->handle() 是如此的重要,我们真的需要先跟它打打招呼交个朋友才行,那咱们就先走个回头路,看一看 网络请求 传递给 Laravel HTTP Kernel 之前,Laravel 都做了什么,虽然这部分内容不是网络请求生命周期走的流程,但是却是 Laravel 生命诞生的地方,咱们这次的专题是 Laravel 的生命周期,所以这部分内容还是要提一提的,到了这里很多朋友应该可以开始解开疑惑了,是的,我们这个专题其实涉及到了两个生命周期,一个是 网络请求的生命周期,一个就是 Laravel 生命周期。由于这两口子水乳交融,夫唱妇随,大部分时间他们都抱在一块儿羞羞,导致很多人把它当作一个东西了,而这也是为什么很多人会觉得网上的教程有些地方总觉得不对劲的原因。从上图中,咱们已经可以看出来,网络响应(Response)已经在第三步就已经返回给用户了。网络响应 返回后,Laravel 的生命周期并未结束,它还有一些善后工作需要处理,也就是第四步 “请求结束,进行回调” ,执行完了这一步之后,Laravel 才算是到了一个终结点。再贴一次代码吧,不少技术人员看不见代码就难受: 

 3. 最关键的步骤,接受请求,对请求进行处理,返回请求处理的结果
$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);
$response = $kernel->handle(
$request = Illuminate\Http\Request::capture()
);
$response->send();

4. 请求结束,进行回调
$kernel->terminate($request, $response);

本来写这篇文章是想着要不就不区分它们了吧,像网上其他教程那样当成一个东西写就行了,但是按照这种逻辑会让人产生混乱,不利于大家真正理解 Laravel 或者说其他网络开发框架,所以还是做了一个区分,先把网络请求的生命周期交代清楚了,咱们再去详细分析 Laravel 的生命周期,思路上就会更清晰明了些,有些东西八股一下还是有必要的。接下来,咱们就可以暂时把网络请求放一边,看一看 Laravel 的创建过程:

1.  加载 Composer 生成的 autoload.php 自动加载文件

这一步没啥好解释的,只要是使用 Composer 构建的框架和项目,都会有这一步,不解释了,如果你对 Composer 不熟悉的话,有时间的时候可以补一补这部分的缺口:

《 Composer PHP 依赖管理工具 》

2.  创建 Laravel 应用容器对象 $app ( Service Container ) 

  $app = new Illuminate\Foundation\Application( dirname(__DIR__) );
$app->singleton(
Illuminate\Contracts\Http\Kernel::class,
App\Http\Kernel::class
);
$app->singleton(
Illuminate\Contracts\Console\Kernel::class,
App\Console\Kernel::class
);
$app->singleton(
Illuminate\Contracts\Debug\ExceptionHandler::class,
App\Exceptions\Handler::class
);
return $app;

在这里基本也没啥难以理解的东西,创建了 $app 容器对象,绑定了 HTTP Kernal 和 Console Kernel,分别用来处理 HTTP 网络请求和 CLI 请求( 执行 php artisan 相关命令请求),还绑定了用来处理应用运行异常的调试处理器。

  # Laravel $app 创建时的构建函数
public function __construct($basePath = null)
{
if ($basePath) {
$this->setBasePath($basePath);
}
$this->registerBaseBindings();
$this->registerBaseServiceProviders();
$this->registerCoreContainerAliases();
}

上面是 $app 实例创建的具体逻辑,我们看到也没啥难以理解的内容,无非就是设定路径,注册基础绑定信息,注册基础 Service Provider 和 Aliases,而做这些工作的意义就是可以帮助我们更方便的访问和获取项目目录中的文件信息,加载配置文件,加载自定义的类,加载Service,aliases 定义了如何把开发框架中最基础的服务注册到 Service Container 中,看了这部分代码,咱们就知道为啥 app('files') app ('Illuminate\Filesystem\Filesystem') 这两种使用方法都可以拿到 FileSystem 服务的原因了。

  public function registerCoreContainerAliases()
{
foreach ([
'app' => [\Illuminate\Foundation\Application::class, \Illuminate\Contracts\Container\Container::class, \Illuminate\Contracts\Foundation\Application::class, \Psr\Container\ContainerInterface::class],
'auth' => [\Illuminate\Auth\AuthManager::class, \Illuminate\Contracts\Auth\Factory::class],
'auth.driver' => [\Illuminate\Contracts\Auth\Guard::class],
'blade.compiler' => [\Illuminate\View\Compilers\BladeCompiler::class],
'cache' => [\Illuminate\Cache\CacheManager::class, \Illuminate\Contracts\Cache\Factory::class],
'files' => [\Illuminate\Filesystem\Filesystem::class],
'filesystem' => [\Illuminate\Filesystem\FilesystemManager::class, \Illuminate\Contracts\Filesystem\Factory::class],
.

'view' => [\Illuminate\View\Factory::class, \Illuminate\Contracts\View\Factory::class],
] as $key => $aliases) {
foreach ($aliases as $alias) {
$this->alias($key, $alias);
}
}
}

其实很多人之所以不愿意去看代码,有一个主要的原因就是没有理解 Service,Service Container,Service Provider,Facades,Contracts 这些核心概念以及他们之间的关联,如果你对 Laravel 核心概念不清楚,我也真的不建议你看源码,你会想把电脑砸掉的 ~

3.  创建 Laravel HTTP Kernel 核心,接收用户的网络请求,处理并返回响应结果

咱们终于又回来了,咱们先来看看创建 Kernel 的时候都做了啥,HTTP 内核继承自 Illuminate\Foundation\Http\Kernel 类,这个类中拥有三个核心成员变量

  protected $app;
protected $router;
protected $bootstrappers = [
\Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables::class, # 加载 .env 中的配置信息
\Illuminate\Foundation\Bootstrap\LoadConfiguration::class, # 加载 config 目录中所有配置文件的配置信息
\Illuminate\Foundation\Bootstrap\HandleExceptions::class, # 异常处理
\Illuminate\Foundation\Bootstrap\RegisterFacades::class, # 注册门面
\Illuminate\Foundation\Bootstrap\RegisterProviders::class, # 注册Service Providers
\Illuminate\Foundation\Bootstrap\BootProviders::class, # 注册启动器
];

这三个核心成员变量就是引导咱们更快理清 $app->kernel->handle(网络请求) 的最好导游:

$app 咱们已经说了足够多,就是应用容器,我们所有的工作都在这个容器中进行。

$router 路由对象,它提供路由相关的服务,帮助我们把网络请求分配给对应的路由进行逻辑处理,然后把处理的结果(网络响应)返回给我们,我们在web.php中定义的路由就是由它来管理的。

$bootstrappers 数组,这个数组中的任务项在网络请求被处理前运行,我们可以看到 环境检查,配置加载,异常处理,Facedes 门面注册,ServiceProvider 注册 等等任务都需要在网络请求被处理前被首先执行,而且这些任务是有前后顺序的,排在前面的会首先执行,这也很容易理解,因为不管是 Facades 还是 Service Providers 都是定义在 config 目录中的 app.php 文件中的,只有加载来配置之后才能注册门面和Service Providers。

然后咱们再来看看 $app->kernel 的构造函数,在这里咱们看到了一个脸熟的不能再熟的老朋友 — Middleware ,在 Kernel 以及它的基类 Illuminate\Foundation\Http\Kernel 中定义了一系列的 middlewares ,借助这些中间件,就可以完成对用户请求的过滤和安全检查等等功能。

  public function __construct(Application $app, Router $router)
{
$this->app = $app;
$this->router = $router;

$router->middlewarePriority = $this->middlewarePriority;
foreach ($this->middlewareGroups as $key => $middleware) {
$router->middlewareGroup($key, $middleware);
}
foreach ($this->routeMiddleware as $key => $middleware) {
$router->aliasMiddleware($key, $middleware);
}
}

至此咱们还是终于可以绕回来进入  $app->kernel->handle($request) 看一看它是如何处理网络请求的吧,下面这张图结合第一张图会更清楚一些,接下来的内容实在不想贴代码了:

laravel kernel handle 

4.  结束请求,进行回调,终止 Laravel 应用,Laravel 到此也完成了它的历史使命。中间件中有一类的中间件,terminable middleware 的处理逻辑就是在这个阶段执行的。

文章写到这里基本上就结束了,太多细节我们并没有涉及,要靠诸君亲力亲为了,我呢,还是那些话,不要在一开始的阶段就去学习 laravel 的生命周期,等你用laravel 工作了一段时间,对它的使用方法和核心概念都清楚了之后再来学习,基本上就是一件很容易的事了。最后附上一张大图,梳理一下整个流程,也作为文章的结束吧。

Laravel 生命周期图解

 作图真累,图看不清的话,就单开一个网页看它吧 ~

西门撸码 2018.10.24 03:44

站长威武,站长出品,都是奔着精品去的啊,用啥画的图啊?

codinget 2018.10.24 03:53

我画图通常用两个软件,一个是 Visio,微软出的,这个只能在 windows 下用,另一个是亿图图示,使用方法都一样,都挺好玩儿的 ~

kobe 2018.10.28 09:58

站长可以把图做得更全面些,把所有流程都用图理清了

codinget 2018.10.28 09:59

会吐血的,但是咱们可以合作共同完成

路人乙 2018.11.29 10:41

能看出来作者真的很用心

codinget 2018.11.29 10:44

这是捧我

路人乙 2018.11.29 11:15

接下来的日子会慢慢看光你所有的博文与课程

Benny 2018.12.03 10:19

读了作者文章,获益匪浅。期待后面的文章👍

codinget 2018.12.03 10:48

下来我会翻译一批好的文章和书籍,速度或许会比较慢

‭‭小铭 2018.12.04 10:55

我这是第二次来看了,感觉稍微有一点儿明白,哈哈,起码我知道什么是 Middleware(过滤想要的请求及数据),BootStrap(好像是前端框架),App(控制器,业务逻辑层),Route(路由,定义HTTP请求及传参)

codinget 2018.12.04 10:59

你现在先不要看生命周期的内容,先继续打基础,网站里的内容很多,队列,定时任务,事件,授权等等,等你对这些都了解了以后再看这个系列的文章就行了

‭‭小铭 2018.12.04 11:00

嘻嘻,好的。

marvin 2020.04.26 06:23

我现在也是小白,但是这里的bootstrap应该不是前端框架那个bootstrap吧

codinget 2020.04.27 08:40

不是的,你说的那是前端的东西,这个是后端的一个核心启动逻辑,很多后端系统都又这个东西的

大风车 2019.01.08 01:49

入坑laravel,不经意间走进您的博客,希望走出博客的时候,自己已经填平laravel坑了!!

codinget 2019.01.08 01:59

laravel坑不重要,我希望帮大家填更深的坑

weave 2019.04.12 12:09

如获至宝

codinget 2019.04.12 12:12

希望我以后写的东西不会让你冒冷汗

weave 2019.04.30 10:14

今天加入永久会员啦~

codinget 2019.04.30 10:21

谢谢兄弟,有你们的支持,我才能更好的把这件事坚持下去,咱们共同努力,好好把自己的职业和生活都做的更好,我能做的不多,就是录录视频,写写图书了,其实一直有一本图书想写给永久会员,跟技术无关,但是我觉得对于大部分来说却会很有帮助,特别能帮助大家减轻焦虑,把很多问题看得更清楚,但是特别担心会写成一本禁书,一直没敢动手写。

weave 2019.04.30 10:25

视频和文章都很有力量,对我的帮助极大

codinget 2019.04.30 10:29

那我多努力吧,咱们都是路上的人,这也是我第一次独立运营一个网站,录教程也是人生头一遭,我尽了我最大的努力

清流 2019.07.30 11:14

$app-&gtkerner 勘误。

codinget 2019.07.30 11:28

感谢,简直就是鹰眼系统

浪子晨 2019.08.09 10:03
本来写这篇文章是想着要不就不区分它们了吧,像网上其他教程那样当成一个东西写就行了,但是按照这种逻辑会让人产生混乱,不利用大家真正理解 Laravel 或者说其他网络开发框架,所以还是做了一个区分,先把网络请求的生命周期交代清楚了,咱们再去详细分析 Laravel 的生命周期,思路上就会更清晰明了些,有些东西八股一下还是有必要的。接下来,咱们就可以暂时把网络请求放一边,看一看 Laravel 的创建过程:

找个手误,第二排的不利用-&gt不利于

codinget 2019.08.09 11:22

浪子并非真的浪,都是真正的认真人,只有我才是真的浪,我下午就该,赶上吃饭时间了

mengge 2019.09.11 12:09

透彻明了,涨姿势了

找一条适合自己的路,坚持走下去
编程原力 京ICP备17045322号-2
版权所有, 侵权者追究法律责任