extend() des Containers können Sie den aufgelösten Service in einen Decorator einhüllen, ohne die ursprüngliche Erzeugungslogik neu zu definieren.
Diese Seite bezieht sich auf den Standard-Branch 13.x der offiziellen Laravel-Dokumentation sowie auf die offizielle Implementierung und die Tests des neuesten Framework-Releases v13.40.0, geprüft am 9. November 2026. Neben dem Unterschied zu den Hooks nach der Service-Auflösung behandelt sie, wie lange eine Dekoration erhalten bleibt. Das hängt davon ab, ob beim Registrieren bereits eine Instanz gespeichert ist.
Konfiguration ergänzen und Objekt ersetzen trennen
Ein
extend()-Callback erhält den Ziel-Service und den Container. Sie können dasselbe Objekt konfigurieren und zurückgeben oder einen neuen Decorator zurückgeben. Wenn Sie das return vergessen, wird null an die weitere Auflösung übergeben.
Hier geht es um
Illuminate\Container\Container::extend(). Das ist eine andere API als das extend() von Managern wie Cache oder Mail, das einen Treibernamen und seine Erzeugungslogik registriert.Einen Decorator mit demselben Vertrag erstellen
Die ursprüngliche Implementierung einhüllen statt ersetzen
Zur Veranschaulichung legen Sie den folgenden Vertrag zum Formatieren von Nachrichten insrc/MessageFormatter.php ab. Auch die folgenden Klassen liegen jeweils in einer gleichnamigen PHP-Datei unter src/.
PlainMessageFormatter als Typ angeben, kann ein Decorator, der nur dieselbe Schnittstelle implementiert, sie nicht ersetzen. Machen Sie MessageFormatter zur öffentlichen Abhängigkeit und halten Sie den Vertrag für Argumente, Rückgabewerte, Exceptions usw. ein. Dynamisches Weiterleiten von Methoden allein ersetzt keine Implementierung einer PHP-Schnittstelle.
Die Dekoration im Provider registrieren
src/PrefixServiceProvider.php, das die Dekoration bereitstellt, überschreibt die Erzeugungsweise nicht, sondern registriert nur eine Erweiterung im Container. Zum Laden des Providers siehe Interne Struktur der automatischen Paket-Erkennung.
$formatter als Ziel-Service. Ein erneutes make() desselben Schlüssels im Callback kann eine zirkuläre Auflösung verursachen.
Binden Sie die ursprüngliche Implementierung in der nutzenden Anwendung, zum Beispiel in AppServiceProvider::register().
extend() auch vor dem bind() des Ziels registrieren. Ist das Ziel noch nicht aufgelöst und keine Instanz gespeichert, erzeugt die Registrierung allein den ursprünglichen Service nicht. Auch die offiziellen Tests prüfen die verzögerte Erzeugung und die Registrierung vor dem Binden.
Danach gibt app(MessageFormatter::class)->format('Versendet.') den Wert [Acme] Versendet. zurück. Auch wenn die ursprüngliche Implementierung durch eine andere MessageFormatter-Implementierung ersetzt wird, hüllt der Decorator diese ein.
Ziel und Reihenfolge der Dekoration verstehen
Bei einer normalen Auflösung erzeugt der Container den ursprünglichen Service, ersetzt ihn in Registrierungsreihenfolge durch den Rückgabewert jedes Extenders, speichert ihn als geteilte Instanz und ruft dann die Hooks nach der Auflösung auf. Registrieren Sie A und dann B, ist das ErgebnisB(A(ursprünglicher Service)). Methodenaufrufe der Nutzer beginnen beim äußeren B, verwechseln Sie also nicht Registrierungsreihenfolge und Startreihenfolge der Verarbeitung. Wenn Sie von der Dekorationsreihenfolge mehrerer Pakete abhängen, legen Sie die Reihenfolge in der nutzenden Anwendung explizit fest und testen Sie die Kombination.
Extender gelten für den Auflösungsschlüssel, nachdem der Container Aliase normalisiert hat. Eine Registrierung auf MessageFormatter::class wird nicht schon deshalb angewendet, weil die Typen übereinstimmen, wenn PlainMessageFormatter::class direkt aufgelöst wird. Verwenden Sie einheitlich die Schnittstelle.
Dekorationen gespeicherter Instanzen bleiben anders erhalten
Container::extend() in v13.40.0 verzweigt danach, ob eine Instanz des Ziels im Container gespeichert ist.
Die letzte Zeile ist wichtig. Wenn Sie ein aufgelöstes Singleton dekorieren und danach
forgetInstance() aufrufen oder denselben Schlüssel neu binden, wird eine nur sofort angewendete Dekoration bei der nächsten Erzeugung nicht erneut angewendet. Das unterscheidet sich von einer Registrierung im Extender-Array vor der ersten Auflösung.
Auch scoped() verwendet gespeicherte Instanzen. Gehen Sie also nicht davon aus, dass eine mitten im Scope spät registrierte Dekoration für den nächsten Scope registriert bleibt. Prüfen Sie dies zusammen mit dem Lebenszyklus-Design für Paket-Services.
Wenn Sie außerdem später ein Objekt direkt mit instance() speichern, durchläuft es nicht die Schleife, die Extender bei normalen Erzeugungen anwendet. Auch bei Ersetzungen in Tests gilt nicht immer: „Es ist registriert, also wird das übergebene Objekt automatisch dekoriert.“
Bereits verteilte Referenzen werden nicht automatisch ersetzt
Wenn Sie wie in diesem Beispiel einen neuen Wrapper zurückgeben, ändert sich das ursprüngliche Objekt nicht, das bereits in einer Eigenschaft eines anderen Service gespeichert ist, auch wenn sich der gespeicherte Wert im Container ändert. Einerebinding()-Benachrichtigung übergibt den neuen Service an registrierte Callbacks. Sie sucht nicht alle Abhängigen und schreibt sie um.
Auch das Registrieren eines Benachrichtigungs-Callbacks mit rebinding() ist nicht immer verzögert. Ist das Ziel bereits gebunden, wird auch bei der Registrierung das Ergebnis von make() zurückgegeben. Auf dem Weg, bei dem Sie ein früher aufgelöstes normales Binding mit extend() erweitern, kann für die Benachrichtigung ebenfalls make() aufgerufen werden, wenn ein Rebound-Callback existiert. Gehen Sie nicht pauschal davon aus, dass Initialisierung und Nebeneffekte „bei der nächsten Verwendung“ stattfinden.
Doppelte Dekoration und übernommenen Zustand vermeiden
extend() entfernt Registrierungen derselben Closure oder desselben Decorators nicht automatisch als Duplikate. Wird register() dieses Providers zweimal auf denselben Container angewendet, kann auch das Präfix doppelt erscheinen.
- Bündeln Sie die Registrierung von Dekorationen im festen Boot-Pfad des Providers und wiederholen Sie sie nicht pro Request oder Job.
- Verhindern Sie Duplikate nicht mit einem einzelnen statischen Flag, das alle Container teilen. Tests und neue Anwendungen würden benötigte Registrierungen verpassen.
- Die Dekoration eines Singletons bleibt für dieselbe geteilte Lebensdauer erhalten. Binden Sie nicht den aktuellen Benutzer, Request o. Ä. fest an den Wrapper.
forgetExtenders()löscht nur das Registrierungs-Array. Bereits erzeugte Wrapper werden dadurch nicht zurückverwandelt.
Registrierungszeitpunkte mit demselben Container testen
Das folgende PHPUnit-Beispiel setzt voraus, dass die obigen Klassen über den Autoload des Pakets geladen werden können. Es wendetregister() des Providers direkt auf einen Container an und prüft, wie lange die Dekoration erhalten bleibt, ohne HTTP oder externe Dienste. Prüfen Sie die automatische Erkennung und den Start der gesamten Anwendung separat mit Laravel-Pakete mit Orchestra Testbench testen.
forgetInstance() allein nicht als API zum Entfernen oder Neuregistrieren von Dekorationen.
Prüfen Sie vor einem Release auch die Reihenfolge mehrerer Extender, doppelte Registrierungen, Ersetzungen per instance() und Referenzen, die Nutzer bereits halten. Wenn Sie weitere Laravel-Versionen unterstützen, lesen Sie neben Auflösungsschlüsseln und Typverträgen auch die Verzweigungen in extend() und die offiziellen Tests erneut.
Verwandte Seiten
Hooks nach der Service-Auflösung in Paketen
Behandelt callAfterResolving(), das Objekte konfiguriert, ohne sie zu ersetzen, und worauf bei der Anzahl der Ausführungen zu achten ist.
Lebenszyklus-Design für Paket-Services
Behandelt bind, singleton und scoped sowie die Abhängigkeiten, die erzeugte Services festhalten.
Herangezogene Primärquellen
- Offizielle Laravel-Dokumentation: Extending bindings und Rebinding
- Offizielle Laravel-Dokumentation: Service Providers
- Offizielle Laravel-Dokumentation: Paketentwicklung und automatische Erkennung
- Container: extend(), resolve(), rebound(), instance(), forgetExtenders()
- Offizielle Tests: verzögerte Erzeugung, Registrierung vor dem Binden, Aliase, mehrere Extender
- ServiceProvider: Konstruktor und register()