Skip to main content
Pour qu’un package fonctionne non seulement avec les requêtes HTTP classiques, mais aussi avec Octane ou les workers de file d’attente, il faut décider pendant combien de temps et quelles données un service peut conserver. Si vous stockez des données propres à une requête dans un singleton, un autre traitement risque de lire des données obsolètes. Cette page examine l’implémentation du conteneur et du worker de file d’attente de Laravel Framework v13.36.0, ainsi que celle d’Octane, et relie les méthodes d’enregistrement de base du conteneur de services à la maintenance à long terme d’un package. Choisir scoped() ne suffit pas à réinitialiser toutes les références entre objets ni tous les états statiques.

Choisir la méthode d’enregistrement selon la durée de conservation du service

Ce tableau compare les résolutions classiques. Vérifiez séparément les chemins particuliers, comme la résolution avec paramètres explicites ou le contextual binding. Même avec bind(), si vous stockez le service récupéré dans une propriété d’un autre singleton, cette référence persiste. La méthode d’enregistrement est une règle de création et de réutilisation du conteneur : elle n’impose pas la recréation des objets conservés par le code appelant.

scoped combine un enregistrement partagé et le suivi des clés à supprimer

Container::scoped(), tel que nous l’avons examiné, effectue les deux opérations suivantes :
  1. Il enregistre la clé dans scopedInstances.
  2. Il appelle singleton() pour l’enregistrer en tant que liaison partagée.
L’instance elle-même est stockée dans le même tableau instances que pour un singleton. forgetScopedInstances() appelle unset() sur les instances stockées pour les clés enregistrées. La logique de création de la liaison est conservée, ce qui permet de créer un nouvel objet lors de la prochaine résolution classique. Ce traitement ne réinitialise pas les propriétés de l’ancien objet et ne réécrit pas les références détenues par d’autres objets. Un ancien objet encore référencé reste utilisable.

C’est l’environnement d’exécution qui crée la frontière

Le conteneur lui-même ne surveille pas le début des requêtes HTTP ou des jobs.
  • Worker de file d’attente : QueueServiceProvider transmet au Worker un callback qui réinitialise la portée. Worker::daemon() l’appelle dans sa boucle avant de récupérer le job suivant, et ce callback appelle notamment forgetScopedInstances() et vide les instances résolues des Facades.
  • Octane : la configuration officielle par défaut enregistre FlushTemporaryContainerInstances sur OperationTerminated. Cet écouteur appelle forgetScopedInstances().
  • Commandes résidentes ou boucles personnalisées : répéter simplement make() ne crée pas de frontière. Le cycle de vie doit être conçu côté infrastructure d’exécution.
N’appelez pas forgetScopedInstances() de votre propre initiative depuis le code applicatif classique ou depuis le boot() d’un package. Cela affecte tous les services scoped du conteneur. De plus, tous les chemins d’exécution des jobs, y compris l’exécution synchrone, ne passent pas forcément par le même chemin de réinitialisation que le Worker résident.

Rendre scoped l’état partagé au sein d’un traitement

Prenons l’exemple d’un package de livraison qui souhaite partager l’identifiant de destination au sein d’une requête ou d’un job. Au lieu de conserver directement la Request HTTP, il ne stocke que la valeur nécessaire.
Dans le service provider du package, enregistrez un context vide, sans lire l’état de l’application au moment de la création.
Dans le contrôleur ou la méthode handle() d’un job de l’application, définissez l’identifiant après avoir validé l’entrée et vérifié les autorisations. Les autres services qui résolvent ce type dans le même cycle de vie peuvent alors accéder au même context.
Ce context n’est pas un mécanisme d’autorisation. Pouvoir définir un identifiant ne signifie pas que l’accès à cette destination est autorisé. De plus, scoped() n’assure aucune persistance en base de données ou dans un cache externe. Les valeurs à transmettre au job suivant doivent être passées explicitement dans les données du job.
Avec Octane, les méthodes register() et boot() des service providers s’exécutent au démarrage initial du worker. Ne concevez pas votre package pour stocker l’utilisateur courant ou la destination dans le context depuis boot(). Définissez les valeurs propres à chaque traitement une fois celui-ci commencé.

Les références d’un singleton vers un service scoped ne sont pas mises à jour automatiquement

L’enregistrement suivant ne fonctionne pas comme prévu, même si seul le context est scoped.
Si DeliveryService stocke le context dans une propriété dans son constructeur, il conserve le premier context créé. Même si le conteneur supprime le context stocké, la propriété du DeliveryService déjà créé ne change pas. Choisissez la solution selon la responsabilité du service.
  1. Passer les valeurs nécessaires à chaque traitement en argument de méthode. Si aucun état ne doit être partagé, c’est la solution la plus simple.
  2. Rendre aussi scoped le service qui conserve la référence. DeliveryService et le context sont alors tous deux recréés à chaque traitement.
  3. Passer un resolver qui résout le service au moment de l’utilisation. Si le singleton doit être conservé, récupérez le service nécessaire depuis le conteneur courant à chaque fois.
Dans le troisième exemple, au lieu de capturer l’ancien context dans une closure, on le résout au moment de l’appel.
Dans cet exemple, DeliveryService doit recevoir une Closure dans son constructeur et être modifié pour exposer une API qui l’appelle à chaque opération. Appeler le resolver une seule fois dans le constructeur et en stocker le résultat ne résout pas le problème. Il s’agit d’un exemple de conception qui applique le container resolver de la documentation officielle d’Octane à un context propre au package.

Vérifier aussi les états que scoped ne supprime pas

scoped() ne supprime pas automatiquement les états suivants :
  • les valeurs ajoutées aux propriétés statiques propres au package ;
  • les références au context stockées dans un autre singleton ou une variable globale ;
  • « l’utilisateur courant » ou « la destination courante » conservés à l’intérieur d’un singleton ;
  • les références conservées par des Facades ou des couches de cache personnalisées.
Distinguez la réinitialisation effectuée par l’infrastructure d’exécution standard de celle des états propres au package. Réduire le nombre d’emplacements qui conservent un état par traitement est plus simple que de maintenir une logique de nettoyage personnalisée pour chaque environnement d’exécution.

Écrire des tests qui franchissent une frontière dans le même conteneur

Si chaque test HTTP crée simplement une nouvelle application, vous risquez de passer à côté des problèmes liés à la réutilisation du même conteneur par un processus résident. Commencez par vérifier, au niveau du conteneur seul, le partage au sein d’un même traitement et la recréation après la frontière. Les tests suivants utilisent PHPUnit. Le premier test utilise forgetScopedInstances(), comme les tests officiels du Container, pour vérifier l’effet de la frontière sans démarrer un Worker complet.
Le second test documente le comportement selon lequel les références persistent. Dans un vrai package, ajoutez aussi des tests vérifiant que la solution retenue utilise bien un nouveau context ou les valeurs actuelles. Les tests unitaires du conteneur ne vérifient pas le chemin d’exécution réel de la sandbox d’Octane ou du worker de file d’attente. Des tests d’intégration exécutant des traitements successifs dans les environnements que vous déclarez prendre en charge sont également nécessaires. Lors d’un déploiement modifiant les méthodes d’enregistrement ou l’implémentation des services, rechargez les processus résidents pour appliquer le nouveau code.

Pages connexes

Hooks de résolution des services d'un package

Découvrez afterResolving, la récupération des instances partagées et les différences de configuration lors des nouvelles résolutions.

Laravel Octane

Découvrez la conservation de la Request et du conteneur, ainsi que les points d’attention liés à l’exploitation des processus résidents.

Sources primaires consultées

Dernière modification le 14 octobre 2026