欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

[原创] Laravel 启动流程

程序员文章站 2022-04-09 19:03:09
[TOC] Laravel 5.5 请求到响应的整个执行阶段归纳为 4 个: 1. 程序启动准备阶段 文件自动加载 服务容器实例化 基础服务提供者的注册 核心类的实例化 2. 请求实例化阶段 实例化 Request 实例 3. 请求处理阶段 准备请求处理的环境 将请求实例通过中间件处理 及 通过路由 ......

目录

laravel 5.5
请求到响应的整个执行阶段归纳为 4 个:

  1. 程序启动准备阶段
    • 文件自动加载
    • 服务容器实例化
    • 基础服务提供者的注册
    • 核心类的实例化
  2. 请求实例化阶段
    • 实例化 request 实例
  3. 请求处理阶段
    • 准备请求处理的环境
    • 将请求实例通过中间件处理 及 通过路由和控制器的分发控制
  4. 响应发送和程序终止阶段
    • 将响应内容返回给客户端
    • 记录与客户端有关的信息等

1. 程序启动准备

程序入口在 index.php

require __dir__.'/../vendor/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);

创建服务容器实例

服务容器的创建在 bootstrap\app.php 中进行.

$app = new illuminate\foundation\application(
    realpath(__dir__.'/../')
);

1.1 容器基础配置

容器 application 的构造函数:

public function __construct($basepath = null)
{
    if ($basepath) {
        $this->setbasepath($basepath);
    }

    $this->registerbasebindings();

    $this->registerbaseserviceproviders();

    $this->registercorecontaineraliases();
}

构造函数 主要完成以下基本配置:

  • 目录路径(绑定到容器中, 并提供类方法获取子目录)

    public function setbasepath($basepath)
    {
        $this->basepath = rtrim($basepath, '\/');
    
        $this->bindpathsincontainer();
    
        return $this;
    }
    
    protected function bindpathsincontainer()
        {
            $this->instance('path', $this->path());
            $this->instance('path.base', $this->basepath());
            $this->instance('path.lang', $this->langpath());
            $this->instance('path.config', $this->configpath());
            $this->instance('path.public', $this->publicpath());
            $this->instance('path.storage', $this->storagepath());
            $this->instance('path.database', $this->databasepath());
            $this->instance('path.resources', $this->resourcepath());
            $this->instance('path.bootstrap', $this->bootstrappath());
        }
  • 绑定容器自身

    protected function registerbasebindings()
    {
        static::setinstance($this);
    
        $this->instance('app', $this);
    
        $this->instance(container::class, $this);
    
        $this->instance(packagemanifest::class, new packagemanifest(
            new filesystem, $this->basepath(), $this->getcachedpackagespath()
        ));
    }
  • 基础服务注册( event, log, route)

    protected function registerbaseserviceproviders()
    {
        $this->register(new eventserviceprovider($this));
    
        $this->register(new logserviceprovider($this));
    
        $this->register(new routingserviceprovider($this));
    }
  • 别名注册

    多个接口名 对应一个简短别名, 后续在注册服务时只需绑定到别名上即可 (而不必绑定到具体接口名)

    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],
            'cache.store'          => [\illuminate\cache\repository::class, \illuminate\contracts\cache\repository::class],
            'config'               => [\illuminate\config\repository::class, \illuminate\contracts\config\repository::class],
            'cookie'               => [\illuminate\cookie\cookiejar::class, \illuminate\contracts\cookie\factory::class, \illuminate\contracts\cookie\queueingfactory::class],
            'encrypter'            => [\illuminate\encryption\encrypter::class, \illuminate\contracts\encryption\encrypter::class],
            'db'                   => [\illuminate\database\databasemanager::class],
            'db.connection'        => [\illuminate\database\connection::class, \illuminate\database\connectioninterface::class],
            'events'               => [\illuminate\events\dispatcher::class, \illuminate\contracts\events\dispatcher::class],
            'files'                => [\illuminate\filesystem\filesystem::class],
            'filesystem'           => [\illuminate\filesystem\filesystemmanager::class, \illuminate\contracts\filesystem\factory::class],
            'filesystem.disk'      => [\illuminate\contracts\filesystem\filesystem::class],
            'filesystem.cloud'     => [\illuminate\contracts\filesystem\cloud::class],
            'hash'                 => [\illuminate\contracts\hashing\hasher::class],
            'translator'           => [\illuminate\translation\translator::class, \illuminate\contracts\translation\translator::class],
            'log'                  => [\illuminate\log\writer::class, \illuminate\contracts\logging\log::class, \psr\log\loggerinterface::class],
            'mailer'               => [\illuminate\mail\mailer::class, \illuminate\contracts\mail\mailer::class, \illuminate\contracts\mail\mailqueue::class],
            'auth.password'        => [\illuminate\auth\passwords\passwordbrokermanager::class, \illuminate\contracts\auth\passwordbrokerfactory::class],
            'auth.password.broker' => [\illuminate\auth\passwords\passwordbroker::class, \illuminate\contracts\auth\passwordbroker::class],
            'queue'                => [\illuminate\queue\queuemanager::class, \illuminate\contracts\queue\factory::class, \illuminate\contracts\queue\monitor::class],
            'queue.connection'     => [\illuminate\contracts\queue\queue::class],
            'queue.failer'         => [\illuminate\queue\failed\failedjobproviderinterface::class],
            'redirect'             => [\illuminate\routing\redirector::class],
            'redis'                => [\illuminate\redis\redismanager::class, \illuminate\contracts\redis\factory::class],
            'request'              => [\illuminate\http\request::class, \symfony\component\httpfoundation\request::class],
            'router'               => [\illuminate\routing\router::class, \illuminate\contracts\routing\registrar::class, \illuminate\contracts\routing\bindingregistrar::class],
            'session'              => [\illuminate\session\sessionmanager::class],
            'session.store'        => [\illuminate\session\store::class, \illuminate\contracts\session\session::class],
            'url'                  => [\illuminate\routing\urlgenerator::class, \illuminate\contracts\routing\urlgenerator::class],
            'validator'            => [\illuminate\validation\factory::class, \illuminate\contracts\validation\factory::class],
            'view'                 => [\illuminate\view\factory::class, \illuminate\contracts\view\factory::class],
        ] as $key => $aliases) {
            foreach ($aliases as $alias) {
                $this->alias($key, $alias);
            }
        }
    }

