> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# Decorazione dei servizi ed extend nei pacchetti

> Analizza Container::extend() di Laravel 13 per capire i decorator che mantengono il contratto, la registrazione prima e dopo la risoluzione, la durata della decorazione di un singleton, la doppia decorazione e i test durante gli aggiornamenti.

A volte un pacchetto deve aggiungere un comportamento mantenendo l'implementazione del servizio scelta dall'applicazione che lo utilizza. Con `extend()` del container puoi avvolgere il servizio risolto in un decorator senza ridefinire la logica di creazione originale.

Questa pagina fa riferimento al branch predefinito `13.x` della documentazione ufficiale di Laravel e all'implementazione e ai test ufficiali dell'ultima release del Framework, `v13.40.0`, verificati il 9 novembre 2026. Oltre alla differenza rispetto agli [hook di risoluzione dei servizi](https://kawax.biz/it/advanced/package-service-resolution.md), spiega per quanto tempo viene mantenuta una decorazione, che cambia a seconda che **un'istanza sia già salvata al momento della registrazione**.

## Separare l'aggiunta di configurazione dalla sostituzione dell'oggetto

| API | Scopo | Valore di ritorno della callback |
| - | - | - |
| `bind()`, `singleton()` | Registrare come creare un servizio e se è condiviso | Il servizio da creare |
| `afterResolving()` | Applicare configurazioni e simili al servizio risolto | Non viene usato per sostituire il servizio |
| `extend()` | Mantenere la logica di creazione e decorare o modificare il servizio | Deve sempre restituire il servizio modificato |

La callback di `extend()` riceve il servizio di destinazione e il container. Puoi configurare lo stesso oggetto e restituirlo, oppure restituire un nuovo decorator. Se dimentichi il `return`, alla parte successiva della risoluzione viene passato `null`.

> [!NOTE]
>
> Qui si tratta di `Illuminate\Container\Container::extend()`. È un'API diversa dall'`extend()` dei manager come Cache o Mail, che registra il nome di un driver e la sua logica di creazione.

## Creare un decorator che implementa lo stesso contratto

### Avvolgere l'implementazione originale senza sostituirla

A scopo illustrativo, inserisci il seguente contratto per formattare i messaggi in `src/MessageFormatter.php`. Anche le classi seguenti si trovano ciascuna in un file PHP con lo stesso nome in `src/`.

```php theme={null}
<?php

namespace Acme\Messages;

interface MessageFormatter
{
    public function format(string $message): string;
}
```

L'implementazione non decorata restituisce l'input invariato.

```php theme={null}
<?php

namespace Acme\Messages;

final class PlainMessageFormatter implements MessageFormatter
{
    public function format(string $message): string
    {
        return $message;
    }
}
```

Il decorator implementa la stessa interfaccia e delega al servizio ricevuto. In questo esempio, aggiunge un prefisso specifico del pacchetto al valore restituito dal delegato.

```php theme={null}
<?php

namespace Acme\Messages;

final class PrefixedMessageFormatter implements MessageFormatter
{
    public function __construct(
        private readonly MessageFormatter $inner,
    ) {}

    public function format(string $message): string
    {
        return '[Acme] '.$this->inner->format($message);
    }
}
```

Se chi usa il pacchetto dichiara come tipo la classe concreta `PlainMessageFormatter`, un decorator che implementa solo la stessa interfaccia non può sostituirla. Esponi `MessageFormatter` come dipendenza e mantieni il contratto su argomenti, valori di ritorno, eccezioni e così via. Il solo inoltro dinamico dei metodi non sostituisce l'implementazione di un'interfaccia PHP.

### Registrare la decorazione nel provider

`src/PrefixServiceProvider.php`, che fornisce la decorazione, non sovrascrive il modo in cui il servizio viene creato. Registra soltanto un'estensione nel container. Per il caricamento del provider consulta [Struttura interna del package auto-discovery](https://kawax.biz/it/advanced/package-discovery.md).

```php theme={null}
<?php

namespace Acme\Messages;

use Illuminate\Support\ServiceProvider;

final class PrefixServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        $this->app->extend(
            MessageFormatter::class,
            static fn (MessageFormatter $formatter): MessageFormatter =>
                new PrefixedMessageFormatter($formatter),
        );
    }
}
```

La callback riceve anche il container come secondo argomento, ma questo esempio non ne ha bisogno e quindi non lo dichiara. Usa l'argomento `$formatter` come servizio di destinazione. Chiamare di nuovo `make()` sulla stessa chiave all'interno della callback può causare una risoluzione circolare.

Esegui il binding dell'implementazione originale nell'applicazione che usa il pacchetto, ad esempio in `AppServiceProvider::register()`.

```php theme={null}
use Acme\Messages\MessageFormatter;
use Acme\Messages\PlainMessageFormatter;

$this->app->bind(MessageFormatter::class, PlainMessageFormatter::class);
```

Puoi anche registrare `extend()` prima del `bind()` della destinazione. Se la destinazione non è stata risolta e non c'è un'istanza salvata, la sola registrazione non crea il servizio originale. Anche i test ufficiali verificano la creazione differita e la registrazione prima del binding.

Successivamente, `app(MessageFormatter::class)->format('Spedito.')` restituisce `[Acme] Spedito.`. Anche se l'implementazione originale viene sostituita con un'altra implementazione di `MessageFormatter`, il decorator avvolge quella implementazione.

> [!WARNING]
>
> Come regola, registra prima della prima risoluzione. Un altro provider potrebbe usare il servizio per primo, quindi spostare semplicemente la registrazione in `boot()` non impedisce la risoluzione precedente. Inoltre, se rendi differito con leggerezza un provider che registra solo estensioni, la registrazione stessa potrebbe perdere la condizione che la avvia.

## Capire dove si applica la decorazione e in quale ordine

In una risoluzione normale, il container crea il servizio originale, lo sostituisce con il valore restituito da ciascun extender nell'ordine di registrazione, lo salva come istanza condivisa e poi chiama gli hook successivi alla risoluzione.

```mermaid theme={null}
flowchart LR
    A["Creazione originale"] --> B["Servizio"]
    B --> C["extender A<br>Avvolgere con Wrapper A"]
    C --> D["extender B<br>Avvolgere con Wrapper B"]
    D --> E["Salvare se condiviso"]
    E --> F["Hook post-risoluzione e utilizzatori"]
```

Se registri A e poi B, il risultato è `B(A(servizio originale))`. Le chiamate ai metodi degli utilizzatori partono dalla B esterna, quindi non confondere l'ordine di registrazione con l'ordine di avvio dell'elaborazione. Se dipendi dall'ordine di decorazione di più pacchetti, rendi esplicito l'ordine nell'applicazione e testa la combinazione.

Gli extender si applicano alla **chiave di risoluzione** dopo che il container ha normalizzato gli alias. Una registrazione su `MessageFormatter::class` non viene applicata quando `PlainMessageFormatter::class` viene risolto direttamente solo perché i tipi coincidono. Usa sempre l'interfaccia.

## La decorazione di un'istanza salvata viene mantenuta diversamente

`Container::extend()` in `v13.40.0` si comporta diversamente a seconda che un'istanza della destinazione sia salvata nel container.

| Stato alla registrazione | Comportamento di `extend()` | Mantenuto per le creazioni future |
| - | - | - |
| Non ancora risolto e nessuna istanza salvata | Aggiunge l'extender all'array | Applicato nelle successive creazioni normali |
| Un `bind()` normale è stato risolto in passato, ma nessuna istanza è salvata | Aggiunge l'extender e tenta una notifica rebound | Applicato nelle successive creazioni normali |
| È salvato un oggetto singleton o di `instance()` | Lo decora subito, sostituisce il valore salvato con il valore restituito e invia una notifica rebound | **Questo extender non viene aggiunto all'array** |

L'ultima riga è importante. Se decori un singleton già risolto e poi chiami `forgetInstance()` o rifai il binding della stessa chiave, **una decorazione applicata solo sul momento** non viene riapplicata alla creazione successiva. È diverso dalla registrazione nell'array degli extender prima della prima risoluzione.

Anche `scoped()` usa istanze salvate, quindi non dare per scontato che una decorazione registrata in ritardo a metà di uno scope resti registrata anche per lo scope successivo. Verificalo insieme a [Progettare il ciclo di vita dei servizi di un pacchetto](https://kawax.biz/it/advanced/package-service-lifetimes.md).

Inoltre, se in seguito salvi direttamente un oggetto con `instance()`, questo non passa per il ciclo che applica gli extender nelle creazioni normali. Anche per le sostituzioni nei test, non è sempre vero che "essendo registrato, l'oggetto che passo viene decorato automaticamente".

### I riferimenti già distribuiti non vengono sostituiti automaticamente

Quando restituisci un nuovo wrapper, come in questo esempio, anche se il valore salvato nel container cambia, l'oggetto originale già salvato in una proprietà di un altro servizio non cambia. La notifica di `rebinding()` passa il nuovo servizio alle callback registrate. Non cerca né riscrive tutte le dipendenze.

Anche la registrazione di una callback di notifica con `rebinding()` non è sempre differita. Se la destinazione ha già un binding, viene restituito il risultato di `make()` anche al momento della registrazione. Nel percorso in cui applichi `extend()` a un binding normale già risolto, `make()` può essere chiamato per la notifica se esiste una callback rebound. Non presumere in modo generale che inizializzazione ed effetti collaterali avvengano "al prossimo utilizzo".

## Evitare la doppia decorazione e lo stato trascinato

`extend()` non elimina automaticamente le registrazioni duplicate della stessa closure o dello stesso decorator. Se il `register()` di questo provider viene applicato due volte allo stesso container, anche il prefisso può essere aggiunto due volte.

* Concentra la registrazione delle decorazioni nel percorso di avvio fisso del provider e non ripeterla per ogni richiesta o job.
* Non prevenire i duplicati con un unico flag statico condiviso da tutti i container. Test e nuove applicazioni perderebbero registrazioni necessarie.
* La decorazione di un singleton viene mantenuta per lo stesso periodo condiviso. Non fissare nel wrapper l'utente corrente, la richiesta o stati simili.
* `forgetExtenders()` cancella solo l'array delle registrazioni. Non riporta allo stato originale i wrapper già creati.

Questo esempio modifica la stringa di output. Anche il prefisso, il numero di chiamate delegate e la gestione delle eccezioni diventano comportamento pubblico del pacchetto, quindi non trattarli come un semplice "cambiamento interno dell'implementazione" e verifica la compatibilità.

## Testare i tempi di registrazione con lo stesso container

Il seguente esempio PHPUnit presuppone che le classi sopra possano essere caricate tramite l'autoload del pacchetto. Applica direttamente il `register()` del provider a un Container e verifica per quanto tempo viene mantenuta la decorazione, senza HTTP né servizi esterni. Verifica separatamente l'auto-discovery e l'avvio dell'intera applicazione con [Testare pacchetti Laravel con Orchestra Testbench](https://kawax.biz/it/advanced/package-testing.md).

```php theme={null}
<?php

namespace Acme\Messages\Tests;

use Acme\Messages\MessageFormatter;
use Acme\Messages\PlainMessageFormatter;
use Acme\Messages\PrefixedMessageFormatter;
use Acme\Messages\PrefixServiceProvider;
use Illuminate\Container\Container;
use PHPUnit\Framework\TestCase;

final class ServiceDecorationTest extends TestCase
{
    public function test_extension_is_lazy_and_can_precede_binding(): void
    {
        $container = new Container;
        $builds = 0;

        (new PrefixServiceProvider($container))->register();
        $container->bind(MessageFormatter::class, function () use (&$builds) {
            $builds++;

            return new PlainMessageFormatter;
        });

        $this->assertSame(0, $builds);

        $first = $container->make(MessageFormatter::class);
        $second = $container->make(MessageFormatter::class);

        $this->assertSame('[Acme] ready', $first->format('ready'));
        $this->assertSame('[Acme] ready', $second->format('ready'));
        $this->assertNotSame($first, $second);
        $this->assertSame(2, $builds);
    }

    public function test_early_extension_survives_singleton_recreation(): void
    {
        $container = new Container;
        $container->singleton(MessageFormatter::class, PlainMessageFormatter::class);
        (new PrefixServiceProvider($container))->register();

        $first = $container->make(MessageFormatter::class);

        $this->assertInstanceOf(PrefixedMessageFormatter::class, $first);
        $this->assertSame($first, $container->make(MessageFormatter::class));

        $container->forgetInstance(MessageFormatter::class);
        $second = $container->make(MessageFormatter::class);

        $this->assertNotSame($first, $second);
        $this->assertSame('[Acme] ready', $second->format('ready'));
    }

    public function test_late_singleton_extension_is_not_retained_as_a_callback(): void
    {
        $container = new Container;
        $container->singleton(MessageFormatter::class, PlainMessageFormatter::class);
        $original = $container->make(MessageFormatter::class);

        (new PrefixServiceProvider($container))->register();
        $decorated = $container->make(MessageFormatter::class);

        $this->assertInstanceOf(PrefixedMessageFormatter::class, $decorated);
        $this->assertNotSame($original, $decorated);
        $this->assertSame('ready', $original->format('ready'));
        $this->assertSame('[Acme] ready', $decorated->format('ready'));

        $container->forgetInstance(MessageFormatter::class);
        $recreated = $container->make(MessageFormatter::class);

        $this->assertInstanceOf(PlainMessageFormatter::class, $recreated);
        $this->assertSame('ready', $recreated->format('ready'));
    }
}
```

L'ultimo test non raccomanda la registrazione tardiva, ma rende esplicito il limite nella versione verificata. Non progettare `forgetInstance()` da solo come API per rimuovere o registrare di nuovo le decorazioni.

Prima di una release, verifica anche l'ordine di più extender, le registrazioni duplicate, la sostituzione con `instance()` e i riferimenti già detenuti dagli utilizzatori. Quando aggiungi versioni di Laravel supportate, rileggi i rami di `extend()` e i test ufficiali, oltre alle chiavi di risoluzione e ai contratti dei tipi.

## Pagine correlate

> [card] [Hook di risoluzione dei servizi nei pacchetti](https://kawax.biz/it/advanced/package-service-resolution.md)
>
> Illustra callAfterResolving(), che configura gli oggetti senza sostituirli, e le precauzioni sul numero di applicazioni.

> [card] [Progettare il ciclo di vita dei servizi di un pacchetto](https://kawax.biz/it/advanced/package-service-lifetimes.md)
>
> Illustra bind, singleton e scoped e le dipendenze mantenute dai servizi già creati.

## Fonti primarie consultate

* [Documentazione ufficiale di Laravel: Extending bindings e Rebinding](https://github.com/laravel/docs/blob/13.x/container.md#extending-bindings)
* [Documentazione ufficiale di Laravel: Service Providers](https://github.com/laravel/docs/blob/13.x/providers.md)
* [Documentazione ufficiale di Laravel: sviluppo di pacchetti e auto-discovery](https://github.com/laravel/docs/blob/13.x/packages.md)
* [Container: extend(), resolve(), rebound(), instance(), forgetExtenders()](https://github.com/laravel/framework/blob/v13.40.0/src/Illuminate/Container/Container.php)
* [Test ufficiali: creazione differita, registrazione prima del binding, alias, più extender](https://github.com/laravel/framework/blob/v13.40.0/tests/Container/ContainerExtendTest.php)
* [ServiceProvider: costruttore e register()](https://github.com/laravel/framework/blob/v13.40.0/src/Illuminate/Support/ServiceProvider.php)


## Related topics

- [Hook di risoluzione dei servizi nei pacchetti](https://kawax.biz/it/advanced/package-service-resolution.md)
- [Argomenti avanzati](https://kawax.biz/it/advanced/index.md)
- [Registrazione e test dei transport di posta nei pacchetti](https://kawax.biz/it/advanced/package-mail-transports.md)
- [Registrazione e manutenzione dei task pianificati dei pacchetti](https://kawax.biz/it/advanced/package-scheduling.md)
- [Estendere le connessioni al database nei pacchetti](https://kawax.biz/it/advanced/package-database-connections.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.