Why

隨著 Laravel 開發的指令越多,要記得每一個指令的檔案位置也相對困難。由於從指令列執行 artisn list 只顯示每個指令所設定的 signature 屬性,但不曉得對應的檔案名稱。只期望 signature 跟檔案名稱具有相似性,可以順藤摸瓜。也因此才有了開發相關套件的念頭。

How

之前在 Laravel Goto 的 VSCode 套件開發上,是透過檢索每一個指令的 signature 屬性,建構出每一個指令與相對應檔案的關聯。這次希望透過 artisan 指令的執行,能夠將指令的類別名稱與路徑名稱顯示出來。

最初的構想是透過添加一個全域型的 --classname 參數顯示指令列表的類別名稱,類似如下:

$php ./artisan --classname
Available commands:
  clear-compiled        Illuminate\Foundation\Console\ClearCompiledCommand
  completion            Symfony\Component\Console\Command\DumpCompletionCommand
  ...

不過翻開原始碼,artisn list 指令藉由 Symfony\Component\Console\Command\ListCommand 顯示其內容。

ListCommand 藉由 Symfony\Component\Console\Helper\DescriptorHelper 挑選出合適的 Descriptor 類別。預設會使用 Symfony\Component\Console\Descriptor\TextDescriptor,其中 describeApplication 方法描述如何渲染 artisn list 指令的顯示內容。

如果要達到上述的功能,勢必須要修改 TextDescriptor 類別。要動到 Symfony 的套件難度太大。後續決定以獨立指令的方式來實現。

Now

後續建立了一個全新的 Laravel Package,設計一個名為 classname 的指令。並且藉由繼承 TextDescriptor 並覆寫 describeApplication 來實現。由於原本的實作只需少許修改就能滿足需求,也因此幾乎照搬 describeApplication 的程式碼。由於希望該套件能能支援 Laravel 5.6 ~ 10.x 之間的版本,相依的 symfony/console 套件也橫跨了 4.x ~ 6.x 之間的版本。其中差異的部分是對於字元寬度的計算邏輯,透過簡單的邏輯判斷解決了該問題。

另外為了能夠顯示指令的路徑,增加 --path 選項,並利用 ReflectionClass 反射取得檔案位置:

$reflector = new ReflectionClass($command);
$commandDescription = $reflector->getFileName();

最後增加 command-name 參數,可以針對特定的指令顯示其指令類別

# classname [options] [--] [<command-name>]
$php ./artisan classname clear-compiled

 Available commands:
  clear-compiled        Illuminate\Foundation\Console\ClearCompiledCommand

完整的專案位置:Laravel Command Classname