2016-09-23 5 views
2

ich die folgende Definition haben:Laravel latenten Dienstleister `provides` nicht aufgerufen wird

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]; 
    } 
} 

Und es gibt eine Instanz von SomeClass wie erwartet, mit der Ausnahme, dass nach der Dokumentation, wenn $defer gilt dann die provides() Methode sollte aufgerufen werden. Egal was ich $defer eingestellt habe, und egal ob ich tatsächlich nach einer Instanz von SomeClass frage oder nicht, provides() wird nie aufgerufen.

So wie ich bin für eine Instanz der Klasse fragen, ist wie folgt:

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

Haben Sie den Provider im Provider-Array in 'config/app.php' registriert? – user3158900

+0

@ user3158900 Ja. Der Anbieter funktioniert gut, außer provides() wird nie aufgerufen, also denke ich nicht, dass es richtig verzögert. – Julian

+0

Stellen Sie sicher, dass Laravels Container Instanzen von Klassen erstellen kann, ohne dass diese jemals bei einem Dienstanbieter registriert sind. Wenn also Laravel nichts von dem Dienstleister weiß, dann hat er keine Ahnung, dass er ihn verschieben muss. – user3158900

Antwort

3

Kurze Antwort:
kompilierten Manifest-Datei wird bereits von Rahmen zusammengestellt.

Auf der ersten Zeit, als Laravel die Anwendung erstellen (und löst alle Dienstleister in IoC) es im Cache gespeicherte Datei services.php namens schreibt (das heißt, die Manifest-Datei, platziert in: bootstrap/cache/services.php).
Also, wenn Sie den über php artisan clear-compiled kompilierten Befehl löschen, sollte Framework zwingen, die Manifest-Datei neu zu erstellen, und Sie könnten feststellen, dass provides Methode aufgerufen wird. Bei den nächsten Calls/Requests wird provides Methode nicht mehr aufgerufen.

Die Sequenz Boot von Rahmen ist fast wie folgt aus:

//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); 

Eine Bootstrappers Illuminate\Foundation\Bootstrap\RegisterProviders ist die \Illuminate\Foundation\Application::registerConfiguredProviders() und ruft ruft dann \Illuminate\Foundation\ProviderRepository::__construct() und schließlich:

\Illuminate\Foundation\ProviderRepository::load()

Wenn \Illuminate\Foundation\ProviderRepository::load() ist

Alle Diensteanbieter angerufen wird registriert und \Illuminate\Support\ServiceProvider::provides() heißen auch gut.

Und hier ist das Snippet Sie wissen sollten (von \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() ist der Ort, wo Sie Ihre provides() Verfahren ausgeführt wird.

+0

Danke für die sehr detaillierten Informationen. Ich bin mit dem Debugger durchgetreten und kann jetzt dank Ihrer Erklärung viel mehr verstehen, was passiert. – Julian

0

Nach meinen eigenen Tests scheint es, dass dies ein Problem ist (vielleicht ist "Problem" in diesem Fall ein starkes Wort).

Wenn Sie etwas bei einem Dienstanbieter registrieren, der den Namen einer Klasse hat, wird Laravel nur diese Klasse zurückgeben und alles ignorieren, was in dem Dienstanbieter ist. Ich begann das gleiche tun wie du ....

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'); 

Und $test eine Instanz von SomeClass ist. Aber wenn ich die folgende Änderung mache ...

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

Und verwenden

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

Dann trifft es die deffered Funktion und gibt den Text testerino.

Ich denke, das Problem hier ist, dass Laravel weiß, dass Sie gerade versuchen, eine Klasse zu greifen. In diesem Fall gibt es keinen Grund zu registrieren, was Sie versuchen, mit dem Container zu registrieren, Sie tun nichts, außer Laravel eine Instanz von App\SomeClass zu machen, wenn es eine Instanz von App\SomeClass machen soll.

Wenn Sie jedoch Laravel sagen Sie eine Instanz von App\SomeClass wünschen, wenn Sie App::make('test') nennen, dann muss es tatsächlich diese Klasse zu test binden, so ist, dann denke ich, es auf die Service-Provider zu zahlen beginnt.