Laravel 2.2 建立 Application 例項

myjob發表於2019-06-01

簡述

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->instanceIlluminate\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';
}
  • 通過分析,可以看出這個檔案主要是放著我們自己引入第三方的 ServiceProvidersaliases,我們對照專案根路徑的 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\ApplicationIlluminate\Contracts\Container\ContainerIlluminate\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 例項

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章