1.2 核心类绑定

$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
);

绑定重要接口:

  • http 核心类
  • 命令行 核心类
  • 异常处理类

1.3 实例化 http 核心类

$kernel = $app->make(illuminate\contracts\http\kernel::class);

http 核心类的构造函数

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/http/kernel.php

<?php

namespace app\http;

use illuminate\foundation\http\kernel as httpkernel;

class kernel extends httpkernel
{
    // 全局中间件,最先调用
    protected $middleware = [

        // 检测是否应用是否进入『维护模式』
        // 见:https://d.laravel-china.org/docs/5.5/configuration#maintenance-mode
        \illuminate\foundation\http\middleware\checkformaintenancemode::class,

        // 检测请求的数据是否过大
        \illuminate\foundation\http\middleware\validatepostsize::class,

        // 对提交的请求参数进行 php 函数 `trim()` 处理
        \app\http\middleware\trimstrings::class,

        // 将提交请求参数中空子串转换为 null
        \illuminate\foundation\http\middleware\convertemptystringstonull::class,

        // 修正代理服务器后的服务器参数
        \app\http\middleware\trustproxies::class,
    ];

    // 定义中间件组
    protected $middlewaregroups = [

        // web 中间件组,应用于 routes/web.php 路由文件
        'web' => [
            // cookie 加密解密
            \app\http\middleware\encryptcookies::class,

            // 将 cookie 添加到响应中
            \illuminate\cookie\middleware\addqueuedcookiestoresponse::class,

            // 开启会话
            \illuminate\session\middleware\startsession::class,

            // 认证用户,此中间件以后 auth 类才能生效
            // 见:https://d.laravel-china.org/docs/5.5/authentication
            \illuminate\session\middleware\authenticatesession::class,

            // 将系统的错误数据注入到视图变量 $errors 中
            \illuminate\view\middleware\shareerrorsfromsession::class,

            // 检验 csrf ,防止跨站请求伪造的安全威胁
            // 见:https://d.laravel-china.org/docs/5.5/csrf
            \app\http\middleware\verifycsrftoken::class,

            // 处理路由绑定
            // 见:https://d.laravel-china.org/docs/5.5/routing#route-model-binding
            \illuminate\routing\middleware\substitutebindings::class,
        ],

        // api 中间件组,应用于 routes/api.php 路由文件
        'api' => [
            // 使用别名来调用中间件
            // 请见:https://d.laravel-china.org/docs/5.5/middleware#为路由分配中间件
            'throttle:60,1',
            'bindings',
        ],
    ];

