zoukankan      html  css  js  c++  java
  • [原创] Laravel 启动流程

    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(IlluminateContractsHttpKernel::class);
    
    $response = $kernel->handle(
        $request = IlluminateHttpRequest::capture()
    );
    
    $response->send();
    
    $kernel->terminate($request, $response);
    

    创建服务容器实例

    服务容器的创建在 bootstrapapp.php 中进行.

    $app = new IlluminateFoundationApplication(
        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'                  => [IlluminateFoundationApplication::class, IlluminateContractsContainerContainer::class, IlluminateContractsFoundationApplication::class,  PsrContainerContainerInterface::class],
              'auth'                 => [IlluminateAuthAuthManager::class, IlluminateContractsAuthFactory::class],
              'auth.driver'          => [IlluminateContractsAuthGuard::class],
              'blade.compiler'       => [IlluminateViewCompilersBladeCompiler::class],
              'cache'                => [IlluminateCacheCacheManager::class, IlluminateContractsCacheFactory::class],
              'cache.store'          => [IlluminateCacheRepository::class, IlluminateContractsCacheRepository::class],
              'config'               => [IlluminateConfigRepository::class, IlluminateContractsConfigRepository::class],
              'cookie'               => [IlluminateCookieCookieJar::class, IlluminateContractsCookieFactory::class, IlluminateContractsCookieQueueingFactory::class],
              'encrypter'            => [IlluminateEncryptionEncrypter::class, IlluminateContractsEncryptionEncrypter::class],
              'db'                   => [IlluminateDatabaseDatabaseManager::class],
              'db.connection'        => [IlluminateDatabaseConnection::class, IlluminateDatabaseConnectionInterface::class],
              'events'               => [IlluminateEventsDispatcher::class, IlluminateContractsEventsDispatcher::class],
              'files'                => [IlluminateFilesystemFilesystem::class],
              'filesystem'           => [IlluminateFilesystemFilesystemManager::class, IlluminateContractsFilesystemFactory::class],
              'filesystem.disk'      => [IlluminateContractsFilesystemFilesystem::class],
              'filesystem.cloud'     => [IlluminateContractsFilesystemCloud::class],
              'hash'                 => [IlluminateContractsHashingHasher::class],
              'translator'           => [IlluminateTranslationTranslator::class, IlluminateContractsTranslationTranslator::class],
              'log'                  => [IlluminateLogWriter::class, IlluminateContractsLoggingLog::class, PsrLogLoggerInterface::class],
              'mailer'               => [IlluminateMailMailer::class, IlluminateContractsMailMailer::class, IlluminateContractsMailMailQueue::class],
              'auth.password'        => [IlluminateAuthPasswordsPasswordBrokerManager::class, IlluminateContractsAuthPasswordBrokerFactory::class],
              'auth.password.broker' => [IlluminateAuthPasswordsPasswordBroker::class, IlluminateContractsAuthPasswordBroker::class],
              'queue'                => [IlluminateQueueQueueManager::class, IlluminateContractsQueueFactory::class, IlluminateContractsQueueMonitor::class],
              'queue.connection'     => [IlluminateContractsQueueQueue::class],
              'queue.failer'         => [IlluminateQueueFailedFailedJobProviderInterface::class],
              'redirect'             => [IlluminateRoutingRedirector::class],
              'redis'                => [IlluminateRedisRedisManager::class, IlluminateContractsRedisFactory::class],
              'request'              => [IlluminateHttpRequest::class, SymfonyComponentHttpFoundationRequest::class],
              'router'               => [IlluminateRoutingRouter::class, IlluminateContractsRoutingRegistrar::class, IlluminateContractsRoutingBindingRegistrar::class],
              'session'              => [IlluminateSessionSessionManager::class],
              'session.store'        => [IlluminateSessionStore::class, IlluminateContractsSessionSession::class],
              'url'                  => [IlluminateRoutingUrlGenerator::class, IlluminateContractsRoutingUrlGenerator::class],
              'validator'            => [IlluminateValidationFactory::class, IlluminateContractsValidationFactory::class],
              'view'                 => [IlluminateViewFactory::class, IlluminateContractsViewFactory::class],
          ] as $key => $aliases) {
              foreach ($aliases as $alias) {
                  $this->alias($key, $alias);
              }
          }
      }
      

    1.2 核心类绑定

    $app->singleton(
        IlluminateContractsHttpKernel::class,
        AppHttpKernel::class
    );
    
    $app->singleton(
        IlluminateContractsConsoleKernel::class,
        AppConsoleKernel::class
    );
    
    $app->singleton(
        IlluminateContractsDebugExceptionHandler::class,
        AppExceptionsHandler::class
    );
    

    绑定重要接口:

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

    1.3 实例化 Http 核心类

    $kernel = $app->make(IlluminateContractsHttpKernel::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 AppHttp;
    
    use IlluminateFoundationHttpKernel as HttpKernel;
    
    class Kernel extends HttpKernel
    {
        // 全局中间件,最先调用
        protected $middleware = [
    
            // 检测是否应用是否进入『维护模式』
            // 见:https://d.laravel-china.org/docs/5.5/configuration#maintenance-mode
            IlluminateFoundationHttpMiddlewareCheckForMaintenanceMode::class,
    
            // 检测请求的数据是否过大
            IlluminateFoundationHttpMiddlewareValidatePostSize::class,
    
            // 对提交的请求参数进行 PHP 函数 `trim()` 处理
            AppHttpMiddlewareTrimStrings::class,
    
            // 将提交请求参数中空子串转换为 null
            IlluminateFoundationHttpMiddlewareConvertEmptyStringsToNull::class,
    
            // 修正代理服务器后的服务器参数
            AppHttpMiddlewareTrustProxies::class,
        ];
    
        // 定义中间件组
        protected $middlewareGroups = [
    
            // Web 中间件组,应用于 routes/web.php 路由文件
            'web' => [
                // Cookie 加密解密
                AppHttpMiddlewareEncryptCookies::class,
    
                // 将 Cookie 添加到响应中
                IlluminateCookieMiddlewareAddQueuedCookiesToResponse::class,
    
                // 开启会话
                IlluminateSessionMiddlewareStartSession::class,
    
                // 认证用户,此中间件以后 Auth 类才能生效
                // 见:https://d.laravel-china.org/docs/5.5/authentication
                IlluminateSessionMiddlewareAuthenticateSession::class,
    
                // 将系统的错误数据注入到视图变量 $errors 中
                IlluminateViewMiddlewareShareErrorsFromSession::class,
    
                // 检验 CSRF ,防止跨站请求伪造的安全威胁
                // 见:https://d.laravel-china.org/docs/5.5/csrf
                AppHttpMiddlewareVerifyCsrfToken::class,
    
                // 处理路由绑定
                // 见:https://d.laravel-china.org/docs/5.5/routing#route-model-binding
                IlluminateRoutingMiddlewareSubstituteBindings::class,
            ],
    
            // API 中间件组,应用于 routes/api.php 路由文件
            'api' => [
                // 使用别名来调用中间件
                // 请见:https://d.laravel-china.org/docs/5.5/middleware#为路由分配中间件
                'throttle:60,1',
                'bindings',
            ],
        ];
    
        // 中间件别名设置,允许你使用别名调用中间件,例如上面的 api 中间件组调用
        protected $routeMiddleware = [
    
            // 只有登录用户才能访问,我们在控制器的构造方法中大量使用
            'auth' => IlluminateAuthMiddlewareAuthenticate::class,
    
            // HTTP Basic Auth 认证
            'auth.basic' => IlluminateAuthMiddlewareAuthenticateWithBasicAuth::class,
    
            // 处理路由绑定
            // 见:https://d.laravel-china.org/docs/5.5/routing#route-model-binding
            'bindings' => IlluminateRoutingMiddlewareSubstituteBindings::class,
    
            // 用户授权功能
            'can' => IlluminateAuthMiddlewareAuthorize::class,
    
            // 只有游客才能访问,在 register 和 login 请求中使用,只有未登录用户才能访问这些页面
            'guest' => AppHttpMiddlewareRedirectIfAuthenticated::class,
    
            // 访问节流,类似于 『1 分钟只能请求 10 次』的需求,一般在 API 中使用
            'throttle' => IlluminateRoutingMiddlewareThrottleRequests::class,
        ];
    }
    

    2. 请求实例化

    以处理 Http 请求为例

    index.php 入口文件

    $response = $kernel->handle(
        $request = IlluminateHttpRequest::capture()
    );
    

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

    3. 请求处理

    入口文件:

    $response = $kernel->handle(
        $request = IlluminateHttpRequest::capture()
    );
    

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

    IlluminateFoundationHttpKernel

    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 EventsRequestHandled($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 = [
      
          IlluminateFoundationHttpMiddlewareCheckForMaintenanceMode::class,
      
          IlluminateFoundationHttpMiddlewareValidatePostSize::class,
      
          AppHttpMiddlewareTrimStrings::class,
      
          IlluminateFoundationHttpMiddlewareConvertEmptyStringsToNull::class,
      
          AppHttpMiddlewareTrustProxies::class,
      
      ];
      
      

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

    3.1 请求处理环境初始化

    核心类的初始化 bootstrap()

    protected $bootstrappers = [
        IlluminateFoundationBootstrapLoadEnvironmentVariables::class,
        IlluminateFoundationBootstrapLoadConfiguration::class,
        IlluminateFoundationBootstrapHandleExceptions::class,
        IlluminateFoundationBootstrapRegisterFacades::class,
        IlluminateFoundationBootstrapRegisterProviders::class,
        IlluminateFoundationBootstrapBootProviders::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. 环境监测 IlluminateFoundationBootstrapLoadEnvironmentVariables::class

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

    依赖 vlucas/phpdotenv 扩展包

    2. 配置加载 IlluminateFoundationBootstrapLoadConfiguration::class

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

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

    3. 常处理 IlluminateFoundationBootstrapHandleExceptions::class

    报告所有错误 error_report(E_ALL)

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

    4. 外观注册 IlluminateFoundationBootstrapRegisterFacades::class

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

    'aliases' => [
    
            'App' => IlluminateSupportFacadesApp::class,
            'Artisan' => IlluminateSupportFacadesArtisan::class,
            'Auth' => IlluminateSupportFacadesAuth::class,
            'Blade' => IlluminateSupportFacadesBlade::class,
            'Broadcast' => IlluminateSupportFacadesBroadcast::class,
            'Bus' => IlluminateSupportFacadesBus::class,
            'Cache' => IlluminateSupportFacadesCache::class,
            'Config' => IlluminateSupportFacadesConfig::class,
            'Cookie' => IlluminateSupportFacadesCookie::class,
            'Crypt' => IlluminateSupportFacadesCrypt::class,
            'DB' => IlluminateSupportFacadesDB::class,
            'Eloquent' => IlluminateDatabaseEloquentModel::class,
            'Event' => IlluminateSupportFacadesEvent::class,
            'File' => IlluminateSupportFacadesFile::class,
            'Gate' => IlluminateSupportFacadesGate::class,
            'Hash' => IlluminateSupportFacadesHash::class,
            'Lang' => IlluminateSupportFacadesLang::class,
            'Log' => IlluminateSupportFacadesLog::class,
            'Mail' => IlluminateSupportFacadesMail::class,
            'Notification' => IlluminateSupportFacadesNotification::class,
            'Password' => IlluminateSupportFacadesPassword::class,
            'Queue' => IlluminateSupportFacadesQueue::class,
            'Redirect' => IlluminateSupportFacadesRedirect::class,
            'Redis' => IlluminateSupportFacadesRedis::class,
            'Request' => IlluminateSupportFacadesRequest::class,
            'Response' => IlluminateSupportFacadesResponse::class,
            'Route' => IlluminateSupportFacadesRoute::class,
            'Schema' => IlluminateSupportFacadesSchema::class,
            'Session' => IlluminateSupportFacadesSession::class,
            'Storage' => IlluminateSupportFacadesStorage::class,
            'URL' => IlluminateSupportFacadesURL::class,
            'Validator' => IlluminateSupportFacadesValidator::class,
            'View' => IlluminateSupportFacadesView::class,
    
        ],
    

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

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

    5. 服务提供者注册 IlluminateFoundationBootstrapRegisterProviders::class

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

    'providers' => [
    
            /*
             * Laravel Framework Service Providers...
             */
            IlluminateAuthAuthServiceProvider::class,
            IlluminateBroadcastingBroadcastServiceProvider::class,
            IlluminateBusBusServiceProvider::class,
            IlluminateCacheCacheServiceProvider::class,
            IlluminateFoundationProvidersConsoleSupportServiceProvider::class,
            IlluminateCookieCookieServiceProvider::class,
            IlluminateDatabaseDatabaseServiceProvider::class,
            IlluminateEncryptionEncryptionServiceProvider::class,
            IlluminateFilesystemFilesystemServiceProvider::class,
            IlluminateFoundationProvidersFoundationServiceProvider::class,
            IlluminateHashingHashServiceProvider::class,
            IlluminateMailMailServiceProvider::class,
            IlluminateNotificationsNotificationServiceProvider::class,
            IlluminatePaginationPaginationServiceProvider::class,
            IlluminatePipelinePipelineServiceProvider::class,
            IlluminateQueueQueueServiceProvider::class,
            IlluminateRedisRedisServiceProvider::class,
            IlluminateAuthPasswordsPasswordResetServiceProvider::class,
            IlluminateSessionSessionServiceProvider::class,
            IlluminateTranslationTranslationServiceProvider::class,
            IlluminateValidationValidationServiceProvider::class,
            IlluminateViewViewServiceProvider::class,
    
            /*
             * Package Service Providers...
             */
    
            /*
             * Application Service Providers...
             */
            AppProvidersAppServiceProvider::class,
            AppProvidersAuthServiceProvider::class,
            // AppProvidersBroadcastServiceProvider::class,
            AppProvidersEventServiceProvider::class,
            AppProvidersRouteServiceProvider::class,	# 路由表生成
        ],
    

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

    • eager 类型

      马上调用 register 注册

    • deferred 类型

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

    • when 类型

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

    6. 启动提供者 IlluminateFoundationBootstrapBootProviders::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);
        };
    }
    

    路由表存储结构说明

    IlluminateRoutingRoute 存储单条路由

    IlluminateRoutingRouteCollection 保存所有 Route 实例, 形成路由表

    IlluminateRoutingRouter 类实例持有 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 EventsRouteMatched($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() 方法最终将请求转给 IlluminateRoutingControllerDispatcher::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 IlluminateHttpResponse|IlluminateHttpJsonResponse
    */
    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)

    该过程是在 SymfonyComponentHttpFoundationResponse::prepare() 中进行

    对响应的封装是通过 IlluminateHttpResponse 类完成, 该类底层是 Symfony 框架的 Response 类

    即, SymfonyComponentHttpFoundationResponse

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

    SymfonyComponentHttpFoundationResponse

    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);
        }
    }
    
  • 相关阅读:
    bzoj 3155: Preprefix sum
    bzoj 1854: [Scoi2010]游戏
    UVA1608 不无聊的序列 Non-boring sequences
    UVA1747 【Swap Space】
    Luogu P5550 Chino的数列
    bzoj 1799: [Ahoi2009]self 同类分布
    bzoj 1054: [HAOI2008]移动玩具
    MATLAB工具箱,应用程序,软件和资源的精选清单
    论文格式排版Issue及解决办法
    《将博客搬至CSDN》
  • 原文地址:https://www.cnblogs.com/youjiaxing/p/10033987.html
Copyright © 2011-2022 走看看