Naar hoofdinhoud gaan
Soms wil een package gedrag toevoegen en tegelijk de service-implementatie behouden die de gebruikende applicatie heeft gekozen. Met extend() van de container kun je de geresolvede service in een decorator verpakken zonder de oorspronkelijke aanmaaklogica opnieuw te definiëren. Deze pagina verwijst naar de standaardbranch 13.x van de officiële Laravel-documentatie en naar de officiële implementatie en tests van de nieuwste Framework-release v13.40.0, gecontroleerd op 9 november 2026. Naast het verschil met hooks na het resolven van packageservices behandelt ze hoe lang een decoratie behouden blijft. Dat hangt af van of er bij het registreren al een instantie is opgeslagen.

Configuratie toevoegen en het object vervangen scheiden

Een extend()-callback ontvangt de doelservice en de container. Je kunt hetzelfde object configureren en retourneren, of een nieuwe decorator retourneren. Vergeet je de return, dan wordt null doorgegeven aan de rest van het resolveproces.
Het gaat hier om Illuminate\Container\Container::extend(). Dat is een andere API dan de extend() van managers zoals Cache en Mail, die een drivernaam en de aanmaaklogica registreert.

Een decorator maken die hetzelfde contract implementeert

De oorspronkelijke implementatie verpakken in plaats van vervangen

Ter illustratie plaats je het volgende contract voor het opmaken van berichten in src/MessageFormatter.php. Ook de onderstaande klassen staan elk in een PHP-bestand met dezelfde naam in src/.
De ongedecoreerde implementatie retourneert de invoer ongewijzigd.
De decorator implementeert dezelfde interface en delegeert naar de ontvangen service. In dit voorbeeld voegt hij een packagespecifiek voorvoegsel toe aan de returnwaarde van de gedelegeerde.
Als gebruikers de concrete klasse PlainMessageFormatter als type opgeven, kan een decorator die alleen dezelfde interface implementeert die niet vervangen. Maak MessageFormatter de afhankelijkheid die je publiceert en behoud het contract voor argumenten, returnwaarden, excepties enzovoort. Alleen dynamisch methoden doorsturen is geen vervanging voor het implementeren van een PHP-interface.

De decoratie registreren in een provider

src/PrefixServiceProvider.php, dat de decoratie levert, overschrijft niet hoe de service wordt aangemaakt. Het registreert alleen een extensie in de container. Zie voor het laden van de provider De interne structuur van package discovery.
De callback ontvangt als tweede argument ook de container, maar dit voorbeeld heeft die niet nodig en declareert hem daarom niet. Gebruik het argument $formatter als doelservice. Dezelfde sleutel opnieuw met make() resolven binnen de callback kan een circulaire resolutie veroorzaken. Bind de oorspronkelijke implementatie in de gebruikende applicatie, bijvoorbeeld in AppServiceProvider::register().
Je kunt extend() ook registreren vóór de bind() van het doel. Als het doel nog niet is geresolved en er geen instantie is opgeslagen, maakt de registratie alleen de oorspronkelijke service niet aan. Ook de officiële tests controleren uitgestelde aanmaak en registratie vóór de binding. Daarna retourneert app(MessageFormatter::class)->format('Verzonden.') de waarde [Acme] Verzonden.. Ook als de oorspronkelijke implementatie wordt vervangen door een andere MessageFormatter-implementatie, verpakt de decorator die implementatie.
Registreer als uitgangspunt vóór de eerste resolutie. Een andere provider kan de service eerder gebruiken, dus de registratie alleen naar boot() verplaatsen voorkomt geen resolutie vóór de registratie. Als je bovendien een provider die alleen extensies registreert zomaar uitgesteld maakt, kan de registratie zelf haar startvoorwaarde verliezen.

Begrijpen waar decoratie wordt toegepast en in welke volgorde

Bij een normale resolutie maakt de container de oorspronkelijke service aan, vervangt hem in registratievolgorde door de returnwaarde van elke extender, slaat hem op als gedeelde instantie en roept daarna de hooks na het resolven aan. Registreer je eerst A en dan B, dan is het resultaat B(A(oorspronkelijke service)). Methodeaanroepen van gebruikers beginnen bij de buitenste B, dus verwar de registratievolgorde niet met de volgorde waarin de verwerking start. Ben je afhankelijk van de decoratievolgorde van meerdere packages, leg de volgorde dan expliciet vast in de gebruikende applicatie en test de combinatie. Extenders worden toegepast op de resolutiesleutel nadat de container aliassen heeft genormaliseerd. Een registratie op MessageFormatter::class wordt niet toegepast wanneer PlainMessageFormatter::class direct wordt geresolved, alleen omdat de types overeenkomen. Gebruik consequent de interface.

