Skip to main content
Wenn ein Paket einem Laravel-Service Funktionalität hinzufügt, soll die Instanz oft erst bei Bedarf erzeugt werden – und gleichzeitig soll die Konfiguration auch dann greifen, wenn die Instanz bereits existiert. Die protected-Methode 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:
  1. Es registriert den Callback über afterResolving() des Containers.
  2. Ist resolved() true, holt es den Service mit make() und ruft den Callback zusätzlich sofort auf.
Ist der Service noch nicht aufgelöst, ruft die Methode selbst kein 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 von validator. Der offizielle ValidationServiceProvider registriert diesen Schlüssel als Singleton, und der Provider selbst unterstützt Lazy Loading.
In der nutzenden Anwendung wird sie zusammen mit gewöhnlichen Regeln verwendet.
Das 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 von callAfterResolving() 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:
  1. Der neue Callback wird registriert.
  2. Da resolved() true ist, wird make() aufgerufen.
  3. make() erzeugt ein neues Objekt, und dessen Auflösungsereignis führt den Callback aus.
  4. Auf dasselbe von make() zurückgegebene Objekt wird der Callback zusätzlich sofort angewendet.
Auf diesem Weg wird der soeben registrierte Callback zweimal auf dasselbe Objekt angewendet. Übertragen Sie einen Entwurf, der nur den Abruf von Singletons berücksichtigt, nicht unverändert auf gewöhnliche Bindings.
Versenden Sie im Callback keine E-Mails, rufen Sie keine externen APIs auf, lösen Sie keine Abrechnungen aus und fügen Sie keine Listener bedingungslos hinzu. Beschränken Sie den Callback darauf, Konfiguration idempotent auf den Service anzuwenden, und gestalten Sie ihn so, dass sich Seiteneffekte bei erneuter Registrierung oder Auflösung nicht doppeln.

Zum Ersetzen von Objekten eine andere API verwenden

Der Rückgabewert eines afterResolving-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

Zuletzt geändert am 10. Oktober 2026