Skip to main content
想為套件加入用於診斷、同步、安裝的 Artisan 指令時,需要將指令的註冊、實例的建立與處理的執行分開思考。若在建構子中解析僅在執行時才需要的服務,可能會連無關的 Artisan 指令啟動也受到牽連。 本頁會確認 Laravel Framework v13.35.0 的實作,深入探討 Laravel 套件開發中的 commands() 註冊範例。檔案產生的實作請參閱 GeneratorCommand,參與最佳化任務請參閱整合至 optimize。

從提供者明確註冊

套件內的指令要在服務提供者的 boot() 中註冊類別名稱。應用程式針對 app/Console/Commands 的自動偵測,與套件所提供類別的註冊是不同的入口。
此範例的前提是,提供者會透過套件自動偵測或應用程式端的明確註冊而被載入。僅放置指令類別,並不會自動註冊套件的提供者。 runningInConsole() 判斷的是是否為主控台環境,而非是否正在執行特定指令。若也支援在 HTTP 請求中呼叫 Artisan::call() 的用途,就要考量因此條件而未被註冊的可能性,並在包含無條件註冊在內的選項中決定使用約定。從程式中執行的方式請參閱 Artisan。

commands() 不會當場執行指令

ServiceProvider::commands() 會將使用所接收類別名稱的閉包註冊到 Illuminate\Console\Application::starting()。此時並不會呼叫指令的 handle()。 在一般的 Console Kernel 中,之後建立 Artisan Application 時,會執行已註冊的啟動回呼。此時 resolveCommands() 會將各個類別傳給 resolve(),進行指令的註冊。 這裡的重點在於:指令的實例建立,不一定與該指令的執行發生在同一時機。對於不使用後文所述專用延遲註冊路徑的一般類別,resolve() 會呼叫容器的 make(),因此在註冊時就會解析建構子及其相依性。 因此,僅執行 php artisan list 或其他指令,套件指令的建構子也可能會執行。將 commands() 放在 boot() 中這件事本身,並不能保證實例建立會延後到執行時。

將執行時的相依性注入 handle()

以下以僅顯示套件設定啟用或停用狀態的指令為例。與 Laravel 13 官方 Artisan 文件相同,使用 Signature、Description 屬性定義名稱與說明。
此範例不加入建構子,而是將設定 Repository 作為 handle() 的引數接收。在指令的執行路徑中,Command::execute() 會使用容器的 call(),因此會注入已指定型別的相依性。 使用外部 API 用戶端或同步服務時,若僅在執行時需要,也可以注入到相同位置。不過,若服務本身的建立或其他提供者的 boot() 有副作用,並不會自動防止這些副作用。
請勿在建構子中連線至外部 API、更新資料庫或覆寫已發佈的檔案等。即使在剛安裝完成或連線設定尚未完成的狀態下,套件的使用者也必須能使用 list、help。
自行定義建構子時,也要呼叫 parent::__construct()。因為 Laravel 的父類別建構子會根據屬性、signature、description 等來組成指令。

Signature 與延遲註冊是不同的機制

在所確認的 Application::resolve() 中,有一個分支會讀取 Symfony AsCommand 屬性的 name,並將名稱與類別的對應儲存到 command map。此分支不會當場呼叫容器的 make()。Console Kernel 之後會呼叫 setContainerCommandLoader(),設定使用此 map 的載入器。 另一方面,上述範例使用的 Laravel Signature 屬性與這個 AsCommand 判斷無關。僅加上 Signature,並不會讓該類別進入這條延遲註冊路徑。 請將此差異視為閱讀內部實作時的注意事項,首先透過官方的指令定義與對 handle() 的相依性注入,降低建立成本與副作用。即使有延遲載入器,清單、說明等也可能需要指令資訊,因此不要以「除了執行所選指令時之外絕不會被建立」為前提來設計。

不要連指令的註冊也延遲

延遲載入服務繫結,與讓 Artisan 得知指令的存在是兩回事。若提供者實作了 DeferrableProvider,在需要目標服務之前,boot() 本身可能都不會執行。 若只把指令註冊放在該 boot() 中,而在 Artisan 啟動前提供者未被載入,那麼用來告知指令存在的啟動回呼也不會被註冊。即使移至 handle() 進行相依性注入的服務會載入延遲提供者,也無法解決 Artisan 找到指令之前的問題。 作為確保指令公開的架構,可以將註冊放在立即載入的提供者中,只將繁重服務的繫結拆分到另一個延遲提供者。具體要求請參閱延遲服務提供者。

更新時需要確認的事項

指令名稱、引數、選項、輸出、結束代碼,是使用者的 Shell 指令碼或 CI 所使用的公開介面。不要以變更類別內部實作的心態修改它們,而要確認相容性與遷移步驟。 以上述的 StatusCommand 為例,可以在載入提供者的測試環境中,如下驗證輸出與結束代碼。測試基礎請參閱以 Orchestra Testbench 測試 Laravel 套件,assertion 的詳細內容請參閱主控台測試。
此外,對於使用繁重服務的指令,也要驗證在執行清單或說明時不會呼叫服務的業務方法。僅確認能夠註冊,無法偵測到對無關指令的影響。

參考的第一手資料

最後修改於 2026年10月12日