明白流程不表明精通Laravel。由於還須要把這個流程與實際開發須要融匯貫通:學以至用。好比,如何修改Laravel日誌存放目錄?Laravel並無自定義日誌目錄的配置,須要修改源代碼,這就須要瞭解Laravel的生命流程,明白Laravel是在哪裏,何時進行日誌目錄設置。php
綁定的異常處理:App\Exceptions\Handler::class 能夠在這裏自定義html
由此看來,Applicantion的初始化並無很複雜laravel
Illuminate\Events\Dispatcher
- 而且設置了`setQueueResolver` Resolver 通常是一個callable類型,用於向容器獲取某個對象。好比這裏的是一個隊列
public function createLogger() { $log = // \Monolog\Logger new Monolog($this->channel()), // \Illuminate\Contracts\Events\Dispatcher 上面Events的綁定 $this->app['events'] if ($this->app->hasMonologConfigurator()) { call_user_func($this->app->getMonologConfigurator(), $log->getMonolog()); } else { // 默認會走這裏 $this->configureHandler($log); } return $log; } // channel 只是獲得當前的環境 protected function channel() { return $this->app->bound('env') ? $this->app->environment() : 'production'; }
默認會走configureHandler
方法:web
protected function configureHandler(Writer $log) { $this->{'configure'.ucfirst($this->handler()).'Handler'}($log); } // 從配置中獲得 是single/daily/Syslog/Errorlog protected function handler() { if ($this->app->bound('config')) { return $this->app->make('config')->get('app.log', 'single'); } return 'single'; } // single 調用: $log->useFiles // daily 調用:$log->useDailyFiles // Syslog 調用:$log->useSyslog // Errorlog 調用:$log->useErrorLog
這裏是要從配置上獲取啊,關鍵是如今尚未加載配置!!這是什麼狀況?json
$this->app->singleton('log', function () { return $this->createLogger(); });
雖然是綁定,但只是綁定一個Resolver
,一個閉包,並非立刻實例化Logger的。因此在加載配置前,將會使用默認值形式記錄日誌的。bootstrap
意思是設置monolog 該把日誌寫在哪裏,怎麼寫,好比 daily。api
protected function configureDailyHandler(Writer $log) { $log->useDailyFiles( // 固定storage目錄和文件名 能夠在這修改 $this->app->storagePath().'/logs/laravel.log', $this->maxFiles(), $this->logLevel() ); }
若是想要自定義處理,則:數組
// 設置一個回調便可 $app->configureMonologUsing('myLogCallback'); function myLogCallback($log) { $log->useDailyFiles( '/tmp/logs/laravel.log', 5, // 最多幾個日誌文件,能夠寫成配置 'debug' // 日誌等級,能夠寫成配置 ); } //關鍵是在哪裏調用這個設置比較好?
若是隻是修改日誌路徑,我建議修改對應handler的路徑便可,好比修改configureDailyHandler
的路徑修改一下。甚至能夠在服務提供者下添加一個自定義的handler。我認爲Laravel應該把這個Log服務提供者作成Kernel這樣的繼承。緩存
好比: App 的 registerBaseServiceProviders 中:cookie
$this->register(new \Illuminate\Log\LogServiceProvider($this));
改爲:
$this->register(new \App\Providers\LogServiceProvider($this));
而後再app/Providers目錄下新增一個LogServiceProvider extens \Illuminate\Log\LogServiceProvider
。 這樣咱們就能夠重寫或者添加日誌的configuredHandler日誌配置handler
了.
Illuminate\Routing\RoutingServiceProvider
的註冊 register
/** * Register the service provider. * * @return void */ public function register() { /* 綁定 :router => Illuminate\Routing\Router Router 定義了 get post put */ $this->registerRouter(); /* 綁定: url => Illuminate\Routing\UrlGenerator 綁定: routes => \Illuminate\Routing\RouteCollection 回調: session :setSessionResolver 回調: 上述綁定的 rebind */ $this->registerUrlGenerator(); /* 綁定:redirect => Illuminate\Routing\Redirector */ $this->registerRedirector(); $this->registerPsrRequest(); $this->registerPsrResponse(); $this->registerResponseFactory(); }
暫時能夠把Kernel看做一個黑匣子:把請求往裏放,響應返回出來。
Facade 中的Route實際上調用的就是 Illuminate\Routing\Router 實例的方法。Router還有魔術方法__call 相似
as
,domain
,middleware
,name
,namespace
,prefix
這些Router沒有的方法,會重新建立一個新的Illuminate\Routin\RouteRegistrar
實例調用其attribute
方法 (Registrar是登記的意思)
這些都回在RouteServiceProvider
上調用:
/** * Define the "web" routes for the application. * * These routes all receive session state, CSRF protection, etc. * * @return void */ protected function mapWebRoutes() { Route::middleware('web') // 這裏就建立了RouteRegistrar 而且返回自身 ->namespace($this->namespace) ->group(base_path('routes/web.php')); }
最終 RouteRegistrar 仍是對router對象進行操做。 疑問:爲何不直接操做router,而走這麼複雜的路?
主要是給RouteServiceProvider 的mapWebRoutes
和 mapApiRoutes
方法使用。做用是區分而且保存 路由設置的屬性。由於每次調用都會從新建立一個RouteRegistrar
實例,所以 其attributes都是不共享的,起到Web與Api理由設置的屬性不會相互影響。
要注意:服務提供者註冊的時候沒有進行依賴注入,也不須要使用依賴注入;由於實例化的時候就把app
做爲參數傳遞進去,若是須要直接使用app
獲取依賴的對象便可
看代碼:
$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class); $response = $kernel->handle( $request = Illuminate\Http\Request::capture() ); $response->send(); $kernel->terminate($request, $response);
上面看,Kernel大致作了4件事:
public function __construct(Application $app, Router $router) { $this->app = $app; $this->router = $router; // 設置中間件優先級列表 $router->middlewarePriority = $this->middlewarePriority; // 中間件組 web 和 api 的中間件組 foreach ($this->middlewareGroups as $key => $middleware) { $router->middlewareGroup($key, $middleware); } // 路由中間件 foreach ($this->routeMiddleware as $key => $middleware) { $router->aliasMiddleware($key, $middleware); } }
Kernel初始化作了3件事,整體是一件事(設置中間件):
後兩步的中間件屬性都是空的,具體的值在App\Http\Kernel
中
在設置路由中間件中aliasMiddleware
顧名思義,就是給中間件設置一個別名而已。
捕獲請求後面再說
這裏是核心部分了:路由調度,中間件棧(閉包),自定義異常處理。在此以前都沒有進行過一次的依賴注入
。由於尚未在容器當中。
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); } event(new Events\RequestHandled($request, $response)); return $response; }
注意:Throwable
可以捕捉 Exception 和 Error。這是php7 的特徵。
enableHttpMethodParameterOverride
作什麼?方法欺騙!
/** * Enables support for the _method request parameter to determine the intended HTTP method. * * Be warned that enabling this feature might lead to CSRF issues in your code. * Check that you are using CSRF tokens when required. * If the HTTP method parameter override is enabled, an html-form with method "POST" can be altered * and used to send a "PUT" or "DELETE" request via the _method request parameter. * If these methods are not protected against CSRF, this presents a possible vulnerability. * * The HTTP method can only be overridden when the real HTTP method is POST. */ public static function enableHttpMethodParameterOverride() { self::$httpMethodParameterOverride = true; }
主要看註釋。大概意思:
爲了可以經過_method參數進行方法欺騙.被警告,啓用這個特性可能會致使你的代碼中的CSRF問題。檢查您是否在須要時使用CSRF令牌。若是啓用HTTP方法參數覆蓋,則能夠更改具備方法「POST」的html表單,並經過_method請求參數發送「PUT」或「DELETE」請求。若是這些方法不受CSRF保護,就會出現一個可能的漏洞。方法前片只有在真正的HTTP方法是POST的時候纔會被修改
protected function sendRequestThroughRouter($request) { // 經過路由的時候才綁定請求到容器中 $this->app->instance('request', $request); // 清除Facade的$resolvedInstance屬性保存的對象。 // 當使用Facade的時候若是不存在則從app容器獲取對象 Facade::clearResolvedInstance('request'); // 啓動App 實際上是調用一些類 $this->bootstrap(); return (new Pipeline($this->app)) ->send($request) ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware) ->then($this->dispatchToRouter()); }
App 下的
public function bootstrapWith(array $bootstrappers) { // 修改成已經啓動 $this->hasBeenBootstrapped = true; // 啓動類 觸發事件 foreach ($bootstrappers as $bootstrapper) { $this['events']->fire('bootstrapping: '.$bootstrapper, [$this]); // 調用這些類的bootstrap方法 $this->make($bootstrapper)->bootstrap($this); $this['events']->fire('bootstrapped: '.$bootstrapper, [$this]); } }
看一下App啓動流程有哪些?
protected $bootstrappers = [ // 加載 .env環境變量 \Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables::class, // 加載config目錄的配置 \Illuminate\Foundation\Bootstrap\LoadConfiguration::class, \Illuminate\Foundation\Bootstrap\HandleExceptions::class, \Illuminate\Foundation\Bootstrap\RegisterFacades::class, // 註冊配置的服務提供者 \Illuminate\Foundation\Bootstrap\RegisterProviders::class, // 啓動服務提供者 即調用 boot方法 \Illuminate\Foundation\Bootstrap\BootProviders::class, ];
注意:環境配置與config配置的加載以前已經說過了,這裏就不說了:
這裏簡單總結一下上面兩個連接:
爲何有這樣的要求?由於若是使用 php artisan config:cache 把配置緩存後,env函數將再也不去加載.env文件,config函數也不會加載config目錄(目錄中的類也就不能加載了)
Illuminate\Foundation\Bootstrap\HandleExceptions
的bootstrap
方法:
public function bootstrap(Application $app) { $this->app = $app; // 設置報全部的錯 -1 的補碼全都是1 error_reporting(-1); // 設置系統錯誤地handler:warning notice等 set_error_handler([$this, 'handleError']); // 設置異常處理 set_exception_handler([$this, 'handleException']); // 設置php結束時的處理函數 register_shutdown_function([$this, 'handleShutdown']); if (! $app->environment('testing')) { ini_set('display_errors', 'Off'); } }
其實就是設置一些錯誤和異常處理回調,最終的handler是以前說的App\Exceptions\Handler::class
類。
咱們看看:
public function handleError($level, $message, $file = '', $line = 0, $context = []) { if (error_reporting() & $level) { throw new ErrorException($message, 0, $level, $file, $line); } }
只是把一些能夠捕獲的PHP Error轉爲Exception,而後再由ExceptionHandler處理。
public function handleException($e) { if (! $e instanceof Exception) { $e = new FatalThrowableError($e); } // 報告錯誤,好比寫入錯誤日誌 $this->getExceptionHandler()->report($e); // 根據不一樣的運行環境輸出不同的錯誤形式 if ($this->app->runningInConsole()) { $this->renderForConsole($e); } else { $this->renderHttpResponse($e); } }
好了,看到這裏你們明白了自定義異常處理是在:``App\Exceptions\Handler::class` 處理的.
這個是須要在app.aliases
上進行配置的。
調用了一個AliasLoader
類:顧名思義,就是一個類別名自動加載者;就是爲這些Facade設置自動加載。
/** * AliasLoader類 * Prepend the load method to the auto-loader stack. * * @return void */ protected function prependToLoaderStack() { spl_autoload_register([$this, 'load'], true, true); }
bool spl_autoload_register ([ callable $autoload_function [, bool $throw = true [, bool $prepend = false ]]] )
Facade性能: Laravel把Facade的加載放在最前面。所以使用Facade是能夠提升自動加載速度的。可是若是在app.aliases加了一個不是Facade的一個別名,這將會下降性能(由於會去寫文件,建立一個Facade)
看看Facade是怎麼加載的:
public function load($alias) { // 若是沒有Facade的命名空間,說明不是Facade if (static::$facadeNamespace && strpos($alias, static::$facadeNamespace) === 0) { // 使用Facade的模板自動建立一個與別名同樣的Facade,而且加載它:ensureFacadeExists $this->loadFacade($alias); // 返回 true 告訴PHP不用再去加載,已經加載了 return true; } // 有Facade這個命名空間: Facades\\ if (isset($this->aliases[$alias])) { // 設置別名,而且自動加載 return class_alias($this->aliases[$alias], $alias); } } // PHP class_alias原生函數 bool class_alias ( string $original , string $alias [, bool $autoload = TRUE ] )
public function registerConfiguredProviders() { (new ProviderRepository($this, new Filesystem, $this->getCachedServicesPath())) ->load($this->config['app.providers']); }
服務提供者的緩載介紹:服務提供者緩載
這裏簡單說:
$this->getCachedServicesPath()
獲得服務提供者緩存起來的文件主要看Illuminate\Foundation\ProviderRepository
的load
方法:
public function load(array $providers) { // 加載緩存的services.php $manifest = $this->loadManifest(); // 是否須要重新生成緩存文件 if ($this->shouldRecompile($manifest, $providers)) { $manifest = $this->compileManifest($providers); } // 對於 when形式的服務提供者 在某事件觸發才註冊 foreach ($manifest['when'] as $provider => $events) { $this->registerLoadEvents($provider, $events); } // 這些是立刻就註冊的 foreach ($manifest['eager'] as $provider) { $this->app->register($provider); } // 這個是緩載的。只是添加到App的deferredServices屬性 $this->app->addDeferredServices($manifest['deferred']); } // 添加事件監聽 protected function registerLoadEvents($provider, array $events) { if (count($events) < 1) { return; } $this->app->make('events')->listen($events, function () use ($provider) { $this->app->register($provider); }); }
這個時候會使用App 的 call方法來調用全部註冊的服務提供者。爲何使用
call
方法,而不是直接調用?由於使用依賴注入。要知道這個call
方法是public的,咱們能夠在其餘地方也能夠顯示使用
/** * Call the given Closure / class@method and inject its dependencies. * * @param callable|string $callback * @param array $parameters * @param string|null $defaultMethod * @return mixed */ public function call($callback, array $parameters = [], $defaultMethod = null) { return BoundMethod::call($this, $callback, $parameters, $defaultMethod); }
Illuminate\Container\BoundMethod
就是依賴注入實現的類了(須要傳遞App容器進去才行)
從
Illuminate\Container\BoundMethod
的call
方法開始
graph TD A[A BoundMethod::call] --> B B{B callback 是字符串型} -->|Yes|C B --> |No|E C[C 轉爲 array 型callable] --> E E[E callBoundMethod] --> F F{F callback 是 array 型} --> |No|G F --> |Yes|H G[G 直接調用default而且返回] --> Z H{H callback 是 app的bindingMethod} --> |Yes|I I[I 直接調用app的bindingethod] --> Z H --> |No|J J[J 直接調用default而且返回] --> Z Z[Z 返回call結果]
註釋:
function test(Request $request) { ... } $app->call('test');
return $default instanceof Closure ? $default() : $default;
function () use ($container, $callback, $parameters) { return call_user_func_array( $callback, static::getMethodDependencies($container, $callback, $parameters) ); }
看出,進行判斷$callback 須要哪些依賴和參數的方法是:getMethodDependencies
// 獲得方法或者函數的參數列表 包括依賴注入的和$parameters自己傳入的 protected static function getMethodDependencies($container, $callback, array $parameters = []) { $dependencies = []; foreach (static::getCallReflector($callback)->getParameters() as $parameter) { static::addDependencyForCallParameter($container, $parameter, $parameters, $dependencies); } return array_merge($dependencies, $parameters); } // 經過反射獲得方法或者函數定義的參數列表 protected static function getCallReflector($callback) { if (is_string($callback) && strpos($callback, '::') !== false) { $callback = explode('::', $callback); } return is_array($callback) ? new ReflectionMethod($callback[0], $callback[1]) : new ReflectionFunction($callback); // 這裏兼容了函數的依賴注入 } // 計算方法或者函數的 依賴參數 or $parameters or 自己的默認值 protected static function addDependencyForCallParameter($container, $parameter, array &$parameters, &$dependencies) { // 若是 定義的參數列表順序 指定 `手動參數` 放在`自動參數`前面。 // $parameters 是關聯數組的時候纔有用 if (array_key_exists($parameter->name, $parameters)) { $dependencies[] = $parameters[$parameter->name]; unset($parameters[$parameter->name]); } elseif ($parameter->getClass()) { // 類的依賴注入 $dependencies[] = $container->make($parameter->getClass()->name); } elseif ($parameter->isDefaultValueAvailable()) { // 有默認值 $dependencies[] = $parameter->getDefaultValue(); } }
可使用依賴注入的地方:
注意:
須要注入或有默認值的參數
這裏統一稱爲自動參數
;其餘爲手動參數
,就是須要顯示傳遞的參數自動參數
放在前面,手動參數
放後面手動參數
順序保持一致)
call_user_func_array
調用的時候參數列表只是按照順序來傳遞$array = [ 'param2' => '1111', 'param1' => '2222' ]; function test($param1, $param2){ var_dump([$param1, $param2]); } call_user_func_array('test', $array); // 輸出:只是按照順序傳遞 array(2) { [0] => string(4) "1111" [1] => string(4) "2222" }
按照 官網文檔 上說第二個的$param_arr
須要時索引數組。可是上面的例子使用了一個關聯數組也是能夠正常運行的;難道是低版本纔會有這種要求?這個留給讀者去研究了!
Laravel有管道?什麼鬼?怎麼還不說
中間件
和路由
?Laravel的中間件
和路由
是經過管道傳輸的,在傳輸的過程當中就把請求轉化爲響應。
// 管道就只有四個方法 interface Pipeline { /** * Set the traveler object being sent on the pipeline. * 把須要加工的東西放入管道 * * @param mixed $traveler * @return $this */ public function send($traveler); /** * Set the stops of the pipeline. * 管道的停留點。就管道流程的加工點 * * @param dynamic|array $stops * @return $this */ public function through($stops); /** * Set the method to call on the stops. * 設置停留點調用的方法名,好比中間件的handle * * @param string $method * @return $this */ public function via($method); /** * Run the pipeline with a final destination callback. * 運行管道(重點),而且把結果送到目的地 * * @param \Closure $destination * @return mixed */ public function then(Closure $destination); }
爲何叫中間件棧?由於這與棧的思想是一致的。請求最早進入的中間件,最後纔會出去:先進後出。則麼作到的?使用閉包一層一層地包裹着控制器!!先把middleware 3
包裹 控制器
,而後middleware 2
再對其進行包裝一層,最後包上最後一層middlware 1
。
看一下管道是怎麼使用的:
return (new Pipeline($this->app)) ->send($request) ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware) ->then($this->dispatchToRouter()); // Pipeline 下: public function then(Closure $destination) { // array_reduce 是PHP原生的函數,用於數組迭代 $pipeline = array_reduce( // 這裏對pipes進行反轉 是須要從最後的 middleware 3 開始包裹 array_reverse($this->pipes), $this->carry(), $this->prepareDestination($destination) ); // 運行獲得的閉包棧 return $pipeline($this->passable); } // PHP 版本的實現 function array_reduce($array, $callback, $initial=null) { $acc = $initial; foreach($array as $a) $acc = $callback($acc, $a); return $acc; } protected function prepareDestination(Closure $destination) { return function ($passable) use ($destination) { return $destination($passable); }; } protected function carry() { // 返回一個處理 $pipes的callback;$stack=上次迭代完成的中間件棧; $pipe 本次迭代的中間件; // $passable每一層中間件都須要處理的$request請求 return function ($stack, $pipe) { // 本次迭代 包裹完成的$stack 閉包棧。每一層閉包棧都只有一個 $passable 參數 return function ($passable) use ($stack, $pipe) { if ($pipe instanceof Closure) { // 若是管道是一個Closure的實例,咱們將直接調用它,不然咱們將把這些管道從容器中解出來,並用適當的方法和參數調用它,並返回結果。 return $pipe($passable, $stack); } elseif (! is_object($pipe)) { // 字符串形式能夠 傳遞參數 list($name, $parameters) = $this->parsePipeString($pipe); // 若是管道是一個字符串,咱們將解析字符串並將該類從依賴注入容器中解析出來。 而後,咱們能夠構建一個可調用的函數,並執行所需參數中的管道函數。 $pipe = $this->getContainer()->make($name); $parameters = array_merge([$passable, $stack], $parameters); } else { // 若是管道已是一個對象,咱們只須要調用一個對象,而後將它傳遞給管道。 沒有必要作任何額外的解析和格式化,由於咱們提供的對象已是一個徹底實例化的對象。 $parameters = [$passable, $stack]; } // 運行中間件 參數順序: $request請求, 下一層閉包棧,自定義參數 return $pipe->{$this->method}(...$parameters); }; }; }
看了源碼以後,其實管道並非真的把中間件閉包一層一層地進行包裹的。而是把閉包連成一個鏈表!,把相鄰的中間件鏈接起來: middleware 的 $next 參數其實就是 下一個中間件,能夠選擇在何時調用。好比在
middleware
的handle
方法中:
前置操做... 能夠進行權限控制 $response = $next($request); 後置操做... 能夠統一處理返回格式,好比返回jsonp格式
注意:上面使用經過管道通過的中間件是在Http Kernel上配置的必然通過的中間件。可是路由的中間件/控制器中間件呢?接下來就是路由調度的是了!!
其實上面流程圖最後的節點是
控制器
,其實並非,而是被路由中間件棧包裹
的控制器。這也是須要使用管道的
public function dispatchToRoute(Request $request) { // First we will find a route that matches this request. We will also set the // route resolver on the request so middlewares assigned to the route will // receive access to this route instance for checking of the parameters. // 路由調度是這裏 $route = $this->findRoute($request); // 給$request 設置$route實例 $request->setRouteResolver(function () use ($route) { return $route; }); // 事件配發 就是觸發某事件 $this->events->dispatch(new Events\RouteMatched($route, $request)); // 把請求經過管道(中間件是管道節點) 獲得請求 這裏就不說了 $response = $this->runRouteWithinStack($route, $request); // 這裏只是封裝如下請求 不是路由調度的內容 return $this->prepareResponse($request, $response); }
Illuminate\Routing\RouteCollection
:全部路由到保存在這裏。public function match(Request $request) { $routes = $this->get($request->getMethod()); // First, we will see if we can find a matching route for this current request // method. If we can, great, we can just return it so that it can be called // by the consumer. Otherwise we will check for routes with another verb. // 翻譯:首先,咱們將看看咱們是否能夠找到這個當前請求方法的匹配路由。 若是咱們能夠的話,那麼咱們能夠把它歸還給消費者consumer。 不然,咱們將檢查另外一個verb的路線。 $route = $this->matchAgainstRoutes($routes, $request); // 找到匹配路由 if (! is_null($route)) { return $route->bind($request); } // If no route was found we will now check if a matching route is specified by // another HTTP verb. If it is we will need to throw a MethodNotAllowed and // inform the user agent of which HTTP verb it should use for this route. // 翻譯:若是沒有找到路由,咱們如今檢查一個匹配路由是否由另外一個HTTP verb指定。 若是是這樣的話,咱們須要拋出一個MethodNotAllowed方法,並告知用戶代理它應該爲這個路由使用哪一個HTTP verb。意思是提示:方法不被容許的Http 405 錯誤 $others = $this->checkForAlternateVerbs($request); if (count($others) > 0) { return $this->getRouteForMethods($request, $others); } throw new NotFoundHttpException; } protected function matchAgainstRoutes(array $routes, $request, $includingMethod = true) { // Arr::first 返回提一個符合條件的數組元素 return Arr::first($routes, function ($value) use ($request, $includingMethod) { return $value->matches($request, $includingMethod); }); } // Illuminate\Routing\Route 類的 matches public function matches(Request $request, $includingMethod = true) { // $this->compileRoute(); foreach ($this->getValidators() as $validator) { if (! $includingMethod && $validator instanceof MethodValidator) { continue; } // 任意一個不符合 就不符合要求 if (! $validator->matches($this, $request)) { return false; } } return true; } public static function getValidators() { if (isset(static::$validators)) { return static::$validators; } // To match the route, we will use a chain of responsibility pattern with the // validator implementations. We will spin through each one making sure it // passes and then we will know if the route as a whole matches request. // 翻譯:爲了匹配路線,咱們將使用驗證器實現的責任模式鏈。 咱們將經過每個確保它經過,而後咱們將知道是否整個路線符合要求。 return static::$validators = [ new UriValidator, new MethodValidator, new SchemeValidator, new HostValidator, ]; }
注意:
Symfony\Component\HttpFoundation\Response
public function send() { // 設置 header 和 cookie $this->sendHeaders(); // echo $this->content $this->sendContent(); if (function_exists('fastcgi_finish_request')) { fastcgi_finish_request(); } elseif ('cli' !== PHP_SAPI) { // ob_end_flush() static::closeOutputBuffers(0, true); } return $this; } public static function closeOutputBuffers($targetLevel, $flush) { // 獲取有效的緩衝區嵌套層數 $status = ob_get_status(true); $level = count($status); // PHP_OUTPUT_HANDLER_* are not defined on HHVM 3.3 $flags = defined('PHP_OUTPUT_HANDLER_REMOVABLE') ? PHP_OUTPUT_HANDLER_REMOVABLE | ($flush ? PHP_OUTPUT_HANDLER_FLUSHABLE : PHP_OUTPUT_HANDLER_CLEANABLE) : -1; // 一層一層進行清除 while ($level-- > $targetLevel && ($s = $status[$level]) && (!isset($s['del']) ? !isset($s['flags']) || $flags === ($s['flags'] & $flags) : $s['del'])) { if ($flush) { ob_end_flush(); } else { ob_end_clean(); } } }
http Kernel的terminate終止。通常狀況都是不須要作些什麼的,由於不多去設置終止回調
terminate
方法)public function terminate($request, $response) { // 逐個調用中間件的terminate 。沒有 $next 參數 $this->terminateMiddleware($request, $response); $this->app->terminate(); } // App public function terminate() { // 使用依賴注入形式 調用設置的回調 foreach ($this->terminatingCallbacks as $terminating) { $this->call($terminating); } }
本文還不是還完整:好比請求捕獲;路由如何調用控制器等等。以後有時間會繼續更新的!!
原文:https://laravel-china.org/articles/7135/laravel-54-life-process-and-source-code-analysis