Skip to main content
Lorsque vous souhaitez ajouter à votre package des commandes Artisan de diagnostic, de synchronisation ou d’installation, considérez séparément l’enregistrement de la commande, la création de son instance et l’exécution du traitement. Si vous résolvez dans le constructeur des services qui ne sont nécessaires qu’à l’exécution, vous risquez d’impacter jusqu’au démarrage de commandes Artisan sans rapport. Cette page examine l’implémentation de Laravel Framework v13.35.0 et approfondit l’exemple d’enregistrement avec commands() présenté dans Développement de packages Laravel. Pour l’implémentation de la génération de fichiers, consultez GeneratorCommand, et pour la participation aux tâches d’optimisation, consultez Intégration à optimize.

Enregistrer explicitement depuis le provider

Les commandes d’un package sont enregistrées par leur nom de classe dans la méthode boot() du service provider. La découverte automatique du répertoire app/Console/Commands de l’application et l’enregistrement des classes fournies par un package sont deux points d’entrée distincts.
Cet exemple suppose que le provider est chargé via la découverte automatique des packages ou par un enregistrement explicite côté application. Il ne suffit pas de placer une classe de commande pour que le provider du package soit automatiquement enregistré. runningInConsole() détermine si l’on se trouve dans un environnement console, et non si une commande particulière est en cours d’exécution. Si vous prévoyez aussi un usage via Artisan::call() depuis une requête HTTP, tenez compte du fait que cette condition peut empêcher l’enregistrement, et définissez le contrat d’utilisation en envisageant éventuellement un enregistrement inconditionnel. Pour l’exécution programmatique, consultez Artisan.

commands() n’exécute pas la commande sur-le-champ

ServiceProvider::commands() enregistre auprès de Illuminate\Console\Application::starting() une closure qui utilise les noms de classes reçus. À ce stade, la méthode handle() de la commande n’est pas appelée. Avec le Console Kernel habituel, les callbacks de démarrage enregistrés sont exécutés plus tard, lorsque l’Artisan Application est créée. C’est alors que resolveCommands() transmet chaque classe à resolve() et procède à l’enregistrement des commandes. Le point essentiel est que l’instanciation d’une commande ne coïncide pas nécessairement avec son exécution. Pour les classes ordinaires qui n’empruntent pas le chemin d’enregistrement différé dédié décrit plus bas, resolve() appelle make() du conteneur : le constructeur et ses dépendances sont donc résolus au moment de l’enregistrement. Par conséquent, le simple fait d’exécuter php artisan list ou une autre commande peut déclencher le constructeur de la commande du package. Placer commands() dans boot() ne garantit pas en soi que l’instanciation sera reportée jusqu’à l’exécution.

Injecter les dépendances d’exécution dans handle()

À titre d’exemple, créons une commande qui affiche uniquement si la configuration du package est activée ou désactivée. Comme dans la documentation officielle d’Artisan pour Laravel 13, le nom et la description sont définis avec les attributs Signature et Description.
Cet exemple n’ajoute pas de constructeur et reçoit le Repository de configuration en argument de handle(). Comme Command::execute() utilise call() du conteneur dans le chemin d’exécution de la commande, les dépendances typées sont injectées. Si vous utilisez un client d’API externe ou un service de synchronisation, vous pouvez également l’injecter au même endroit s’il n’est nécessaire qu’à l’exécution. Cependant, si la création du service lui-même ou le boot() d’un autre provider produit des effets de bord, ceux-ci ne sont pas évités automatiquement.
N’effectuez pas dans le constructeur de connexion à une API externe, de mise à jour de la base de données ni d’écrasement de fichiers publiés. Les utilisateurs du package doivent pouvoir utiliser list et help même juste après l’installation ou lorsque la configuration de connexion n’est pas encore finalisée.
Si vous définissez votre propre constructeur, appelez également parent::__construct(). En effet, le constructeur parent de Laravel configure la commande à partir des attributs, de la signature, de la description, etc.

Signature et l’enregistrement différé sont deux mécanismes distincts

La méthode Application::resolve() examinée contient une branche qui lit le name de l’attribut Symfony AsCommand et enregistre la correspondance entre le nom et la classe dans une command map. Dans cette branche, make() du conteneur n’est pas appelé immédiatement. Le Console Kernel appelle ensuite setContainerCommandLoader() pour configurer un loader qui utilise cette map. En revanche, l’attribut Laravel Signature utilisé dans l’exemple ci-dessus est distinct de cette détection d’AsCommand. Ajouter Signature ne suffit pas à faire passer la classe par ce chemin d’enregistrement différé. Considérez cette différence comme un point d’attention lors de la lecture de l’implémentation interne, et commencez par réduire le coût de création et les effets de bord grâce à la définition officielle des commandes et à l’injection de dépendances dans handle(). Même avec un loader différé, les informations de la commande peuvent être nécessaires pour la liste, l’aide, etc. : ne concevez donc pas votre code en supposant que la commande « ne sera jamais créée en dehors de son exécution ».

Ne pas différer l’enregistrement des commandes

Différer le chargement des liaisons de services et faire connaître une commande à Artisan sont deux choses différentes. Lorsqu’un provider implémente DeferrableProvider, sa méthode boot() elle-même peut ne pas être exécutée tant que le service concerné n’est pas nécessaire. Si l’enregistrement des commandes se trouve uniquement dans ce boot() et que le provider n’est pas chargé avant le démarrage d’Artisan, le callback de démarrage qui signale l’existence de la commande n’est pas enregistré non plus. Même si un service déplacé vers l’injection de dépendances de handle() charge le provider différé, cela ne résout pas le problème qui survient avant qu’Artisan ne trouve la commande. Pour garantir l’exposition des commandes, vous pouvez placer leur enregistrement dans un provider chargé immédiatement et isoler uniquement les liaisons des services lourds dans un provider différé distinct. Pour les exigences précises, consultez Service providers différés.

Points à vérifier lors des mises à jour

Le nom de la commande, ses arguments, ses options, sa sortie et son code de sortie constituent une interface publique utilisée par les scripts shell et la CI des utilisateurs. Ne les modifiez pas avec la même légèreté que l’implémentation interne d’une classe ; vérifiez la compatibilité et la procédure de migration. Pour le StatusCommand ci-dessus, vous pouvez vérifier la sortie et le code de sortie comme suit dans un environnement de test qui charge le provider. Pour l’infrastructure de test, consultez Tester des packages Laravel avec Orchestra Testbench, et pour le détail des assertions, consultez Tests de console.
En complément, pour les commandes qui utilisent des services lourds, vérifiez aussi que les méthodes métier du service ne sont pas appelées lors de l’exécution de la liste ou de l’aide. Vérifier uniquement que l’enregistrement fonctionne ne permet pas de détecter l’impact sur des commandes sans rapport.

Sources primaires consultées

Dernière modification le 12 octobre 2026