Laravel 原始碼筆記 容器 register 方法

php_yt發表於2020-03-07

概念

嚮應用程式註冊服務提供者

服務容器中的變化

$app = new Illuminate\Foundation\Application(
    realpath(__DIR__.'/../')
);

$app->register(new Illuminate\Events\EventServiceProvider($app));
//$app->register('Illuminate\Events\EventServiceProvider');

dd($app);

列印後

Laravel 原始碼筆記 容器 register 方法
影響的屬性有:
所有已註冊的服務提供商
protected $serviceProviders = [];
載入的服務提供者的名稱
protected $loadedProviders = [];

可能會影響的屬性有:
容器的繫結
protected $bindings = [];

原始碼

下方每個程式碼塊,只解釋一行程式碼。

第一行

/**
* 嚮應用程式註冊服務提供者。返回provider例項。
*/
public function register($provider, $options = [], $force = false)
{
    /*
    * 如果已經註冊了並且非強制註冊,則返回例項
    *
    * -->假設我們已經註冊過了
    */
    if (($registered = $this->getProvider($provider)) && ! $force) {
            return $registered;
    }
    ...
}

/*
* 獲取註冊的服務提供者例項(如果它存在的話)
*
* 此時 $provider = new Illuminate\Events\EventServiceProvider($app);
* 或 'Illuminate\Events\EventServiceProvider'
*
* return EventServiceProvider {#4 ▶}
*/
public function getProvider($provider)
{
    //array_values():只提取陣列中的鍵值,忽略鍵名。
    return array_values($this->getProviders($provider))[0] ?? null;
}

/*
* 從serviceProviders屬性中查詢是該例項是否存在
*
* return array
*/
 public function getProviders($provider)
 {
    // get_class():php內建函式獲得類名
    // 此時 $name = 'Illuminate\Events\EventServiceProvider'
     $name = is_string($provider) ? $provider : get_class($provider);

    return Arr::where($this->serviceProviders, function ($value) use ($name) {
        return $value instanceof $name;
    });
}
/*
* 使用給定的回撥篩選陣列
*
* 此時$array = array( EventServiceProvider {#4 ▶} )
*
* return array( 0 => EventServiceProvider {#4 ▶} )
*/
class Arr{

    public static function where($array, callable $callback)
    {
        //該函式把輸入陣列中的每個鍵值傳給回撥函式。如果回撥函式返回 true,則把輸入陣列中的當前鍵值返回給結果陣列。陣列鍵名保持不變。
        return array_filter($array, $callback, ARRAY_FILTER_USE_BOTH);
    }
}

第二行

public function register($provider, $options = [], $force = false)
{
    // 如果已經註冊過,並且非強制註冊,則返回該例項
    if (($registered = $this->getProvider($provider)) && ! $force) {
        return $registered;
    }
    //如果給定的“provider”是一個字串,我們將解析它
    if (is_string($provider)) {
        $provider = $this->resolveProvider($provider);
    }
}

/*
* 從類名解析服務提供者例項
*
* 此時$provider = 'Illuminate\Events\EventServiceProvider'
*
* return new Illuminate\Events\EventServiceProvider($this);
*/
public function resolveProvider($provider)
{
    return new $provider($this);
}

第三行

public function register($provider, $options = [], $force = false)
{
    // 如果已經註冊過,並且非強制註冊,則返回該例項
    if (($registered = $this->getProvider($provider)) && ! $force) {
        return $registered;
    }

    //如果給定的“provider”是一個字串,我們將解析它
    if (is_string($provider)) {
        $provider = $this->resolveProvider($provider);
    }

    // 如果該 serviceProvider 中有 register 方法,則執行
    if (method_exists($provider, 'register')) {
        $provider->register();
    }
}

/*
* EventServiceProvider類
*/
namespace Illuminate\Events;
use Illuminate\Support\ServiceProvider;
class EventServiceProvider extends ServiceProvider{

    public function register()
    {
        /***注意***列印圖中bindings屬性中的'events'是這時候新增進去的*/
        $this->app->singleton('events', function ($app) {
            ...
        });
    }
}

第四行

public function register($provider, $options = [], $force = false)
{
    // 如果已經註冊過,並且非強制註冊,則返回該例項
    if (($registered = $this->getProvider($provider)) && ! $force) {
        return $registered;
    }

    //如果給定的“provider”是一個字串,我們將解析它
    if (is_string($provider)) {
        $provider = $this->resolveProvider($provider);
    }

    //呼叫ServiceProvider中的register()方法註冊服務
    if (method_exists($provider, 'register')) {
        $provider->register();
    }

    //--> 將該provider做上已註冊的標記
    $this->markAsRegistered($provider);
}

/*
* param $provider = EventServiceProvider {#4 ▶}
*/
protected function markAsRegistered($provider)
{
    //加入所有已註冊的服務提供商
    $this->serviceProviders[] = $provider;

    //已載入的服務提供者的名稱
    $this->loadedProviders[get_class($provider)] = true;
}

第五行及最後一行

public function register($provider, $options = [], $force = false)
{
    // 如果已經註冊過,並且非強制註冊,則返回該例項
    if (($registered = $this->getProvider($provider)) && ! $force) {
        return $registered;
    }

    //如果給定的“provider”是一個字串,我們將解析它
    if (is_string($provider)) {
        $provider = $this->resolveProvider($provider);
    }

    //呼叫EventServiceProvider中的register()方法註冊服務
    if (method_exists($provider, 'register')) {
        $provider->register();
    }

    //將該provider做上已註冊的標記
    $this->markAsRegistered($provider);

    //-->如果應用程式已經啟動,我們將在provider類上呼叫這個啟動方法,這樣它就有機會執行它的啟動邏輯,併為這個開發人員的應用程式邏輯的任何使用做好準備。
    if ($this->booted) {
        $this->bootProvider($provider);
    }
    return $provider;
}

// 如果該 serviceProvider 中有 boot 方法,則呼叫
protected function bootProvider(ServiceProvider $provider)
{
    if (method_exists($provider, 'boot')) {
        return $this->call([$provider, 'boot']);
    }
}

/*
* 何時啟動?
*
* 在index.php中的 執行handle()時 $this->booted = true
*/
$response = $kernel->handle(
    $request = Illuminate\Http\Request::capture()
);

總結

  1. 如果 serviceProviders 屬性陣列中已經存在這個例項,則直接返回。這裡判斷是否存在的方式是遍歷該物件陣列,用 instanceof 判斷一個物件是不是某個型別的例項。
  2. 因為我們要儲存例項,所以如果 serviceProvider 傳入的是字串,將解析它。
  3. 如果該 serviceProvider 包含 register 方法,則呼叫。
  4. 將該 serviceProvider 做上已註冊的標記,即將例項儲存在 serviceProviders 屬性中 ,loadedProviders屬性中標記已載入。
  5. 如果應用已經啟動,則啟動該 serviceProvider 。應用何時啟動?執行 $response = $kernel->handle($request) 時。
本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章