以前就想學着看源碼了,無奈老是半途而廢,此次但願能學完,讓本身沉澱下。php
從入口文件index.php的第一行開始把,laravel
define('LARAVEL_START', microtime(true)); require __DIR__.'/../vendor/autoload.php';
第一行代碼表示記錄項目開始加載的時間,而後加載composer自動加載文件。bootstrap
$app = require_once __DIR__.'/../bootstrap/app.php';
這裏獲取app變量,這裏是整個項目的應用實例,後續還會有不少地方用到他,這裏先跳到app.php文件去看看.數組
app.php文件解析:緩存
$app = new Illuminate\Foundation\Application( realpath(__DIR__.'/../') );
這裏把項目目錄地址的絕對路徑傳入Application類中進行初始化,如今要跳往Application類去看下了:app
public function __construct($basePath = null) { if ($basePath) { $this->setBasePath($basePath); } $this->registerBaseBindings(); $this->registerBaseServiceProviders(); $this->registerCoreContainerAliases(); }
這個類繼承自Illuminate\Container\Container,說明其實整個laravel是一個巨大的容器。composer
若是傳入了項目地址,則首先經過方法setBasePath方法設置基礎路徑,並在此方法中調用bindPathsInContainer方法初始化一系列目錄地址:ide
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()); }
registerBaseBindings方法以下:函數
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() )); }
static::setInstance($this) 這個段代碼是把當前類也就是Application賦值給自身的一個靜態變量$instance,應該是實現了單例模式吧。post
instance 方法綁定一個已存在的對象實例到容器,隨後調用容器將老是返回給定的實例.
從新回到構造函數裏,$this->registerBaseServiceProviders()裏註冊基礎的服務提供者,代碼以下:
$this->register(new EventServiceProvider($this)); $this->register(new LogServiceProvider($this)); $this->register(new RoutingServiceProvider($this));
註冊了事件服務提供者、日誌服務提供者、路由服務提供者,這裏的服務提供者都是Illuminate\Foundation\Support\Providers\ServiceProvider的子類,構造函數接受一個Application實例。
這裏須要去看看register的代碼是怎樣的,以下:
public function register($provider, $options = [], $force = false) { if (($registered = $this->getProvider($provider)) && ! $force) { return $registered; } // If the given "provider" is a string, we will resolve it, passing in the // application instance automatically for the developer. This is simply // a more convenient way of specifying your service provider classes. if (is_string($provider)) { $provider = $this->resolveProvider($provider); } if (method_exists($provider, 'register')) { $provider->register(); } $this->markAsRegistered($provider); // If the application has already booted, we will call this boot method on // the provider class so it has an opportunity to do its boot logic and // will be ready for any usage by this developer's application logic. if ($this->booted) { $this->bootProvider($provider); } return $provider; }
已註冊的服務提供者實例會放在serviceProviders這個數組裏,因此開頭的代碼意思是 若是能從這個數組裏獲取到實例而且force爲false 應該是非強制註冊吧,那麼就地返回實例,不然往下看,若是$provider變量是字符串,則調用resolveProvider解析服務提供者,並返回實例:
if (is_string($provider)) { $provider = $this->resolveProvider($provider); } public function resolveProvider($provider) { return new $provider($this); }
繼續往下看:
if (method_exists($provider, 'register')) { $provider->register(); }
若是傳入的服務提供者存在register方法,則調用其register方法。
接着繼續往下走實例服務提供者:
$this->markAsRegistered($provider); protected function markAsRegistered($provider) { $this->serviceProviders[] = $provider; $this->loadedProviders[get_class($provider)] = true; }
這裏把服務提供者的實例放入serviceProviders數組緩存,並用loadedProviders這個數組標記爲已實例狀態。
繼續往下看,還有最後一段:
if ($this->booted) { $this->bootProvider($provider); } protected function bootProvider(ServiceProvider $provider) { if (method_exists($provider, 'boot')) { return $this->call([$provider, 'boot']); } }
這裏的booted標識項目是否啓動,默認爲false,若是是true(也就是啓動的話),則若是服務提供者裏存在boot方法就會調用。
再次回到構造函數裏,這裏還調用了registerCoreContainerAliases方法,看名字就知道是幹嗎的,註冊核心容器別名。
到這裏Application的初始化工做就完成了,回到app.php文件吧。
$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請求Kernel對象、命令行Kernel對象、錯誤處理。
最後返回$app應用對象。
回到入口文件,往下看:
$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class); $response = $kernel->handle( $request = Illuminate\Http\Request::capture() ); $response->send(); $kernel->terminate($request, $response);
先簡單說一下:
第一行從容器解析kernel對象,初始化。
第二行捕獲請求。
第三行發送請求。
第四行Kernel終止。
下面詳細說:
第一行實際上解析App\Http\Kernel對象,其代碼並無construct構造函數,可是它集成自Illuminate\Foundation\Http\Kernel,那麼咱們追蹤到這個類的構造函數:
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); } }
middlewarePriority這個屬性表示中間件的加載順序,若是不想默認的話能夠在App\Http\Kernel對象重寫這個屬性。
下面的代碼就是加載中間件組合中間件了。
第二,捕獲請求,這裏比較核心,和路由有關
會調用handle方法,此方法依舊不在App\Http\Kernel對象裏,則仍是回到老地方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; }
$request->enableHttpMethodParameterOverride();這裏開啓方法欺騙.好比在post表單模擬put、delete、patch請求。
繼續看sendRequestThroughRouter這個方法。
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()); }
首先把$request對象實例綁定到app容器裏。
$this->bootstrap() 再進行項目初始化,初始化類以下:
1.Illuminate\Foundation\Bootstrap\DetectEnvironment 環境配置($app['env'])
2.Illuminate\Foundation\Bootstrap\LoadConfiguration 基本配置($app['config'])
3.Illuminate\Foundation\Bootstrap\ConfigureLogging 日誌文件($app['log'])
4.Illuminate\Foundation\Bootstrap\HandleExceptions 錯誤&異常處理
5.Illuminate\Foundation\Bootstrap\RegisterFacades 清除已解析的Facade並從新啓動,註冊config文件中alias定義的全部Facade類到容器
6.Illuminate\Foundation\Bootstrap\RegisterProviders 註冊config中providers定義的全部Providers類到容器
7.Illuminate\Foundation\Bootstrap\BootProviders 調用全部已註冊Providers的boot方法
app裏的hasBeenBootstrapped屬性標識是否初始化過,默認爲false,未初始化的狀況下才會運行bootstrap。
運行以上類中的bootstrap方法。
在進行路由調度以前進行一些操做
return (new Pipeline($this->app)) ->send($request) ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware) ->then($this->dispatchToRouter());
最後調用dispatchToRouter開始把請求映射到路由。
protected function dispatchToRouter() { return function ($request) { $this->app->instance('request', $request); return $this->router->dispatch($request); }; }
此函數把當前請求request對象綁定到容器中,而後由route對象來匹配路由。
先看下Illuminate\Routing\Router類吧.
public function dispatch(Request $request) { $this->currentRequest = $request return $this->dispatchToRoute($request); } public function dispatchToRoute(Request $request) { return $this->runRoute($request, $this->findRoute($request)); } protected function findRoute($request) { $this->current = $route = $this->routes->match($request); $this->container->instance(Route::class, $route); return $route; }
這裏匹配路由的邏輯主要在Illuminate\Routing\RouteCollection的match方法裏:
public function match(Request $request) { $routes = $this->get($request->getMethod()); $route = $this->matchAgainstRoutes($routes, $request); if (! is_null($route)) { return $route->bind($request); } $others = $this->checkForAlternateVerbs($request); if (count($others) > 0) { return $this->getRouteForMethods($request, $others); } throw new NotFoundHttpException; }
$routes = $this->get($request->getMethod());表示根據當前請求方式匹配路由。