    // 中间件别名设置,允许你使用别名调用中间件,例如上面的 api 中间件组调用
    protected $routemiddleware = [

        // 只有登录用户才能访问,我们在控制器的构造方法中大量使用
        'auth' => \illuminate\auth\middleware\authenticate::class,

        // http basic auth 认证
        'auth.basic' => \illuminate\auth\middleware\authenticatewithbasicauth::class,

        // 处理路由绑定
        // 见:https://d.laravel-china.org/docs/5.5/routing#route-model-binding
        'bindings' => \illuminate\routing\middleware\substitutebindings::class,

        // 用户授权功能
        'can' => \illuminate\auth\middleware\authorize::class,

        // 只有游客才能访问,在 register 和 login 请求中使用,只有未登录用户才能访问这些页面
        'guest' => \app\http\middleware\redirectifauthenticated::class,

        // 访问节流,类似于 『1 分钟只能请求 10 次』的需求,一般在 api 中使用
        'throttle' => \illuminate\routing\middleware\throttlerequests::class,
    ];
}

2. 请求实例化

以处理 http 请求为例

index.php 入口文件

$response = $kernel->handle(
    $request = illuminate\http\request::capture()
);

请求是通过 illuminate\http\request::capture() 实例化的, 主要是将请求信息以对象形式表现出来

3. 请求处理

入口文件:

$response = $kernel->handle(
    $request = illuminate\http\request::capture()
);

$kernel->handle(...) 处理请求过程

illuminate\foundation\http\kernel

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;
}


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);
    };
}

实际处理请求逻辑主要在 sendrequestthroughrouter 方法中, 它主要做了:

  • 核心类的初始化

  • 经由中间件过滤后将请求最终交由 router 处理

    对于 http 请求处理, 中间件包括:

    protected $middleware = [
    
        \illuminate\foundation\http\middleware\checkformaintenancemode::class,
    
        \illuminate\foundation\http\middleware\validatepostsize::class,
    
        \app\http\middleware\trimstrings::class,
    
        \illuminate\foundation\http\middleware\convertemptystringstonull::class,
    
        \app\http\middleware\trustproxies::class,
    
    ];
    

    该中间件数组定义在 http 核心类中, 同时在核心类的构造函数中传递给 router

3.1 请求处理环境初始化

核心类的初始化 bootstrap()

protected $bootstrappers = [
    \illuminate\foundation\bootstrap\loadenvironmentvariables::class,
    \illuminate\foundation\bootstrap\loadconfiguration::class,
    \illuminate\foundation\bootstrap\handleexceptions::class,
    \illuminate\foundation\bootstrap\registerfacades::class,
    \illuminate\foundation\bootstrap\registerproviders::class,
    \illuminate\foundation\bootstrap\bootproviders::class,
];

# 初始化
public function bootstrap()
{
    if (! $this->app->hasbeenbootstrapped()) {
        $this->app->bootstrapwith($this->bootstrappers());
    }
}

protected function bootstrappers()
{
    return $this->bootstrappers;
}

在服务容器 application 类中

public function bootstrapwith(array $bootstrappers)
{
    $this->hasbeenbootstrapped = true;

    foreach ($bootstrappers as $bootstrapper) {
        $this['events']->fire('bootstrapping: '.$bootstrapper, [$this]);

        $this->make($bootstrapper)->bootstrap($this);

        $this['events']->fire('bootstrapped: '.$bootstrapper, [$this]);
    }
}

该步骤主要是主要是对核心类中定义的 $bootstrappers 数组元素(引导类)初始化.

bootstrap 过程具体是在服务容器来中进行, 由核心类调用并传入待初始化的类

http 核心类默认包含以下 6 个启动服务:

1. 环境监测 \illuminate\foundation\bootstrap\loadenvironmentvariables::class

