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

# Service providers différés

> Découvrez comment charger vos services en différé en implémentant l'interface DeferrableProvider. Une fonctionnalité discrète mais cruciale pour l'optimisation des performances des packages.

Au démarrage de Laravel, les service providers enregistrés sont chargés à chaque requête, même si leur fonctionnalité n'est jamais utilisée durant celle-ci. Les service providers différés (Deferred Service Provider) résolvent ce problème en retardant le chargement jusqu'à ce que le service soit réellement utilisé.

<Info>
  Cette page suppose la connaissance des [bases du développement de packages](/fr/advanced/package-development). Il est recommandé de comprendre le fonctionnement de base des service providers avant de la lire.
</Info>

## Pourquoi les providers différés sont nécessaires

Les service providers ordinaires voient leurs `register()` et `boot()` appelés à chaque requête. Initialiser à chaque fois des fonctionnalités que toutes les pages n'utilisent pas — envoi d'e-mails, queues, cache — est un gaspillage.

```php theme={null}
// Le register() de ce provider est appelé à chaque requête
class ReportServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        // Lier un service de rapport lourd à chaque fois
        $this->app->singleton(ReportGenerator::class, function ($app) {
            return new ReportGenerator(
                $app->make(PdfRenderer::class),
                $app->make(ChartRenderer::class),
                $app->make(DataExporter::class),
            );
        });
    }
}
```

Le rendre différé fait que l'initialisation n'a lieu que pour les requêtes qui génèrent réellement un rapport.

***

## L'interface DeferrableProvider

`Illuminate\Contracts\Support\DeferrableProvider` est une interface simple avec une seule méthode `provides()`.

```php theme={null}
namespace Illuminate\Contracts\Support;

interface DeferrableProvider
{
    public function provides();
}
```

Il suffit d'implémenter cette interface pour que le provider passe en mode différé.

***

## Implémentation de base

L'implémentation d'un provider différé se fait en trois étapes.

<Steps>
  <Step title="Implémenter DeferrableProvider">
    ```php theme={null}
    use Illuminate\Contracts\Support\DeferrableProvider;
    use Illuminate\Support\ServiceProvider;

    class ReportServiceProvider extends ServiceProvider implements DeferrableProvider
    {
        // ...
    }
    ```
  </Step>

  <Step title="Lier les services dans register()">
    Écrivez les bindings dans `register()` comme dans un provider ordinaire.

    ```php theme={null}
    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'));
        });
    }
    ```
  </Step>

  <Step title="Retourner les services enregistrés dans provides()">
    `provides()` doit retourner **tous les services** liés dans `register()`. Laravel se base sur cette liste pour déterminer « quel provider charger lorsque tel service est demandé ».

    ```php theme={null}
    public function provides(): array
    {
        return [
            ReportGenerator::class,
            ReportRepository::class,
        ];
    }
    ```
  </Step>
</Steps>

***

## Mécanisme du manifeste des services

Au démarrage, Laravel génère un fichier manifeste `bootstrap/cache/services.php`. Ce fichier contient la liste des services fournis par les providers différés.

```php theme={null}
// Exemple de bootstrap/cache/services.php (généré automatiquement)
return [
    'providers' => [
        // Même liste que bootstrap/providers.php
    ],
    'eager' => [
        // Providers à chargement immédiat
        App\Providers\AppServiceProvider::class,
    ],
    'deferred' => [
        // Mapping nom de service => classe de 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' => [],
];
```

Grâce à ce manifeste, Laravel sait « quel provider fournit ce service » sans avoir à charger de fichiers. Le provider réel n'est chargé que lorsque le service correspondant est résolu pour la première fois.

<Tip>
  Après avoir ajouté ou modifié un provider, régénérez le manifeste.

  ```shell theme={null}
  php artisan optimize:clear
  # ou
  php artisan clear-compiled
  ```
</Tip>

### Flux du fonctionnement interne

```mermaid theme={null}
sequenceDiagram
    participant App as Application<br>Laravel
    participant Container as Conteneur<br>de services
    participant Manifest as Manifeste<br>services.php
    participant Provider as ReportService<br>Provider

    App->>Manifest: Charge le manifeste au démarrage
    Manifest-->>App: Retourne la liste des services deferred
    Note over App: À ce stade, le provider n'est pas chargé

    App->>Container: $app->make(ReportGenerator::class)
    Container->>Manifest: Vérifier si c'est un service deferred
    Manifest-->>Container: Fourni par ReportServiceProvider
    Container->>Provider: Appelle register() + boot()
    Provider-->>Container: Enregistre les bindings
    Container-->>App: Retourne l'instance de ReportGenerator
```

***

## Importance de la méthode provides()

Si `provides()` contient un **oubli**, le service correspondant ne sera jamais résolu.

```php theme={null}
public function register(): void
{
    $this->app->singleton(ReportGenerator::class, fn ($app) => new ReportGenerator());

    // Binding supplémentaire
    $this->app->singleton('report', fn ($app) => $app->make(ReportGenerator::class));
}

public function provides(): array
{
    return [
        ReportGenerator::class,
        'report',  // ← N'oubliez pas de mentionner aussi les bindings à clé chaîne
    ];
}
```

Idem lorsque vous utilisez les propriétés `$bindings` / `$singletons` : incluez-les dans `provides()`.

```php theme={null}
class AnalyticsServiceProvider extends ServiceProvider implements DeferrableProvider
{
    public $singletons = [
        AnalyticsClient::class => DefaultAnalyticsClient::class,
    ];

    public function provides(): array
    {
        // Retourner toutes les clés listées dans $singletons
        return [
            AnalyticsClient::class,
        ];
    }
}
```

***

## Contraintes des providers différés

