专栏首页codersamLaravel的生命周期

Laravel的生命周期

万物皆有生命,每个生命都有自己的生命周期。

想要说清Laravel的生命周期,先来了解一下PHP的生命周期。

PHP的生命周期

众所周知,PHP有两种运行模式:

  1. CLI模式
  2. web模式

当我们在命令行终端键入php这个命令的时候,使用的就是CLI模式;当使用nginx或者其他服务器作为宿主来处理一个请求的时候,会调用php来运行,此时使用的就是web模式。

PHP的两种运行模式都必将经历下面这几个阶段,才能完成一次请求处理。

php_module_startup() // 模块初始化阶段
php_request_startup() // 请求初始化阶段
php_request_shutdown() // 请求关闭阶段
php_module_shutdown() // 模块关闭阶段

当我们请求一个php文件的时候,比如laravel的public/index.php文件时,php为了完成此次请求,会发生5个阶段的生命周期切换:

  1. 模块初始化,即调用php.ini中指明的拓展初始化函数进行初始化工作,如mysql拓展。
  2. 请求初始化,即初始化本次执行脚本所需要的变量名称和变量值,如$_SESSION,$_COOKIE等
  3. 执行该php脚本
  4. 请求处理完成,按顺序调用各个模块的shutdown方法,并对每个变量进行unset()。
  5. 关闭模块,php调用每个拓展的shutdown方法,释放每个模块在内存中的占有。这也意味着没有下一个请求了。

web模式和cli模式的区别

CLI模式会在每次脚本执行都需要经历完整的5个周期,因为脚本执行完不会再有下一个请求。

web模式为了应对并发,会采用多线程(php-fpm),因此周期中的1和5只执行一次,下次接收到请求时,重复2-4的周期,这样就节省了模块初始化带来的开销。

说了这么多,知道这些有什么用?其实就是为了定位Laravel在哪里执行的,没错,就是第3步。

现在我们知道了,每次请求之后php的变量都会unset(),laravel的singleton只是在某一次请求中singleton,在php中的静态变量也不能在多个请求之间共享,不像Java静态变量拥有全局作用。

Laravel的生命周期

官方文档(5.4):https://laravelacademy.org/post/6684.html

// 阶段一 
require __DIR__.'/../bootstrap/autoload.php';
// 阶段二 
$app = require_once __DIR__.'/../bootstrap/app.php';
// 阶段三
$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);
$response = $kernel->handle(
    $request = Illuminate\Http\Request::capture()
);
// 阶段四
$response->send();
// 阶段五
$kernel->terminate($request, $response);

