前言
上一篇文章,研究了larvael框架入口檔案 index.php,這一篇深入研究 獲取框架例項底層程式碼
至於 composer自動載入原始碼分析請到這觀看
bootstrap/app.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;
- 第一步 獲取應用例項
-
第二步 繫結框架核心 http核心,控制檯核心,異常處理單例
Illuminate\Foundation\Application檔案分析
1. 引入
use Closure; //PHP 匿名函式類
use RuntimeException; //php 執行異常類
use Illuminate\Support\Arr; //框架支援的arr類
use Illuminate\Support\Str;//框架支援的str類
use Illuminate\Http\Request;//框架http 請求類
use Illuminate\Support\Collection;//框架支援的集合類
use Illuminate\Container\Container;//框架容器核心類
use Illuminate\Filesystem\Filesystem;//框架檔案系統的檔案類
use Illuminate\Log\LogServiceProvider;//框架日誌元件日誌服務提供者
use Illuminate\Support\ServiceProvider;//框架支援的服務提供者
use Illuminate\Events\EventServiceProvider;//框架事件系統的事件服務提供者
use Illuminate\Routing\RoutingServiceProvider;//框架路由元件的路由服務提供者
use Symfony\Component\HttpKernel\HttpKernelInterface;//Symfony框架http核心介面
use Symfony\Component\HttpKernel\Exception\HttpException;//Symfony框架http核心異常下http異常類
use Illuminate\Contracts\Http\Kernel as HttpKernelContract;//框架http核心契約
use Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables;//框架基礎載入環境變數類
use Symfony\Component\HttpFoundation\Request as SymfonyRequest;//Symfony框架http請求類
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;//Symfony框架http核心異常類
use Illuminate\Contracts\Foundation\Application as ApplicationContract;//框架契約基礎類
2. 定義類定義屬性
class Application extends Container implements ApplicationContract, HttpKernelInterface
{//定義Application物件繼承Container,實現ApplicationContract, HttpKernelInterface
const VERSION = '5.7.12'; //版本號
protected $basePath;//基本路徑
protected $hasBeenBootstrapped = false;//應用程式是否已被自舉。
protected $booted = false;//引用程式是否已啟動
protected $bootingCallbacks = [];//引導回撥的陣列
protected $bootedCallbacks = [];//啟動回撥的陣列
protected $terminatingCallbacks = [];//終止回撥的陣列
protected $serviceProviders = [];//註冊的服務提供者
protected $loadedProviders = [];//載入的服務提供者的名稱
protected $deferredServices = [];//延遲的服務提供者的名稱
protected $databasePath;//資料庫路徑
protected $storagePath;//儲存路徑
protected $environmentPath;//環境變數路徑
protected $environmentFile = '.env';//環境變數檔案
protected $namespace;//應用程式名稱空間
3. 建構函式,初始化處理
1、setBasePath // 設定應用基礎路徑
2、registerBaseBindings // 繫結容器中的所有應用程式路徑
3、registerBaseServiceProviders // 註冊基本繫結
4、registerCoreContainerAliases //註冊容器核心類別名
public function __construct($basePath = null)
{
if ($basePath) {
$this->setBasePath($basePath);
}
$this->registerBaseBindings();
$this->registerBaseServiceProviders();
$this->registerCoreContainerAliases();
}
4. 設定基礎路徑
public function setBasePath($basePath)
{
$this->basePath = rtrim($basePath, '\/');
$this->bindPathsInContainer();
return $this;
}
5. 繫結容器中的所有應用程式路徑
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());
}
6. 將基本繫結註冊到容器中
1、static::setInstance
2、instance
protected function registerBaseBindings()
{
static::setInstance($this);//設定全域性容器可用例項
$this->instance('app', $this);//繫結當前例項到app
$this->instance(Container::class, $this);//繫結當前例項到Container
$this->instance(PackageManifest::class, new PackageManifest(
new Filesystem, $this->basePath(), $this->getCachedPackagesPath()
));//檔案例項繫結到PackageManifest
}
7. 註冊所有的基礎服務提供者
1、register
protected function registerBaseServiceProviders()
{
$this->register(new EventServiceProvider($this));//1. 事件服務提供者
$this->register(new LogServiceProvider($this));// 2. 日誌服務提供者
$this->register(new RoutingServiceProvider($this)); //3. 路由服務提供者
}
8. 註冊方法剖析
1、getProvider
2、resolveProvider
3、bind
4、singleton
5、markAsRegistered
6、bootProvider
7、參考連結
public function register($provider, $force = false)
{
//判斷該服務提供者是否已註冊,已經註冊直接返回例項
if (($registered = $this->getProvider($provider)) && ! $force) {
return $registered;
}
//如果傳入一個字串(類名)
if (is_string($provider)) {
//直接去例項化(直白點就是new 這個類)
$provider = $this->resolveProvider($provider);
}
//如果該服務提供者存在register方法,直接執行
if (method_exists($provider, 'register')) {
$provider->register();
}
//如果服務提供者包含bindings屬性,則進行
if (property_exists($provider, 'bindings')) {
foreach ($provider->bindings as $key => $value) {
$this->bind($key, $value);
}
}
//如果服務提供者包含singletons屬性,則進行
if (property_exists($provider, 'singletons')) {
foreach ($provider->singletons as $key => $value) {
$this->singleton($key, $value);
}
}
//標記該程式為已註冊,加入已註冊服務提供者陣列和已載入服務提供者陣列
$this->markAsRegistered($provider);
//如果程式已啟動,呼叫服務提供者boot方法
if ($this->booted) {
$this->bootProvider($provider);
}
//返回例項
return $provider;
}
9. 獲取版本號
public function version()
10. 註冊回撥以在載入環境之後執行
public function afterLoadingEnvironment(Closure $callback)
{
return $this->afterBootstrapping(
LoadEnvironmentVariables::class, $callback
);
}
11. 註冊回撥以在載入程式之後執行
public function afterBootstrapping($bootstrapper, Closure $callback)
{
$this['events']->listen('bootstrapped: '.$bootstrapper, $callback);//監聽引導後事件
}
12. 註冊回撥在載入程式之前執行
public function beforeBootstrapping($bootstrapper, Closure $callback)
{
$this['events']->listen('bootstrapping: '.$bootstrapper, $callback);//監聽引導前事件
}
13. 是否程式自舉
public function hasBeenBootstrapped()
{
return $this->hasBeenBootstrapped;
}
14. 獲取app目錄路徑
public function path($path = '')
{
return $this->basePath.DIRECTORY_SEPARATOR.'app'.($path ? DIRECTORY_SEPARATOR.$path : $path);
}
15. 獲取laravel根路徑
public function basePath($path = '')
{
return $this->basePath.($path ? DIRECTORY_SEPARATOR.$path : $path);
}
16. 獲取bootstrap路徑
public function bootstrapPath($path = '')
{
return $this->basePath.DIRECTORY_SEPARATOR.'bootstrap'.($path ? DIRECTORY_SEPARATOR.$path : $path);
}
17. 獲取config路徑
public function configPath($path = '')
{
return $this->basePath.DIRECTORY_SEPARATOR.'config'.($path ? DIRECTORY_SEPARATOR.$path : $path);
}
18. 獲取database路徑
public function databasePath($path = '')
{
return ($this->databasePath ?: $this->basePath.DIRECTORY_SEPARATOR.'database').($path ? DIRECTORY_SEPARATOR.$path : $path);
}
19. 設定databsee路徑
public function useDatabasePath($path)
{
$this->databasePath = $path;
$this->instance('path.database', $path);//新增到容器裡
return $this;
}
20. 獲取資源resource路徑
public function resourcePath($path = '')
{
return $this->basePath.DIRECTORY_SEPARATOR.'resources'.($path ? DIRECTORY_SEPARATOR.$path : $path);
}
21. 獲取public路徑
public function publicPath()
{
return $this->basePath.DIRECTORY_SEPARATOR.'public';
}
22. 獲取語言包路徑
public function langPath()
{
return $this->resourcePath().DIRECTORY_SEPARATOR.'lang';
}
23. 獲取strorge的路徑
public function storagePath()
{
return $this->storagePath ?: $this->basePath.DIRECTORY_SEPARATOR.'storage';
}
24. 設定strorge的路徑
public function useStoragePath($path)
{
$this->storagePath = $path;
$this->instance('path.storage', $path);
return $this;
}
26. 獲取env路徑
public function environmentPath()
{
return $this->environmentPath ?: $this->basePath;
}
27. 設定env路徑
public function useEnvironmentPath($path)
{
$this->environmentPath = $path;
return $this;
}
28. 設定env檔案
public function loadEnvironmentFrom($file)
{
$this->environmentFile = $file;
return $this;
}
29. 獲取env檔案
public function environmentFile()
{
return $this->environmentFile ?: '.env';
}
29. 獲取env檔案路徑
public function environmentFilePath()
{
return $this->environmentPath().DIRECTORY_SEPARATOR.$this->environmentFile();
}
30. 獲取應用env配置
public function environment()
{
//判斷是否有引數
if (func_num_args() > 0) {
$patterns = is_array(func_get_arg(0)) ? func_get_arg(0) : func_get_args();
return Str::is($patterns, $this['env']);//字串搜尋
}
return $this['env'];
}
31. 環境是否本地開發
public function isLocal()
{
return $this['env'] === 'local';
}
32. 檢測應用當前環境 detect方法詳解
public function detectEnvironment(Closure $callback)
{
$args = $_SERVER['argv'] ?? null;//獲取cli模式下的引數
return $this['env'] = (new EnvironmentDetector)->detect($callback, $args);//
}
33. 確定應用是否在控制檯執行
public function runningInConsole()
{
if (isset($_ENV['APP_RUNNING_IN_CONSOLE'])) {
return $_ENV['APP_RUNNING_IN_CONSOLE'] === 'true';
}
return php_sapi_name() === 'cli' || php_sapi_name() === 'phpdbg';//cli模式或者phpdbg除錯模式
}
34. 確定應用是否執行在unit單元測試
public function runningUnitTests()
{
return $this['env'] === 'testing';
}
35. 註冊所有配置的提供程式
public function registerConfiguredProviders()
{
$providers = Collection::make($this->config['app.providers'])//建立一個集合例項從app.php獲取到的服務提供者陣列
->partition(function ($provider) {
return Str::startsWith($provider, 'Illuminate\\');//判斷是否已$needles字串開頭
});//*使用給定的回撥或鍵將集合劃分為兩個陣列。一部分框架核心一部分自定義
//將所有包的所有服務提供者類名填充到1的位置
$providers->splice(1, 0, [$this->make(PackageManifest::class)->providers()]);獲取所。
//存入服務提供者快取檔案
(new ProviderRepository($this, new Filesystem, $this->getCachedServicesPath()))
->load($providers->collapse()->toArray());
}
36. 如果已註冊服務提供程式例項存在,則獲取該例項
public function getProvider($provider)
{
return array_values($this->getProviders($provider))[0] ?? null;
}
37. 獲取所有註冊的服務提供者
public function getProviders($provider)
{
$name = is_string($provider) ? $provider : get_class($provider);//獲取類名稱
return Arr::where($this->serviceProviders, function ($value) use ($name) {
return $value instanceof $name;//判斷是否是例項
});
}
38. 類名解析服務提供者例項
public function resolveProvider($provider)
{
return new $provider($this);
}
39. 標記程式為已註冊
protected function markAsRegistered($provider)
{
$this->serviceProviders[] = $provider;
$this->loadedProviders[get_class($provider)] = true;
}
40. 載入所有延遲服務提供者
public function loadDeferredProviders()
{
foreach ($this->deferredServices as $service => $provider) {
$this->loadDeferredProvider($service);//迴圈載入延遲服務提供者
}
$this->deferredServices = [];//將延遲服務提供者置為空
}
41. 載入延遲服務提供者
public function loadDeferredProvider($service)
{
//判斷是否屬於延遲服務提供者
if (! isset($this->deferredServices[$service])) {
return;
}
//判斷是否已載入
if (! isset($this->loadedProviders[$provider])) {
//註冊延遲服務提供者
$this->registerDeferredProvider($provider, $service);
}
}
42. 註冊延遲服務提供者
public function registerDeferredProvider($provider, $service = null)
{
if ($service) {
unset($this->deferredServices[$service]);//將該服務清空出延時服務提供者陣列
}
$this->register($instance = new $provider($this));//註冊服務
if (! $this->booted) {
$this->booting(function () use ($instance) {
$this->bootProvider($instance);//啟動服務呼叫boot
});
}
}
43. 從容器中解析例項
1、getAlias
2、parent::make
public function make($abstract, array $parameters = [])
{
$abstract = $this->getAlias($abstract);//獲取別名
//如果是延遲服務提供者並且沒有例項化就載入延遲程式
if (isset($this->deferredServices[$abstract]) && ! isset($this->instances[$abstract])) {
$this->loadDeferredProvider($abstract);
}
return parent::make($abstract, $parameters);//使用父類方法解析例項並返回
}
44. 確定給定的抽象型別是否已經被繫結
public function bound($abstract)
{
return isset($this->deferredServices[$abstract]) || parent::bound($abstract);
}
45. 應用是否已啟動
public function isBooted()
{
return $this->booted;
}
46. 啟動應用
public function boot()
{
if ($this->booted) {
return;
}
//呼叫引導回撥
$this->fireAppCallbacks($this->bootingCallbacks);
array_walk($this->serviceProviders, function ($p) {
$this->bootProvider($p);//啟動服務提供者
});
$this->booted = true;//設定程式已啟動
//呼叫啟動後
$this->fireAppCallbacks($this->bootedCallbacks);
}
47. 啟動服務提供者
protected function bootProvider(ServiceProvider $provider)
{
if (method_exists($provider, 'boot')) {
return $this->call([$provider, 'boot']);
}
}
48. 設定應用引導回撥陣列
public function booting($callback)
{
$this->bootingCallbacks[] = $callback;
}
49. 設定應用啟動回撥陣列
public function booted($callback)
{
$this->bootedCallbacks[] = $callback;
if ($this->isBooted()) {
$this->fireAppCallbacks([$callback]);//如果已啟動,呼叫回撥
}
}
50. 呼叫程式回撥
protected function fireAppCallbacks(array $callbacks)
{
foreach ($callbacks as $callback) {
call_user_func($callback, $this);
}
}
51. handle
public function handle(SymfonyRequest $request, $type = self::MASTER_REQUEST, $catch = true)
{
return $this[HttpKernelContract::class]->handle(Request::createFromBase($request));
}
52. shouldSkipMiddleware
public function shouldSkipMiddleware()
{
return $this->bound('middleware.disable') &&
$this->make('middleware.disable') === true;
}```
### 53. <a name="getCachedServicesPath">獲取快取服務路徑</a>
1、<a href="#bootstrapPath">bootstrapPath</a>
```php
public function getCachedServicesPath()
{
return $this->bootstrapPath().'/cache/services.php';
}
54. 獲取packages.php的路徑
public function getCachedPackagesPath()
{
return $this->bootstrapPath().'/cache/packages.php';
}
55. 確定是否快取了應用程式配置
public function configurationIsCached()
{
return file_exists($this->getCachedConfigPath());
}
56. 獲取config檔案路徑
public function getCachedConfigPath()
{
return $this->bootstrapPath().'/cache/config.php';
}
57. 確定是否快取了路由配置
public function routesAreCached()
{
return $this['files']->exists($this->getCachedRoutesPath());
}
58. 獲取路由快取路徑
public function getCachedRoutesPath()
{
return $this->bootstrapPath().'/cache/routes.php';
}
59. 確定應用程式當前是否停機進行維護
public function isDownForMaintenance()
{
return file_exists($this->storagePath().'/framework/down');
}
60. 丟擲http異常、
public function abort($code, $message = '', array $headers = [])
{
if ($code == 404) {
throw new NotFoundHttpException($message);
}
throw new HttpException($code, $message, null, $headers);
}
61. 設定程式終止的回撥
public function terminating(Closure $callback)
{
$this->terminatingCallbacks[] = $callback;
return $this;
}
62. 執行終止程式回撥
public function terminate()
{
foreach ($this->terminatingCallbacks as $terminating) {
$this->call($terminating);
}
}
63. 獲取所有已載入的服務提供者
public function getLoadedProviders()
{
return $this->loadedProviders;
}
64. 獲取所有延遲載入服務提供者
public function getDeferredServices()
{
return $this->deferredServices;
}
65. 設定延遲服務提供者
public function setDeferredServices(array $services)
{
$this->deferredServices = $services;
}
66. 新增延遲服務提供者
public function addDeferredServices(array $services)
{
$this->deferredServices = array_merge($this->deferredServices, $services);
}
67. 驗證是否是延遲服務提供者
public function isDeferredService($service)
{
return isset($this->deferredServices[$service]);
}
68. 設定服務門面名稱空間
1、AliasLoader::setFacadeNamespace
public function provideFacades($namespace)
{
AliasLoader::setFacadeNamespace($namespace);
}
69. 獲取當前區域設定
public function getLocale()
{
return $this['config']->get('app.locale');
}
70. 設定當前區域
public function setLocale($locale)
{
$this['config']->set('app.locale', $locale);
$this['translator']->setLocale($locale);
$this['events']->dispatch(new Events\LocaleUpdated($locale));
}
71. 判斷當前區域
public function isLocale($locale)
{
return $this->getLocale() == $locale;
}
72. 在容器中註冊核心類別名
1、alias
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\Hashing\HashManager::class],
'hash.driver' => [\Illuminate\Contracts\Hashing\Hasher::class],
'translator' => [\Illuminate\Translation\Translator::class, \Illuminate\Contracts\Translation\Translator::class],
'log' => [\Illuminate\Log\LogManager::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);
}
}
}
73. 清除所有繫結和解決例項的容器
public function flush()
{
parent::flush();
$this->buildStack = [];
$this->loadedProviders = [];
$this->bootedCallbacks = [];
$this->bootingCallbacks = [];
$this->deferredServices = [];
$this->reboundCallbacks = [];
$this->serviceProviders = [];
$this->resolvingCallbacks = [];
$this->afterResolvingCallbacks = [];
$this->globalResolvingCallbacks = [];
}
74. 獲取名稱空間
public function getNamespace()
{
if (! is_null($this->namespace)) {
return $this->namespace;
}
$composer = json_decode(file_get_contents(base_path('composer.json')), true);
foreach ((array) data_get($composer, 'autoload.psr-4') as $namespace => $path) {
foreach ((array) $path as $pathChoice) {
if (realpath(app_path()) == realpath(base_path().'/'.$pathChoice)) {
return $this->namespace = $namespace;
}
}
}
throw new RuntimeException('Unable to detect application namespace.');
}
75. 執行給定的引導類陣列
public function bootstrapWith(array $bootstrappers)
{
$this->hasBeenBootstrapped = true;
foreach ($bootstrappers as $bootstrapper) {
$this['events']->dispatch('bootstrapping: '.$bootstrapper, [$this]);
$this->make($bootstrapper)->bootstrap($this);
$this['events']->dispatch('bootstrapped: '.$bootstrapper, [$this]);
}
}