目錄php
\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
Laravel 5.5
請求到響應的整個執行階段概括爲 4 個:html
程序入口在 index.php
中laravel
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);
建立服務容器實例web
服務容器的建立在 bootstrap\app.php
中進行.redis
$app = new Illuminate\Foundation\Application( realpath(__DIR__.'/../') );
容器 Application
的構造函數:bootstrap
public function __construct($basePath = null) { if ($basePath) { $this->setBasePath($basePath); } $this->registerBaseBindings(); $this->registerBaseServiceProviders(); $this->registerCoreContainerAliases(); }
構造函數 主要完成如下基本配置:api
目錄路徑(綁定到容器中, 並提供類方法獲取子目錄)數組
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); } } }
$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 );
綁定重要接口:
$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, ]; }
以處理 Http 請求爲例
index.php
入口文件
$response = $kernel->handle( $request = Illuminate\Http\Request::capture() );
請求是經過 Illuminate\Http\Request::capture()
實例化的, 主要是將請求信息以對象形式表現出來
入口文件:
$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
類
核心類的初始化 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 個啓動服務:
\Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables::class
從 .env
文件中解析環境變量到 getevn()
, $_ENV
, $_SERVER
依賴
vlucas/phpdotenv
擴展包
\Illuminate\Foundation\Bootstrap\LoadConfiguration::class
載入 config
目錄下全部 php 配置文件, 並將生成的配置存儲類綁定到服務容器 $app['config']
同時配置時區及 多字節格式(utf8)
\Illuminate\Foundation\Bootstrap\HandleExceptions::class
報告全部錯誤 error_report(E_ALL)
提供對未捕獲的異常, 錯誤的全局處理 set_error_handler
, set_exception_handler
, register_shutdown_function
\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` 調用方法( 使用服務容器實例化對應類)
\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 觸發時在註冊對應服務提供者
\Illuminate\Foundation\Bootstrap\BootProviders::class
調用服務容器的 boot()
方法, 依次調用在服務容器中 register
的全部服務提供者的 boot()
方法
在內核處理請求, 將請求實例經過中間件處理後, 將請求的處理交給路由 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() ); }); }
route
的 run()
方法最終將請求轉給 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控制器 的事了.
在 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; }
在 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; }
在 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); } }