.env 文件中解析环境变量到 getevn(), $_env, $_server

依赖 vlucas/phpdotenv 扩展包

2. 配置加载 \illuminate\foundation\bootstrap\loadconfiguration::class

载入 config 目录下所有 php 配置文件, 并将生成的配置存储类绑定到服务容器 $app['config']

同时配置时区及 多字节格式(utf8)

3. 常处理 \illuminate\foundation\bootstrap\handleexceptions::class

报告所有错误 error_report(e_all)

提供对未捕获的异常, 错误的全局处理 set_error_handler, set_exception_handler, register_shutdown_function

4. 外观注册 \illuminate\foundation\bootstrap\registerfacades::class

app.aliases 中读取外观配置数组

'aliases' => [

        'app' => illuminate\support\facades\app::class,
        'artisan' => illuminate\support\facades\artisan::class,
        'auth' => illuminate\support\facades\auth::class,
        'blade' => illuminate\support\facades\blade::class,
        'broadcast' => illuminate\support\facades\broadcast::class,
        'bus' => illuminate\support\facades\bus::class,
        'cache' => illuminate\support\facades\cache::class,
        'config' => illuminate\support\facades\config::class,
        'cookie' => illuminate\support\facades\cookie::class,
        'crypt' => illuminate\support\facades\crypt::class,
        'db' => illuminate\support\facades\db::class,
        'eloquent' => illuminate\database\eloquent\model::class,
        'event' => illuminate\support\facades\event::class,
        'file' => illuminate\support\facades\file::class,
        'gate' => illuminate\support\facades\gate::class,
        'hash' => illuminate\support\facades\hash::class,
        'lang' => illuminate\support\facades\lang::class,
        'log' => illuminate\support\facades\log::class,
        'mail' => illuminate\support\facades\mail::class,
        'notification' => illuminate\support\facades\notification::class,
        'password' => illuminate\support\facades\password::class,
        'queue' => illuminate\support\facades\queue::class,
        'redirect' => illuminate\support\facades\redirect::class,
        'redis' => illuminate\support\facades\redis::class,
        'request' => illuminate\support\facades\request::class,
        'response' => illuminate\support\facades\response::class,
        'route' => illuminate\support\facades\route::class,
        'schema' => illuminate\support\facades\schema::class,
        'session' => illuminate\support\facades\session::class,
        'storage' => illuminate\support\facades\storage::class,
        'url' => illuminate\support\facades\url::class,
        'validator' => illuminate\support\facades\validator::class,
        'view' => illuminate\support\facades\view::class,

    ],

使用 spl_autoload_register(...) 处理类加载, 配合 class_alias() 提供类的别名调用

