簡述
laravel 框架執行
1 第一步,使用 Composer 完成自動載入
2 第二步
- 建立Application例項,
- 註冊專案路徑(如config、public)、
- 註冊專案基礎服務(如app例項、Container例項、第三方外掛)、
- 註冊專案服務提供者(Event、Log、Routing等)、
- 註冊專案服務提供者別名(auth等)
Illuminate\Foundation\Application
是框架最核心的類,管理整個框架的啟動、執行以及整個生命週期,並通過 ServiceProvider 將其他功能的模組載入框架。Application 是 Container 類的子類,所以也管理者框架中其他類的例項化、儲存等功能。
<?php
// 第一部分: 建立應用例項
$app = new Illuminate\Foundation\Application(
realpath(__DIR__.'/../')
);
// 第二部分: 完成核心繫結
$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
);
return $app;
構造方法
一共四個方法,逐個講
/**
* 註冊應用的基礎路徑並將路徑繫結到 APP 容器 、註冊基礎服務提供者至 APP 容器 、註冊核心容器別名至 APP 容器 等基礎服務的註冊工作
* Create a new Illuminate application instance.
*
* @param string|null $basePath
* @return void
*/
public function __construct($basePath = null)
{
//設定應用程式的絕對路徑
if ($basePath) {
$this->setBasePath($basePath);
}
//繫結 Application 物件、繫結 Container 物件,繫結 需要載入的第三方的外掛
$this->registerBaseBindings();
//在容器中註冊最基本的服務提供者(即ServiceProvider)
$this->registerBaseServiceProviders();
//在容器中註冊核心類別名
$this->registerCoreContainerAliases();
}
setBasePath
- 設定應用程式的絕對路徑
- 依賴於初始化時傳入的路徑引數,路徑為專案的根目錄。在 setBasePath 方法中,bindPathsInContainer 方法被呼叫。這裡又用到了 instance 方法,把路徑填入了類的 $instances 陣列中。
$this->instance
是Illuminate\Container
的成員方法,instance 函式的作用是繫結一個已有物件到容器中,這個物件在容器中共享並且可以通 過鍵獲取。
/**
* 設定應用程式的絕對路徑..
*
* @param string $basePath 程式碼裡面傳的是 realpath(__DIR__.'/../'),就是根目錄
* @return $this
*/
public function setBasePath($basePath)
{
$this->basePath = rtrim($basePath, '\/');
$this->bindPathsInContainer();
return $this;
}
/**
* Bind all of the application paths in the container.
* 繫結容器中的所有應用程式路徑
* @return void
*/
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
這裡同樣呼叫了 instance 方法,但與之前不同的是,這裡傳入的第二個引數是一個類,而之前的是一個字串。第一行它為 Application 類註冊了一個名為 $instance 的靜態例項,二三兩行則為 Application 例項中的 $instances 陣列屬性填入了兩個 key 為 'app' 和 Container::class,value 為 $this 的值。
前兩個主要是繫結 Application
物件和 Container
物件。重點分析 PackageManifest
物件之前,我們看看 $this->getCachedPackagesPath()
這個函式:
/**
* Register the basic bindings into the container.
* 將 bindings 註冊到容器中
* @return void
*/
protected function registerBaseBindings()
{
//將 $this 賦值給自身的 instance 靜態變 量
static::setInstance($this);
//繫結 Application 物件
//註冊一個例項到容器中,這個就變成了 $this->instances['app']=application例項
//你可以使用App::make('app')來取得一個容器物件
$this->instance('app', $this);
//繫結 Container 物件
//註冊一個例項到容器中, $this->instances['"Illuminate\Container\Container"]=Application 例項
$this->instance(Container::class, $this);
//繫結 需要載入的第三方的外掛
$this->instance(PackageManifest::class, new PackageManifest(
new Filesystem, $this->basePath(), $this->getCachedPackagesPath()
));
}
getCachedPackagesPath
這個就是 bootstrap/cache/packages.php
檔案路徑
/**
* Get the path to the cached packages.php file.
* 這個就是 bootstrap/cache/packages.php檔案
* @return string
*/
public function getCachedPackagesPath()
{
return $this->bootstrapPath().'/cache/packages.php';
}
- 通過分析,可以看出這個檔案主要是放著我們自己引入第三方的
ServiceProviders
和aliases
,我們對照專案根路徑的composer.json
你就可以證實了:
bootstrap/cache/packages.php
檔案
<?php return array (
'fideloper/proxy' =>
array (
'providers' =>
array (
0 => 'Fideloper\\Proxy\\TrustedProxyServiceProvider',
),
),
'encore/laravel-admin' =>
array (
'providers' =>
array (
0 => 'Encore\\Admin\\AdminServiceProvider',
),
'aliases' =>
array (
'Admin' => 'Encore\\Admin\\Facades\\Admin',
),
),
'laravel/tinker' =>
array (
'providers' =>
array (
0 => 'Laravel\\Tinker\\TinkerServiceProvider',
),
),
'rebing/graphql-laravel' =>
array (
'providers' =>
array (
0 => 'Rebing\\GraphQL\\GraphQLServiceProvider',
),
'aliases' =>
array (
'GraphQL' => 'Rebing\\GraphQL\\Support\\Facades\\GraphQL',
),
),
'tymon/jwt-auth' =>
array (
'aliases' =>
array (
'JWTAuth' => 'Tymon\\JWTAuth\\Facades\\JWTAuth',
'JWTFactory' => 'Tymon\\JWTAuth\\Facades\\JWTFactory',
),
'providers' =>
array (
0 => 'Tymon\\JWTAuth\\Providers\\LaravelServiceProvider',
),
),
'noh4ck/graphiql' =>
array (
'providers' =>
array (
0 => 'Graphiql\\GraphiqlServiceProvider',
),
),
'rollbar/rollbar-laravel' =>
array (
'providers' =>
array (
0 => 'Rollbar\\Laravel\\RollbarServiceProvider',
),
'aliases' =>
array (
'Rollbar' => 'Rollbar\\Laravel\\Facades\\Rollbar',
),
),
'fanly/log2dingding' =>
array (
'providers' =>
array (
0 => 'Fanly\\Log2dingding\\FanlyLog2dingdingServiceProvider',
),
),
);
專案根路徑的 composer.json
檔案
"require": {
"php": ">=7.0.0",
"encore/laravel-admin": "1.5.*",
"fanly/log2dingding": "^0.0.2",
"fideloper/proxy": "~3.3",
"guzzlehttp/guzzle": "^6.3",
"laravel/framework": "5.5.*",
"laravel/tinker": "~1.0",
"noh4ck/graphiql": "@dev",
"overtrue/phplint": "^1.1",
"rebing/graphql-laravel": "^1.10",
"rollbar/rollbar-laravel": "^2.3",
"tymon/jwt-auth": "^1.0@dev"
},
new PackageManifest()
回過頭來分析 new PackageManifest()
/**
* 這個函式是將 package.php 檔案的外掛陣列的 `providers`整合成一個 Collection 輸出,什麼是 Collection ,看之前的文章 Laravel Collections
*/
public function providers() {}
/**
* 外掛中的 `aliases` 整合成 Collection 輸出。
*
* @return array
*/
public function aliases() {}
/**
* 這個是關鍵,從 verdor/composer/installed.json 檔案中獲取所有通過 composer 安裝的外掛陣列,然後再通過用 `name` 繫結對應的 `ServiceProvider`,構成陣列,然後再排除每個外掛的 `dont-discover` 和專案 composer.json 填入的 `dont-discover`。
* 這也是 Laravel 包自動發現的核心所在。
*
*/
public function build()
{
$packages = [];
if ($this->files->exists($path = $this->vendorPath.'/composer/installed.json')) {
$packages = json_decode($this->files->get($path), true);
}
$ignoreAll = in_array('*', $ignore = $this->packagesToIgnore());
$this->write(collect($packages)->mapWithKeys(function ($package) {
return [$this->format($package['name']) => $package['extra']['laravel'] ?? []];
})->each(function ($configuration) use (&$ignore) {
$ignore = array_merge($ignore, $configuration['dont-discover'] ?? []);
})->reject(function ($configuration, $package) use ($ignore, $ignoreAll) {
return $ignoreAll || in_array($package, $ignore);
})->filter()->all());
}
/**
* 最後就把上面的滿足的 ServiceProvider 寫入到檔案中,就是上文我們說的 `bootstrap/cache/packages.php`
*/
protected function write(array $manifest) {}
registerBaseServiceProviders
這裡主要註冊三個 ServiceProvider
,在註冊過程中,ServiceProvider 的 register 方法會被執行,每一個 ServiceProvider 一般都會有 register 方法。
/**
* Register all of the base service providers.
*
* @return void
*/
protected function registerBaseServiceProviders()
{
//EventServieProvider —— 事件服務提供者
//EventServiceProvider這個服務提供者,其實是向容器註冊了一個key為events的物件
$this->register(new EventServiceProvider($this));
//日誌服務
$this->register(new LogServiceProvider($this));
//RoutingServiceProvider —— 路由服務提供者
//路由服務,這個裡面註冊了很多東西,從下圖可以看出來,路由以後單獨分析
$this->register(new RoutingServiceProvider($this));
//最後他們的例項都在 $this->bindings 裡面, 如下圖
}
registerCoreContainerAliases
這一步是在為一些核心類註冊別名,這些資料就會一個有規律,陣列都是介面和這個介面例項,這也就是laravel 裡的contracts
在呼叫此方法之前,我們想取得一個容器例項的做法是 App::make('app')
,現在我們可以使用App::make('Illuminate\Foundation\Application')
App::make('Illuminate\Contracts\Container\Container')
App::make('Illuminate\Contracts\Foundation\Application')
三種方法來取得一個容器例項,即Illuminate\Foundation\Application
、Illuminate\Contracts\Container\Container
、Illuminate\Contracts\Foundation\Application
三者都是app的別名;
/**
* Register the core class aliases in the container.
* 在容器中註冊核心類別名
* @return void
*/
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);
}
}
}
Application 例項
最後返回了一個Application 例項