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 insrc/MessageFormatter.php. Ook de onderstaande klassen staan elk in een PHP-bestand met dezelfde naam in src/.
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.
$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().
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.
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 resultaatB(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. Eenrebinding()-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.
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 pastregister() 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.
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
- Officiële Laravel-documentatie: Extending bindings en Rebinding
- Officiële Laravel-documentatie: Service Providers
- Officiële Laravel-documentatie: packageontwikkeling en package discovery
- Container: extend(), resolve(), rebound(), instance(), forgetExtenders()
- Officiële tests: uitgestelde aanmaak, registratie vóór binding, aliassen, meerdere extenders
- ServiceProvider: constructor en register()