Vai al contenuto principale
I service provider registrati all’avvio di Laravel vengono caricati a ogni richiesta, anche se le loro funzionalità non vengono mai usate. I deferred service provider risolvono questo problema: rinviano il caricamento finché il servizio non viene effettivamente richiesto.
Questa pagina presuppone la conoscenza delle basi dello sviluppo di pacchetti. Ti consigliamo di leggere prima le nozioni di base sui service provider.

Perché servono i deferred provider

I service provider normali chiamano register() e boot() a ogni richiesta. Inizializzare ogni volta funzionalità che non vengono usate in tutte le pagine — come invio email, code o cache — è uno spreco.
// register() di questo provider viene chiamato a ogni richiesta
class ReportServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        // ogni volta bindiamo un pesante servizio di report
        $this->app->singleton(ReportGenerator::class, function ($app) {
            return new ReportGenerator(
                $app->make(PdfRenderer::class),
                $app->make(ChartRenderer::class),
                $app->make(DataExporter::class),
            );
        });
    }
}
Rendendolo differito, verrà inizializzato solo nelle richieste che generano realmente un report.

L’interfaccia DeferrableProvider

Illuminate\Contracts\Support\DeferrableProvider è un’interfaccia semplice con un solo metodo: provides().
namespace Illuminate\Contracts\Support;

interface DeferrableProvider
{
    public function provides();
}
Basta implementarla e il provider passa in modalità differita.

Implementazione di base

L’implementazione di un deferred provider si articola in tre passi.
1

Implementare DeferrableProvider

use Illuminate\Contracts\Support\DeferrableProvider;
use Illuminate\Support\ServiceProvider;

class ReportServiceProvider extends ServiceProvider implements DeferrableProvider
{
    // ...
}
2

Registrare i servizi in register()

Come in un provider normale, i bind vanno in register().
public function register(): void
{
    $this->app->singleton(ReportGenerator::class, function ($app) {
        return new ReportGenerator(
            $app->make(PdfRenderer::class),
            $app->make(ChartRenderer::class),
            $app->make(DataExporter::class),
        );
    });

    $this->app->singleton(ReportRepository::class, function ($app) {
        return new ReportRepository($app->make('db'));
    });
}
3

Restituire i servizi registrati da provides()

provides() deve restituire tutti i servizi registrati in register(). Da questa lista Laravel decide “quando richiedere quale servizio caricare questo provider”.
public function provides(): array
{
    return [
        ReportGenerator::class,
        ReportRepository::class,
    ];
}

Meccanismo del service manifest

All’avvio Laravel genera un file manifest chiamato bootstrap/cache/services.php. In esso è memorizzata la lista dei servizi forniti dai deferred provider.
// esempio (autogenerato) di bootstrap/cache/services.php
return [
    'providers' => [
        // stessa lista di bootstrap/providers.php
    ],
    'eager' => [
        // provider a caricamento immediato
        App\Providers\AppServiceProvider::class,
    ],
    'deferred' => [
        // mapping nome-servizio => classe provider
        'App\Services\ReportGenerator' => ReportServiceProvider::class,
        'App\Repositories\ReportRepository' => ReportServiceProvider::class,
        'cache'     => Illuminate\Cache\CacheServiceProvider::class,
        'cache.store' => Illuminate\Cache\CacheServiceProvider::class,
        'queue'     => Illuminate\Queue\QueueServiceProvider::class,
    ],
    'when' => [],
];
Grazie a questo manifest, Laravel sa “quale provider fornisce quale servizio” senza dover caricare i file. I provider vengono caricati solo alla prima risoluzione del servizio.
Se aggiungi o modifichi provider, rigenera il manifest.
php artisan optimize:clear
# oppure
php artisan clear-compiled

Flusso del funzionamento interno


Importanza del metodo provides()

Se in provides() manca una registrazione, quel servizio non verrà mai risolto.
public function register(): void
{
    $this->app->singleton(ReportGenerator::class, fn ($app) => new ReportGenerator());

    // binding aggiunto
    $this->app->singleton('report', fn ($app) => $app->make(ReportGenerator::class));
}

public function provides(): array
{
    return [
        ReportGenerator::class,
        'report',  // ← non dimenticare le chiavi stringa
    ];
}
Lo stesso vale per le proprietà $bindings / $singletons: vanno anch’esse elencate in provides().
class AnalyticsServiceProvider extends ServiceProvider implements DeferrableProvider
{
    public $singletons = [
        AnalyticsClient::class => DefaultAnalyticsClient::class,
    ];

    public function provides(): array
    {
        // restituisci tutte le chiavi elencate in $singletons
        return [
            AnalyticsClient::class,
        ];
    }
}

Limitazioni dei deferred provider

