v13.35.0 und vertieft das Registrierungsbeispiel mit commands() aus Grundlagen der Paketentwicklung. Zur Implementierung der Dateigenerierung siehe GeneratorCommand, zur Teilnahme an Optimierungsaufgaben siehe Integration in optimize.
Befehle explizit im Provider registrieren
Befehle in einem Paket werden in der Methodeboot() des Service Providers über ihren Klassennamen registriert. Die automatische Erkennung von app/Console/Commands der Anwendung und die Registrierung von Klassen, die ein Paket bereitstellt, sind getrennte Einstiegspunkte.
runningInConsole() prüft, ob die Anwendung in einer Konsolenumgebung läuft, nicht ob ein bestimmter Befehl ausgeführt wird. Wenn Sie auch den Aufruf über Artisan::call() innerhalb von HTTP-Requests unterstützen, berücksichtigen Sie, dass diese Bedingung die Registrierung verhindern kann, und legen Sie den Nutzungsvertrag fest, einschließlich einer möglichen bedingungslosen Registrierung. Wie Sie Befehle programmgesteuert ausführen, erfahren Sie unter Artisan.
commands() führt den Befehl nicht sofort aus
ServiceProvider::commands() registriert bei Illuminate\Console\Application::starting() eine Closure, die die übergebenen Klassennamen verwendet. Zu diesem Zeitpunkt wird handle() des Befehls nicht aufgerufen.
Beim regulären Console Kernel werden die registrierten Start-Callbacks später ausgeführt, wenn die Artisan Application erzeugt wird. Dort übergibt resolveCommands() jede Klasse an resolve() und setzt die Registrierung der Befehle fort.
Entscheidend ist hier: Die Instanz eines Befehls wird nicht unbedingt zum selben Zeitpunkt erzeugt, zu dem der Befehl ausgeführt wird. Bei regulären Klassen, die nicht den später beschriebenen speziellen Pfad für verzögerte Registrierung nutzen, ruft resolve() das make() des Containers auf, sodass der Konstruktor und seine Abhängigkeiten bereits bei der Registrierung aufgelöst werden.
Daher kann der Konstruktor eines Paketbefehls auch dann laufen, wenn Sie nur php artisan list oder einen anderen Befehl ausführen. Dass commands() in boot() steht, garantiert für sich genommen nicht, dass die Instanziierung bis zur Ausführung verzögert wird.
Laufzeitabhängigkeiten in handle() injizieren
Als Beispiel erstellen wir einen Befehl, der nur anzeigt, ob die Paketkonfiguration aktiviert oder deaktiviert ist. Wie in der offiziellen Artisan-Dokumentation von Laravel 13 werden Name und Beschreibung mit den AttributenSignature und Description definiert.
handle(). Da Command::execute() im Ausführungspfad des Befehls das call() des Containers verwendet, werden typisierte Abhängigkeiten injiziert.
Auch externe API-Clients oder Synchronisierungsservices können Sie an derselben Stelle injizieren, wenn sie nur zur Laufzeit benötigt werden. Hat jedoch die Erzeugung des Services selbst oder das boot() eines anderen Providers Seiteneffekte, werden diese dadurch nicht automatisch verhindert.
Wenn Sie einen eigenen Konstruktor definieren, rufen Sie auch
parent::__construct() auf. Der übergeordnete Konstruktor von Laravel konfiguriert den Befehl anhand von Attributen, Signature, Description und Ähnlichem.
Signature und verzögerte Registrierung sind getrennte Mechanismen
Das untersuchteApplication::resolve() enthält einen Zweig, der den name des Symfony-Attributs AsCommand liest und die Zuordnung von Name und Klasse in einer Command Map speichert. In diesem Zweig wird das make() des Containers nicht sofort aufgerufen. Der Console Kernel ruft später setContainerCommandLoader() auf und richtet einen Loader ein, der diese Map verwendet.
Das im obigen Beispiel verwendete Laravel-Attribut Signature ist dagegen unabhängig von dieser AsCommand-Prüfung. Allein durch das Hinzufügen von Signature gelangt eine Klasse nicht auf diesen Pfad der verzögerten Registrierung.
Behandeln Sie diesen Unterschied als Hinweis beim Lesen der internen Implementierung und halten Sie Erzeugungskosten und Seiteneffekte zunächst mit der offiziellen Befehlsdefinition und der Abhängigkeitsinjektion in handle() gering. Auch wenn ein verzögerter Loader vorhanden ist, können Befehlsinformationen etwa für Auflistungen oder die Hilfe benötigt werden. Gehen Sie beim Design daher nicht davon aus, dass „der Befehl ausschließlich dann erzeugt wird, wenn er ausgewählt und ausgeführt wird“.
Die Registrierung von Befehlen nicht verzögern
Das verzögerte Laden von Service-Bindings und das Bekanntmachen eines Befehls bei Artisan sind zwei verschiedene Dinge. Wenn ein ProviderDeferrableProvider implementiert, wird sein boot() unter Umständen gar nicht ausgeführt, bis der betreffende Service benötigt wird.
Wenn Sie nur die Befehlsregistrierung in dieses boot() legen und der Provider bis zum Start von Artisan nicht geladen wird, wird auch der Start-Callback, der den Befehl bekannt macht, nicht registriert. Selbst wenn ein in die Abhängigkeitsinjektion von handle() verlagerter Service den deferred Provider laden würde, löst das nicht das Problem, das auftritt, bevor Artisan den Befehl findet.
Damit Befehle zuverlässig verfügbar sind, können Sie die Registrierung in einem sofort geladenen Provider unterbringen und nur die schwergewichtigen Service-Bindings in einen separaten deferred Provider auslagern. Die konkreten Anforderungen finden Sie unter Deferred Service Provider.
Was bei Updates zu prüfen ist
Befehlsnamen, Argumente, Optionen, Ausgaben und Exit-Codes sind öffentliche Schnittstellen, die von Shell-Skripten und CI Ihrer Nutzer verwendet werden. Ändern Sie sie nicht so unbekümmert wie klasseninterne Details, sondern prüfen Sie Kompatibilität und Migrationsschritte.
Für den obigen
StatusCommand können Sie Ausgabe und Exit-Code in einer Testumgebung, in der der Provider geladen ist, wie folgt überprüfen. Zur Testinfrastruktur siehe Pakete testen, Details zu Assertions finden Sie unter Console Tests.
Herangezogene Primärquellen
- Offizielle Laravel-Dokumentation: Befehle aus Paketen registrieren
- Offizielle Laravel-Dokumentation: Befehlsstruktur und Abhängigkeitsinjektion
- ServiceProvider: commands()
- Console Application: starting(), bootstrap() und resolve()
- Console Kernel: getArtisan() und Registrierung des Loaders
- Command: Konstruktor und execute()
- Offizielle Tests: Auflösung von Befehlen und Alias-Registrierung mit AsCommand