facade外观类基类依赖__callstatic` 调用方法( 使用服务容器实例化对应类)

5. 服务提供者注册 \illuminate\foundation\bootstrap\registerproviders::class

app.providers 中读取所有服务提供者

'providers' => [

        /*
         * laravel framework service providers...
         */
        illuminate\auth\authserviceprovider::class,
        illuminate\broadcasting\broadcastserviceprovider::class,
        illuminate\bus\busserviceprovider::class,
        illuminate\cache\cacheserviceprovider::class,
        illuminate\foundation\providers\consolesupportserviceprovider::class,
        illuminate\cookie\cookieserviceprovider::class,
        illuminate\database\databaseserviceprovider::class,
        illuminate\encryption\encryptionserviceprovider::class,
        illuminate\filesystem\filesystemserviceprovider::class,
        illuminate\foundation\providers\foundationserviceprovider::class,
        illuminate\hashing\hashserviceprovider::class,
        illuminate\mail\mailserviceprovider::class,
        illuminate\notifications\notificationserviceprovider::class,
        illuminate\pagination\paginationserviceprovider::class,
        illuminate\pipeline\pipelineserviceprovider::class,
        illuminate\queue\queueserviceprovider::class,
        illuminate\redis\redisserviceprovider::class,
        illuminate\auth\passwords\passwordresetserviceprovider::class,
        illuminate\session\sessionserviceprovider::class,
        illuminate\translation\translationserviceprovider::class,
        illuminate\validation\validationserviceprovider::class,
        illuminate\view\viewserviceprovider::class,

        /*
         * package service providers...
         */

        /*
         * application service providers...
         */
        app\providers\appserviceprovider::class,
        app\providers\authserviceprovider::class,
        // app\providers\broadcastserviceprovider::class,
        app\providers\eventserviceprovider::class,
        app\providers\routeserviceprovider::class,  # 路由表生成
    ],

服务提供者经过解析后分为 3 种类型的服务提供者:

  • eager 类型

    马上调用 register 注册

  • deferred 类型

    记录下来, 当服务容器解析对应服务时, 才注册对应的服务提供者

  • when 类型

    记录下来, 当对应 event 触发时在注册对应服务提供者

6. 启动提供者 \illuminate\foundation\bootstrap\bootproviders::class

调用服务容器的 boot() 方法, 依次调用在服务容器中 register 的所有服务提供者的 boot() 方法

3.2 路由处理请求

在内核处理请求, 将请求实例通过中间件处理后, 将请求的处理交给路由 router 进行控制器的分发.

http kernel

protected function dispatchtorouter()
{
    return function ($request) {
        $this->app->instance('request', $request);

        return $this->router->dispatch($request);
    };
}

路由表存储结构说明

illuminate\routing\route 存储单条路由

illuminate\routing\routecollection 保存所有 route 实例, 形成路由表

illuminate\routing\router 类实例持有 routecollection 路由表实例.

即, 一个 router 持有一个 routecollection, 而 routecollection 拥有 n 个 route

router 中对请求的处理同样经过一系列的 路由中间件

# 路由处理请求的入库
public function dispatchtoroute(request $request)
{
    return $this->runroute($request, $this->findroute($request));
}

# 根据请求的 url 和 method 查找对应的 route
protected function findroute($request)
{
    $this->current = $route = $this->routes->match($request);

    $this->container->instance(route::class, $route);

    return $route;
}

# 根据对应的请求和路由条目, 返回相应的 $response
protected function runroute(request $request, route $route)
{
    $request->setrouteresolver(function () use ($route) {
        return $route;
    });

    $this->events->dispatch(new events\routematched($route, $request));

    return $this->prepareresponse($request,
                                  $this->runroutewithinstack($route, $request)
                                 );
}

# 请求经过路由中间件过滤后, 交由 route 的 run() 方法处理
protected function runroutewithinstack(route $route, request $request)
{
    $shouldskipmiddleware = $this->container->bound('middleware.disable') &&
        $this->container->make('middleware.disable') === true;

    $middleware = $shouldskipmiddleware ? [] : $this->gatherroutemiddleware($route);

    return (new pipeline($this->container))
        ->send($request)
        ->through($middleware)
        ->then(function ($request) use ($route) {
            return $this->prepareresponse(
                $request, $route->run()
            );
        });
}

routerun() 方法最终将请求转给 illuminate\routing\controllerdispatcher::dispatch 处理

public function dispatch(route $route, $controller, $method)
{
    $parameters = $this->resolveclassmethoddependencies(
        $route->parameterswithoutnulls(), $controller, $method
    );

    if (method_exists($controller, 'callaction')) {
        return $controller->callaction($method, $parameters);
    }

    return $controller->{$method}(...array_values($parameters));
}

剩下的事情就是 controller控制器 的事了.

3.3 处理返回的 response

router 中有一个方法, 用于对返回的 $response 进行处理

public function prepareresponse($request, $response)
{
    return static::toresponse($request, $response);
}

/**
* @return \illuminate\http\response|\illuminate\http\jsonresponse
*/
public static function toresponse($request, $response)
{
    if ($response instanceof responsable) {
        $response = $response->toresponse($request);
    }

    if ($response instanceof psrresponseinterface) {
        $response = (new httpfoundationfactory)->createresponse($response);
    } elseif (! $response instanceof symfonyresponse &&
              ($response instanceof arrayable ||
               $response instanceof jsonable ||
               $response instanceof arrayobject ||
               $response instanceof jsonserializable ||
               is_array($response))) {
        $response = new jsonresponse($response);
    } elseif (! $response instanceof symfonyresponse) {
        $response = new response($response);
    }

    if ($response->getstatuscode() === response::http_not_modified) {
        $response->setnotmodified();
    }

    return $response->prepare($request);    # 最后的处理
}  

上述过程中, 在返回 $response 之前进行了最后的处理 $response->prepare($request)

该过程是在 symfony\component\httpfoundation\response::prepare() 中进行

对响应的封装是通过 illuminate\http\response 类完成, 该类底层是 symfony 框架的 response 类

即, symfony\component\httpfoundation\response

public function prepare(request $request)
{
    $headers = $this->headers;

    if ($this->isinformational() || $this->isempty()) {
        $this->setcontent(null);
        $headers->remove('content-type');
        $headers->remove('content-length');
    } else {
        // content-type based on the request
        if (!$headers->has('content-type')) {
            $format = $request->getrequestformat();
            if (null !== $format && $mimetype = $request->getmimetype($format)) {
                $headers->set('content-type', $mimetype);
            }
        }

        // fix content-type
        $charset = $this->charset ?: 'utf-8';
        if (!$headers->has('content-type')) {
            $headers->set('content-type', 'text/html; charset='.$charset);
        } elseif (0 === stripos($headers->get('content-type'), 'text/') && false === stripos($headers->get('content-type'), 'charset')) {
            // add the charset
            $headers->set('content-type', $headers->get('content-type').'; charset='.$charset);
        }

        // fix content-length
        if ($headers->has('transfer-encoding')) {
            $headers->remove('content-length');
        }

        if ($request->ismethod('head')) {
            // cf. rfc2616 14.13
            $length = $headers->get('content-length');
            $this->setcontent(null);
            if ($length) {
                $headers->set('content-length', $length);
            }
        }
    }

    // fix protocol
    if ('http/1.0' != $request->server->get('server_protocol')) {
        $this->setprotocolversion('1.1');
    }

    // check if we need to send extra expire info headers
    if ('1.0' == $this->getprotocolversion() && false !== strpos($this->headers->get('cache-control'), 'no-cache')) {
        $this->headers->set('pragma', 'no-cache');
        $this->headers->set('expires', -1);
    }

    $this->ensureieoversslcompatibility($request);

    return $this;
}

4. 响应发送和程序终止

4.1 响应的发送

index.php 入口文件的最后是将响应返回给客户端

$response->send();

symfony\component\httpfoundation\response

public function send()
{
    $this->sendheaders();
    $this->sendcontent();

    if (function_exists('fastcgi_finish_request')) {
        fastcgi_finish_request();
    } elseif (!\in_array(php_sapi, array('cli', 'phpdbg'), true)) {
        static::closeoutputbuffers(0, true);
    }

    return $this;
}

public function sendheaders()
{
    // headers have already been sent by the developer
    if (headers_sent()) {
        return $this;
    }

    // headers
    foreach ($this->headers->allpreservecase() as $name => $values) {
        foreach ($values as $value) {
            header($name.': '.$value, false, $this->statuscode);
        }
    }

    // status
    header(sprintf('http/%s %s %s', $this->version, $this->statuscode, $this->statustext), true, $this->statuscode);

    return $this;
}

public function sendcontent()
{
    echo $this->content;

    return $this;
}

4.2 请求中止

index.php 入口文件的最后:

$kernel->terminate($request, $response);

依旧以 http kernel 为例:

public function terminate($request, $response)
{
    $this->terminatemiddleware($request, $response);    # 中间件中止处理

    $this->app->terminate();    # 服务容器的中止处理函数
}

protected function terminatemiddleware($request, $response)
{
    $middlewares = $this->app->shouldskipmiddleware() ? [] : array_merge(
        $this->gatherroutemiddleware($request),
        $this->middleware
    );

    foreach ($middlewares as $middleware) {
        if (! is_string($middleware)) {
            continue;
        }

        list($name) = $this->parsemiddleware($middleware);

        $instance = $this->app->make($name);

        if (method_exists($instance, 'terminate')) {
            $instance->terminate($request, $response);
        }
    }
}

此处的中间件指的是定义在 kernel 中的 $middleware 中间件数组列表, 不包含 路由中间件.

laravel 5.1 注: 默认只有会话中间件包含 terminate() 函数

application 服务容器的中止处理函数

public function terminate()
{
    foreach ($this->terminatingcallbacks as $terminating) {
        $this->call($terminating);
    }
}