2016-09-23 107 views
2

我有如下定義:Laravel延期服務提供商`provides`不會被調用

namespace App\Providers; 

use Illuminate\Support\ServiceProvider; 
use App\SomeClass; 

class SomeProvider extends ServiceProvider 
{ 
    protected $defer = true; 

    public function register() 
    { 
     $this->app->bind(SomeClass::class, function ($app) 
     { 
      return new SomeClass(); 
     }); 
    } 

    public function provides() 
    { 
     die("This never gets called"); 
     return [SomeClass::class]; 
    } 
} 

而且它的預期收益SomeClass的實例,但根據文件,如果$defer爲真,那麼provides()方法應該被調用。不管我怎麼設置$defer到,而且無論如果我真的要求的SomeClass或不是一個實例,provides()不會被調用。

我要求的類的實例的方法如下:

App::make('SomeClass'); 
+0

您是否在'config/app.php'中的provider數組中註冊了提供程序? – user3158900

+0

@ user3158900是的。提供程序工作正常,但提供()不會被調用,所以我不認爲它正常推遲。 – Julian

+0

只要確保因爲Laravel的容器實際上可以創建類的實例,而不需要他們在服務提供者中註冊。所以如果Laravel不知道服務提供商,那麼它不知道它需要推遲它。 – user3158900

回答

3

簡短的回答:
您編譯清單文件已經被編譯框架。

在其寫入名爲緩存文件中的第一次,當Laravel構建應用程序(和解決所有的服務提供商在IOC) services.php(即清單文件,放置於:bootstrap/cache/services.php)。
因此,如果您清除通過php artisan clear-compiled命令編譯它應該迫使框架重建清單文件,你可以注意的是provides方法被調用。 在接下來的調用/請求provides方法不再被調用。

框架啓動的順序幾乎是這樣的:

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

$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class); 
    \Illuminate\Foundation\Http\Kernel::__construct(); 
    \Illuminate\Foundation\Http\Kernel::handle(); 
     \Illuminate\Foundation\Http\Kernel::sendRequestThroughRouter(); 
      \Illuminate\Foundation\Http\Kernel::bootstrap(); 
       \Illuminate\Foundation\Application::bootstrapWith(); 
        # where $bootstrapper is a item from \Illuminate\Foundation\Http\Kernel::$bootstrappers 
        # and $this is instance of \Illuminate\Foundation\Application 
        \Illuminate\Foundation\Application::make($bootstrapper)->bootstrap($this); 

一個bootstrappers的是Illuminate\Foundation\Bootstrap\RegisterProviders其中 調用\Illuminate\Foundation\Application::registerConfiguredProviders(),然後調用 \Illuminate\Foundation\ProviderRepository::__construct()最後:

\Illuminate\Foundation\ProviderRepository::load()

\Illuminate\Foundation\ProviderRepository::load()是召集所有服務供應商註冊和 \Illuminate\Support\ServiceProvider::provides()也被稱爲好。

這裏是你應該知道(從\Illuminate\Foundation\ProviderRepository::load)的片段:

/** 
    * Register the application service providers. 
    * 
    * @param array $providers 
    * @return void 
    */ 
    public function load(array $providers) 
    { 
     $manifest = $this->loadManifest(); 

     // First we will load the service manifest, which contains information on all 
     // service providers registered with the application and which services it 
     // provides. This is used to know which services are "deferred" loaders. 
     if ($this->shouldRecompile($manifest, $providers)) { 
      $manifest = $this->compileManifest($providers); 
     } 

     // Next, we will register events to load the providers for each of the events 
     // that it has requested. This allows the service provider to defer itself 
     // while still getting automatically loaded when a certain event occurs. 
     foreach ($manifest['when'] as $provider => $events) { 
      $this->registerLoadEvents($provider, $events); 
     } 

     // We will go ahead and register all of the eagerly loaded providers with the 
     // application so their services can be registered with the application as 
     // a provided service. Then we will set the deferred service list on it. 
     foreach ($manifest['eager'] as $provider) { 
      $this->app->register($this->createProvider($provider)); 
     } 

     $this->app->addDeferredServices($manifest['deferred']); 
    } 

\Illuminate\Foundation\ProviderRepository::compileManifest()是執行你的provides()方法的地方。

+0

感謝您提供非常詳細的信息。我正在逐步調試,現在我可以瞭解更多發生了什麼感謝您的解釋。 – Julian

0

做我自己的測試後,似乎這是一個問題(也許「問題」就是在這種情況下,強烈的字眼)。

如果您在註冊具有類的名稱服務提供的東西,Laravel只是要返回類和無視無論是在服務提供商。我開始做同樣的事情,你做了....

protected $defer = true; 

public function register() 
{ 
    $this->app->bind(SomeClass::class, function ($app) 
    { 
     return new SomeClass(); 
    }); 
} 

public function provides() 
{ 
    dd('testerino'); 
} 

$test = \App::make('App\SomeClass'); 

而且$testSomeClass一個實例。但是,如果我做出以下更改...

$this->app->bind('test', function ($app) { ... } 

並使用

$test = \App::make('test'); 

然後它擊中deffered功能和輸出文本testerino

我認爲這裏的問題是,Laravel知道你只是想搶班。在這種情況下,沒有理由註冊你想要在容器中註冊的內容,除非告訴Laravel創建App\SomeClass的實例,否則你沒有做任何事情,因爲它應該創建App\SomeClass的實例。

但是,如果你告訴Laravel你想要的App\SomeClass一個實例,當你調用App::make('test'),那麼它實際需要的那類結合test所以後來我想它開始注重服務提供商。