上面是除却备注public/index.php全部源码,其实laravel最好的文档就是注释。

  1. 阶段一:加载所需的依赖,通过注释就可以得知 /* |-------------------------------------------------------------------------- | Register The Auto Loader |-------------------------------------------------------------------------- | | Composer provides a convenient, automatically generated class loader for | our application. We just need to utilize it! We'll simply require it | into the script here so that we don't have to worry about manual | loading any of our classes later on. It feels great to relax. | */
  2. 阶段二:创建laravel实例(服务器容器) 这个阶段是由 bootstrap/app.php 来完成创建实例(服务器容器)的,实际就是项目初始化的过程,这个过程主要完成注册项目基础服务、注册项目服务提供者别名、注册目录路径等在内的一些列注册工作。 // 创建应用实例 $app = new Illuminate\Foundation\Application( realpath(__DIR__.'/../') ); // 绑定内核到服务器容器 // http内核 $app->singleton( Illuminate\Contracts\Http\Kernel::class, App\Http\Kernel::class ); // console内核 $app->singleton( Illuminate\Contracts\Console\Kernel::class, App\Console\Kernel::class ); // 异常处理内核 $app->singleton( Illuminate\Contracts\Debug\ExceptionHandler::class, App\Exceptions\Handler::class ); return $app;
  3. 阶段二:接收请求并响应 $kernel = $app->make(Illuminate\Contracts\Http\Kernel::class); 这段代码主要是解析内核实例将我们项目目录中的中间件注册到路由器中,来实现http请求前的过滤功能; 研究一下app\Http\Kernel中间件文件继承了HttpKernel,HttpKernel中的__construct()传递了两个参数,$app就是上面一步创建的服务器容器,$route就是我们的路由器。 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); } } 之前所有的步骤都是为了构建服务来处理请求,这个时候就构建完成可以处理请求了。 $response = $kernel->handle( $request = Illuminate\Http\Request::capture() ); 通过Illuminate\Http\Request::capture()获取用户请求实例,拿到用户请求中的报文信息;还是HttpKernel这个类文件,$kernel->handle()拿到用户的请求数据后,返回一个响应实例。 public function handle($request) { try { $request->enableHttpMethodParameterOverride(); // 获取响应 $response = $this->sendRequestThroughRouter($request); } catch (Exception $e) { $this->reportException($e); $response = $this->renderException($request, $e); } catch (Throwable $e) { $this->reportException($e = new FatalThrowableError($e)); $response = $this->renderException($request, $e); } $this->app['events']->dispatch( new Events\RequestHandled($request, $response) ); return $response; } 相信懂得MVC架构的都知道,处理请求并且获取响应都是通过控制器层来调度不同的模型层来处理请求和返回响应数据的,看到这里还是一脸懵逼,感觉没Controller什么事情啊?

下面我们看看$this->sendRequestThroughRouter($request);这个方法 protected function sendRequestThroughRouter($request) { $this->app->instance('request', $request); Facade::clearResolvedInstance('request'); $this->bootstrap(); return (new Pipeline($this->app)) ->send($request) ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware) ->then($this->dispatchToRouter()); } 通过代码来看一下处理请求及返回响应的步骤如下:

  • 先将请求实例注册到当前容器中 public function instance($abstract, $instance) { $this->removeAbstractAlias($abstract); $isBound = $this->bound($abstract); unset($this->aliases[$abstract]); // We'll check to determine if this type has been bound before, and if it has // we will fire the rebound callbacks registered with the container and it // can be updated with consuming classes that have gotten resolved here. $this->instances[$abstract] = $instance; if ($isBound) { $this->rebound($abstract); } }
  • 清除之前的请求实例缓存
  • 启动引导程序 启动引导程序中做了非常多的操作;例如:加载配置文件,注册别名类加载服务,注册服务提供者,启动服务。 具体可研究一下HttpKernel类文件下的$bootstrappers变量和Illuminate\Foundation\Application中的bootstrapWith()方法。
  • 发送请求到路由器(通过路由找到控制器层) protected function sendRequestThroughRouter($request) { $this->app->instance('request', $request); Facade::clearResolvedInstance('request'); $this->bootstrap(); return (new Pipeline($this->app)) ->send($request) ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware) ->then($this->dispatchToRouter()); } 在这段代码中,创建管道,并且将本次请求实例进行中间件处理后,执行通过路由找到控制器层或者匿名函数获取响应数据。 protected function dispatchToRouter() { return function ($request) { $this->app->instance('request', $request); return $this->router->dispatch($request); }; } $this->router->dispatch($request)对应Illuminate\Routing\Router类。 Illuminate\Routing\Router类中完成了,查找到对应的路由实例,并运行路由实例中的控制器或者匿名函数(最终运行routers\web.php配置中匹配到的控制器或匿名函数)。 至此,Laravel就完成了一次请求处理。

  1. 阶段四:返回响应数据 经过上面漫长的处理之后,HTTP请求终于迎来了最终章,将得到的响应数据输出给用户。 发送响应由 Illuminate\Http\Response 父类 Symfony\Component\HttpFoundation\Response 中的 send() 方法完成。 public function send() { $this->sendHeaders(); $this->sendContent(); if (\function_exists('fastcgi_finish_request')) { fastcgi_finish_request(); } elseif (!\in_array(\PHP_SAPI, ['cli', 'phpdbg'], true)) { static::closeOutputBuffers(0, true); } return $this; }
  2. 阶段五:终止程序 $kernel->terminate($request, $response);// 主要清理本次请求注册的中间件

总结

上面的内容大致介绍了Laravel的生命周期,这样我们就可以更加了解Laravel的工作原理以及机制,开发出更加高效的代码。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 关于MySQL优化的两三事(一)-- MYISAM存储引擎

    关于MySQL存储引擎相信大家都很熟悉,MySQL支持多种存储引擎,以支持不同的操作。下面将为介绍一下MySQL中的MYISAM存储引擎。

    用户2475223
  • 关于PHP的cli模式

    用户2475223
  • 分布式架构理解总结

    系统架构随着技术手段越来越成熟近些年得到了很大的提升,从以前的单系统架构,到应用和数据层分离,集群,分布式集群等等,下面就来一一总结。

    用户2475223
  • RocketMq之NameSever浅析

    NameSever 是一种路由服务,类似于dubbo中的注册中心zk,它存储了Broker的路由信息,供Producer和Consumer使用,不然Produc...

    大王叫下
  • Laravel源码分析之Route

    路由是外界访问Laravel应用程序的通路或者说路由定义了Laravel的应用程序向外界提供服务的具体方式:通过指定的URI、HTTP请求方法以及路由参数(可选...

    KevinYan
  • 聊聊claudb的scripting command

    claudb-1.7.1/src/main/java/com/github/tonivade/claudb/command/scripting/Abstract...

    codecraft
  • JavaSE基础之this关键字的引用

    1.0   this 指代当前对象, 在一般方法中可以通过this来引用当前对象的成员(方法,属性)。 2.0  通过  this()  调 用重载的构造器,需...

    Gxjun
  • Spring Cloud Zuul中DispatcherServlet和ZuulServlet

    Zuul是通过Servlet机制实现的。一般情况下,ZuulServet被嵌入到Spring Dispatch机制中,由DispatcherServlet分派处...

    java达人
  • 常用验证码之算术验证码

    这里是常用验证码的第二篇——算术验证码。在上一篇已经实现了 [常用验证码之字符串验证码] ,感兴趣的可以去看一下~ 接下来要实现的就是字符串验证码了,先看下效果...

    流眸
  • 你被追尾了

    被追尾了,严格来讲,就是你的汽车和别人的汽车发生了碰撞. 所以本文来介绍一些检测碰撞的算法.

    ACM算法日常

扫码关注云+社区

领取腾讯云代金券