callAfterResolving() des Service Providers verbindet genau diese beiden Anforderungen.
Diese Seite untersucht die Implementierung in Laravel Framework v13.35.0 und vertieft die Erweiterung aus boot(), die in der Laravel-Paketentwicklung beschrieben ist. Es handelt sich nicht um einen Hook, der auf den vollständigen Start der Anwendung wartet, sondern um einen Hook für die Auflösung eines bestimmten Service.
Warten, falls noch nicht aufgelöst – sofort ausführen, falls bereits aufgelöst
ServiceProvider::callAfterResolving() arbeitet in zwei Schritten:
- Es registriert den Callback über
afterResolving()des Containers. - Ist
resolved()true, holt es den Service mitmake()und ruft den Callback zusätzlich sofort auf.
make() für ihn auf. Bei einer gewöhnlichen Auflösung erstellt der Container das Objekt, wendet die Extender an, ruft die resolving-Callbacks auf und anschließend die afterResolving-Callbacks.
Beim gewöhnlichen Abruf eines Singletons gibt der Container die gespeicherte Instanz frühzeitig zurück, sodass
afterResolving nicht bei jedem Abruf ausgelöst wird. Wichtig ist: Wenn Sie lediglich afterResolving() registrieren, entgeht Ihrer Konfiguration ein Singleton, das bereits vor der Registrierung erzeugt wurde.
Der Validation-Factory eine Regel hinzufügen
Als Beispiel für eine paketeigene String-Regel registrieren wir die Regel nach der Auflösung vonvalidator. Der offizielle ValidationServiceProvider registriert diesen Schlüssel als Singleton, und der Provider selbst unterstützt Lazy Loading.
extend() in diesem Beispiel ist die API zur Regelregistrierung von Illuminate\Validation\Factory. Es ist eine andere Methode als das weiter unten beschriebene extend() des Containers. Da gewöhnliche benutzerdefinierte Regeln etwa bei leeren Werten nicht ausgeführt werden können, legen Sie die Pflichtangabe mit required fest. Details zum Entwurf der Regel selbst finden Sie unter Benutzerdefinierte Validierungsregeln.
Factory::extend() überschreibt das Array-Element mit demselben Regelnamen, sodass sich die Einträge nicht vermehren, wenn dieselbe Logik wie in diesem Beispiel erneut registriert wird. Versehen Sie öffentlich bereitgestellte Regelnamen jedoch mit einem paketspezifischen Präfix, damit Sie keine Regeln anderer Pakete überschreiben.
Der Callback wird ausgeführt, wenn die Factory aufgelöst wird. Die Prüf-Closure der Regel wird erst ausgeführt, wenn der Validator später den jeweiligen Wert validiert. Bei der Registrierung des Hooks werden keine Eingabewerte validiert.
Zielschlüssel und Prüfung auf Auflösung abstimmen
Bei den gewöhnlichen Auflösungsereignissen des Containers werden nicht nur Callbacks ausgewählt, die zum registrierten Schlüssel passen, sondern auch solche, die zum Typ des abgerufenen Objekts passen. Das voncallAfterResolving() sofort verwendete resolved($name) prüft dagegen für den um Aliase normalisierten Schlüssel, ob ein Auflösungs-Flag oder eine gespeicherte Instanz vorhanden ist. Es durchsucht nicht alle bereits erzeugten Objekte.
Dass ein bestimmtes Interface oder eine Klasse bereits aufgelöst ist, bedeutet daher nicht zwingend, dass resolved() auch für einen anderen Schlüssel true liefert. Prüfen Sie das tatsächliche Binding und die Aliase des Ziel-Service und testen Sie den bereits aufgelösten Fall mit demselben Schlüssel. Im obigen Beispiel wird validator angegeben, das der offizielle Provider registriert.
Außerdem umfasst resolved() auch die Aussage „wurde in der Vergangenheit aufgelöst“. Es bedeutet nicht, dass die aktuelle Instanz zwingend noch vorhanden ist.
Keine API, die „genau einmal ausführen“ garantiert
callAfterResolving() lässt den Callback registriert. Auch nach der sofortigen Ausführung wird der registrierte Callback ausgeführt, wenn das Objekt künftig neu aufgelöst wird. Ruft man die Methode erneut auf, wird zudem ein weiterer Callback hinzugefügt.
Besondere Vorsicht ist geboten, wenn ein gewöhnliches, nicht geteiltes Binding bereits aufgelöst wurde. In der untersuchten Implementierung läuft dann Folgendes ab:
- Der neue Callback wird registriert.
- Da
resolved()true ist, wirdmake()aufgerufen. make()erzeugt ein neues Objekt, und dessen Auflösungsereignis führt den Callback aus.- Auf dasselbe von
make()zurückgegebene Objekt wird der Callback zusätzlich sofort angewendet.
Zum Ersetzen von Objekten eine andere API verwenden
Der Rückgabewert einesafterResolving-Callbacks wird nicht dazu verwendet, das vom Container zurückgegebene Objekt zu ersetzen. Wenn Sie einen Decorator zurückgeben und den Service selbst austauschen möchten, ziehen Sie das extend() des Containers in Betracht. Die Closure dieser API muss vertragsgemäß den geänderten Service zurückgeben.
Ebenso ist callAfterResolving() kein Mechanismus, der alle Abhängigkeiten aktualisiert, die bereits in anderen Objekten gespeichert sind. Wenn es darum geht, Abhängigkeiten beim erneuten Binden zu aktualisieren, prüfen Sie rebinding() in der offiziellen Dokumentation sowie den Entwurf der betroffenen Klasse.
Die Anforderungen an Provider, die Services tatsächlich verzögert laden, finden Sie unter DeferrableProvider. Einen Hook zu verwenden und den Provider, der diesen Hook registriert, selbst verzögert zu laden, sind zwei verschiedene Dinge.
Bei Updates zu prüfende Kombinationen
Prüfen Sie vor einem Paket-Release nicht nur die gewöhnliche Startreihenfolge, sondern auch den Fall, dass ein anderer Provider den Service zuvor bereits verwendet hat.
Wenn Sie mit einem statischen Flag festhalten, dass etwas anwendungsweit bereits einmal verwendet wurde, kann die Konfiguration für Services fehlen, die in einem neuen Container oder in Tests neu erzeugt werden. Stellen Sie die Idempotenz der Konfiguration möglichst auf Ebene des Zielobjekts oder des zu registrierenden Schlüssels sicher.
Verwandte Seiten
Paket-Views überschreiben und aktualisieren
Ein Beispiel, wie loadViewsFrom() View-Namensräume über einen Hook nach der Auflösung registriert.
Paketübersetzungen überschreiben und aktualisieren
Die Registrierung von Namensräumen im Translator und der Umgang mit bereits geladenen Übersetzungen.
Herangezogene Primärquellen
- Offizielle Laravel-Dokumentation: Paketentwicklung
- Offizielle Laravel-Dokumentation: Container events, Extending bindings, Rebinding
- Offizielle Laravel-Dokumentation: Benutzerdefinierte Validierungsregeln
- ServiceProvider: callAfterResolving() und seine Verwendung bei der Ressourcenregistrierung
- Container: resolved(), resolve(), afterResolving()
- ValidationServiceProvider: Singleton-Registrierung von validator
- Validation Factory: Regelregistrierung mit extend()