Les providers différés conviennent aux providers dont le seul objectif est **l'enregistrement de bindings dans le conteneur**. Les providers qui effectuent les opérations ci-dessous dans `boot()` ne peuvent pas être différés.

| Contrainte                                    | Raison                                                                       |
| --------------------------------------------- | ---------------------------------------------------------------------------- |
| Enregistrement de routes                      | Les routes sont résolues au démarrage de l'app, le différé ne fonctionne pas |
| Enregistrement de middlewares globaux         | Doit se faire avant le traitement de la requête                              |
| Enregistrement d'event listeners (permanents) | Doit être enregistré avant que l'événement soit émis                         |
| Ajout de directives Blade                     | Doit être enregistré avant la compilation des vues                           |

<Warning>
  Écrire une méthode `boot()` dans un provider différé est possible, mais son contenu ne s'exécutera que lorsqu'un service sera résolu. Écrire des « traitements toujours nécessaires » comme des routes ou des middlewares dans `boot()` produit des comportements inattendus.
</Warning>

***

## La méthode when() — enregistrement déclenché par un événement

La méthode `when()` permet d'enregistrer un provider lorsqu'un événement particulier est émis. Utile pour des providers nécessaires uniquement dans certains contextes (traitement de jobs, etc.).

```php theme={null}
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];
    }

    /**
     * Charger le provider aussi lorsque les événements spécifiés
     * sont émis, en plus de la résolution d'un binding de service.
     */
    public function when(): array
    {
        return [
            \App\Events\ReportRequested::class,
        ];
    }
}
```

Lorsque l'un des événements retournés par `when()` est émis, le provider est chargé même si aucun service n'a été directement résolu.

***

## Utilisation dans le développement de packages

Pour un package tiers, les providers différés contribuent également aux performances de l'application utilisatrice.

### Pattern recommandé

```php theme={null}
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
    {
        // Utiliser boot() uniquement pour l'enregistrement des publications
        if ($this->app->runningInConsole()) {
            $this->publishes([
                __DIR__.'/../config/analytics.php' => config_path('analytics.php'),
            ], 'analytics-config');
        }
    }

    public function provides(): array
    {
        return [
            AnalyticsManager::class,
            'analytics',
        ];
    }
}
```

<Info>
  `mergeConfigFrom()` vérifie en interne si la configuration est en cache, donc l'appeler dans le `register()` d'un provider différé est sûr. Si la configuration est déjà en cache, cela ne fait rien.
</Info>

### Séparer l'enregistrement des commandes Artisan avec `runningInConsole()`

L'enregistrement de commandes n'est nécessaire qu'au démarrage d'Artisan ; branchez donc avec `runningInConsole()`. Toutefois, si vous différez un provider qui fournit des commandes, incluez la classe de commande dans `provides()` ou séparez les commandes dans un provider dédié.

```php theme={null}
public function boot(): void
{
    if ($this->app->runningInConsole()) {
        $this->commands([
            AnalyticsFlushCommand::class,
        ]);
    }
}
```

***

## Exemples d'utilisation dans le cœur de Laravel

Beaucoup des providers du cœur de Laravel sont des providers différés. C'est une conception qui évite de charger immédiatement tous les services non utilisés à chaque requête.

| Provider                     | Services fournis                        |
| ---------------------------- | --------------------------------------- |
| `CacheServiceProvider`       | `cache`, `cache.store`, `RateLimiter`   |
| `QueueServiceProvider`       | `queue`, `queue.worker`, `queue.failer` |
| `MailServiceProvider`        | `mail.manager`, `mailer`                |
| `RedisServiceProvider`       | `redis`, `redis.connection`             |
| `HashServiceProvider`        | `hash`, `hash.driver`                   |
| `ValidationServiceProvider`  | `validator`, `validation.presence`      |
| `TranslationServiceProvider` | `translator`                            |
| `BroadcastServiceProvider`   | `Broadcast`                             |

Dans une application composée uniquement d'endpoints API, `MailServiceProvider` ou `BroadcastServiceProvider` peuvent ne jamais être chargés durant une requête.

***

## Critères pour décider de différer ou non

<AccordionGroup>
  <Accordion title="Services adaptés au différé">
    * Services qui ne sont pas utilisés à chaque requête (mail, rapports, clients d'API externes, etc.)
    * Services dont l'initialisation nécessite une connexion externe ou la lecture de fichiers
    * Services avec un graphe d'objets lourd
    * Providers fournissant des commandes utilisées uniquement en CLI
  </Accordion>

  <Accordion title="Services non adaptés au différé">
    * Providers qui enregistrent des routes (`loadRoutesFrom`, etc.)
    * Providers qui enregistrent des middlewares ou gestionnaires d'exceptions actifs en permanence
    * Providers qui enregistrent des global scopes ou observers Eloquent
    * Services légers utilisés dans la majorité des requêtes (le surcoût du différé peut être supérieur)
  </Accordion>
</AccordionGroup>

***

## Pages associées

<Columns cols={2}>
  <Card title="Bases du développement de packages" icon="box" href="/fr/advanced/package-development">
    Découvrez le développement de packages Laravel, avec les service providers comme noyau.
  </Card>

  <Card title="Gestion de la compatibilité des versions de packages" icon="layers" href="/fr/advanced/package-versioning">
    Stratégies de maintenance de packages pour suivre les montées de version majeures de Laravel et PHP.
  </Card>
</Columns>


## Related topics

- [Service providers](/fr/service-providers.md)
- [Développement de packages Laravel](/fr/advanced/package-development.md)
- [FAQ sur la nouvelle structure d'application Laravel 11+](/fr/advanced/app-structure-faq.md)
- [Mise à niveau de Laravel 10 à 11](/fr/blog/upgrade-10-to-11.md)
- [Structure d'application Laravel 11+](/fr/advanced/app-structure.md)