Decoratie van een opgeslagen instantie blijft anders behouden

Container::extend() in v13.40.0 vertakt op basis van of er een instantie van het doel in de container is opgeslagen. De laatste rij is belangrijk. Als je een geresolvede singleton decoreert en daarna forgetInstance() aanroept of dezelfde sleutel opnieuw bindt, wordt een decoratie die alleen ter plekke is toegepast bij de volgende aanmaak niet opnieuw toegepast. Dat verschilt van registreren in de extender-array vóór de eerste resolutie. Ook scoped() gebruikt opgeslagen instanties, dus ga er niet van uit dat een decoratie die halverwege een scope laat is geregistreerd, voor de volgende scope geregistreerd blijft. Controleer dit samen met Levensduur van packageservices ontwerpen. Als je later een object direct opslaat met instance(), gaat het bovendien niet door de lus die extenders toepast bij normale aanmaak. Ook bij vervangingen in tests geldt niet altijd: “het is geregistreerd, dus het object dat ik doorgeef wordt automatisch gedecoreerd”.

Al uitgedeelde referenties worden niet automatisch vervangen

Als je zoals in dit voorbeeld een nieuwe wrapper retourneert, verandert het oorspronkelijke object dat al in een property van een andere service is opgeslagen niet, ook al verandert de opgeslagen waarde in de container. Een rebinding()-melding geeft de nieuwe service door aan geregistreerde callbacks. Ze zoekt niet alle afhankelijke objecten op om ze te herschrijven. Ook het registreren van een meldingscallback met rebinding() is niet altijd uitgesteld. Als het doel al gebonden is, wordt ook bij de registratie het resultaat van make() geretourneerd. Op het pad waarbij je extend() toepast op een eerder geresolvede normale binding, kan voor de melding ook make() worden aangeroepen als er een rebound-callback bestaat. Ga er niet standaard van uit dat initialisatie en bijwerkingen “bij het volgende gebruik” plaatsvinden.

Dubbele decoratie en meegenomen state vermijden

extend() ontdubbelt registraties van dezelfde closure of dezelfde decorator niet automatisch. Als register() van deze provider twee keer op dezelfde container wordt toegepast, kan het voorvoegsel ook dubbel worden toegevoegd.
  • Bundel de registratie van decoraties in het vaste opstartpad van de provider en herhaal die niet per request of job.
  • Voorkom duplicaten niet met één statische vlag die door alle containers wordt gedeeld. Tests en nieuwe applicaties missen dan registraties die ze nodig hebben.
  • De decoratie van een singleton blijft behouden voor dezelfde gedeelde levensduur. Leg de huidige gebruiker, request of vergelijkbare state niet vast in de wrapper.
  • forgetExtenders() wist alleen de registratie-array. Het zet al aangemaakte wrappers niet terug naar het origineel.
Dit voorbeeld wijzigt de uitvoerstring. Het voorvoegsel, het aantal gedelegeerde aanroepen en de afhandeling van excepties worden ook publiek gedrag van het package. Behandel ze dus niet als een “interne implementatiewijziging”, maar controleer de compatibiliteit.

Registratiemomenten testen met dezelfde container

Het volgende PHPUnit-voorbeeld gaat ervan uit dat de bovenstaande klassen via de autoload van het package kunnen worden geladen. Het past register() van de provider direct toe op een Container en controleert zonder HTTP of externe services hoe lang de decoratie behouden blijft. Controleer package discovery en het opstarten van de hele applicatie apart met Laravel-packages testen met Orchestra Testbench.
De laatste test beveelt late registratie niet aan, maar maakt de grens in de gecontroleerde versie expliciet. Ontwerp forgetInstance() niet als enige API om decoraties te verwijderen of opnieuw te registreren. Controleer vóór een release ook de volgorde van meerdere extenders, dubbele registraties, vervanging met instance() en referenties die gebruikers al vasthouden. Lees bij het toevoegen van ondersteunde Laravel-versies naast de resolutiesleutels en typecontracten ook de vertakkingen in extend() en de officiële tests opnieuw.

Gerelateerde pagina’s

Hooks na het resolven van packageservices

Behandelt callAfterResolving(), dat objecten configureert zonder ze te vervangen, en waar je op moet letten bij hoe vaak het wordt toegepast.

Levensduur van packageservices ontwerpen

Behandelt bind, singleton en scoped en de afhankelijkheden die aangemaakte services vasthouden.

Geraadpleegde primaire bronnen

Laatst gewijzigd op 9 november 2026