I deferred provider sono adatti quando l’unico scopo è registrare binding nel container. Non si può differire un provider che in boot() fa cose come queste.
LimitazioneMotivo
Registrazione di rotteLe rotte vengono risolte all’avvio dell’app, quindi il deferring non funziona
Registrazione di middleware globaliLa registrazione serve prima dell’elaborazione della richiesta
Registrazione di listener di eventi (sempre necessari)Se non è registrato prima che l’evento venga emesso, non funziona
Aggiunta di direttive BladeLa registrazione serve prima della compilazione delle view
Puoi scrivere il metodo boot() in un deferred provider, ma il suo contenuto non verrà eseguito finché il servizio non viene risolto. Mettere in boot() operazioni “sempre necessarie” come rotte e middleware porta a comportamenti imprevisti.

Metodo when() — registrazione via trigger evento

Con il metodo when() puoi registrare il provider al verificarsi di eventi specifici. Utile per provider necessari solo in contesti specifici, come l’elaborazione dei job.
class ReportServiceProvider extends ServiceProvider implements DeferrableProvider
{
    public function register(): void
    {
        $this->app->singleton(ReportGenerator::class, fn () => new ReportGenerator());
    }

    public function provides(): array
    {
        return [ReportGenerator::class];
    }

    /**
     * Carica il provider anche quando l'evento indicato viene emesso,
     * indipendentemente dalla risoluzione dei binding.
     */
    public function when(): array
    {
        return [
            \App\Events\ReportRequested::class,
        ];
    }
}
Se l’evento restituito da when() viene emesso, il provider viene caricato anche se il servizio non è stato risolto direttamente.

Utilizzo nello sviluppo di pacchetti

Anche nella distribuzione come pacchetto di terze parti, i deferred provider contribuiscono alle performance dell’applicazione dell’utente.

Pattern consigliato

namespace Acme\Analytics;

use Illuminate\Contracts\Support\DeferrableProvider;
use Illuminate\Support\ServiceProvider;

class AnalyticsServiceProvider extends ServiceProvider implements DeferrableProvider
{
    public function register(): void
    {
        $this->mergeConfigFrom(__DIR__.'/../config/analytics.php', 'analytics');

        $this->app->singleton(AnalyticsManager::class, function ($app) {
            return new AnalyticsManager($app->make('config')->get('analytics'));
        });

        $this->app->singleton('analytics', fn ($app) => $app->make(AnalyticsManager::class));
    }

    public function boot(): void
    {
        // usa boot() solo per la registrazione della pubblicazione
        if ($this->app->runningInConsole()) {
            $this->publishes([
                __DIR__.'/../config/analytics.php' => config_path('analytics.php'),
            ], 'analytics-config');
        }
    }

    public function provides(): array
    {
        return [
            AnalyticsManager::class,
            'analytics',
        ];
    }
}
mergeConfigFrom() verifica internamente se la configurazione è in cache, quindi può essere chiamato in sicurezza dentro register() di un deferred provider. Se la configurazione è già in cache, non fa nulla.

Isolare la registrazione dei comandi Artisan con runningInConsole()

La registrazione dei comandi serve solo all’avvio di Artisan, quindi usa un branch con runningInConsole(). Se il provider che fornisce i comandi è differito, ricorda di includere le classi dei comandi in provides() oppure predisponi un provider dedicato ai soli comandi.
public function boot(): void
{
    if ($this->app->runningInConsole()) {
        $this->commands([
            AnalyticsFlushCommand::class,
        ]);
    }
}

Esempi nel core di Laravel

Molti dei provider core di Laravel sono differiti. Il design è pensato per evitare di caricare in modo immediato tutti i servizi che non vengono usati a ogni richiesta.
ProviderServizi forniti
CacheServiceProvidercache, cache.store, RateLimiter
QueueServiceProviderqueue, queue.worker, queue.failer
MailServiceProvidermail.manager, mailer
RedisServiceProviderredis, redis.connection
HashServiceProviderhash, hash.driver
ValidationServiceProvidervalidator, validation.presence
TranslationServiceProvidertranslator
BroadcastServiceProviderBroadcast
In un’applicazione fatta solo di endpoint API può capitare che MailServiceProvider o BroadcastServiceProvider non vengano mai caricati durante una richiesta.

Criteri per decidere se differire

  • Servizi non usati in tutte le richieste (mail, report, client di API esterne, ecc.)
  • Servizi la cui inizializzazione richiede connessioni esterne o letture di file
  • Servizi con grafi di oggetti pesanti
  • Provider che offrono comandi usati solo da CLI
  • Provider che registrano rotte (con loadRoutesFrom, ecc.)
  • Provider che registrano middleware o exception handler sempre attivi
  • Provider che registrano global scope o observer Eloquent
  • Servizi leggeri usati nella maggior parte delle richieste (l’overhead del deferring sarebbe maggiore)

Pagine correlate

Nozioni di sviluppo dei pacchetti

Come sviluppare pacchetti Laravel imperniati sui service provider.

Gestire la compatibilità tra versioni dei pacchetti

Strategie di manutenzione dei pacchetti per gli upgrade major di Laravel e PHP.
Ultima modifica il 13 luglio 2026