symfony/consoleで少しコマンドらしいツールを作る
はじめに
日常的にツールを適当なPHPファイル(test.phpなど)を作って書いていたが、味気ないのでsymfony/console
を使って少しコマンドらしいツールに変えてみた。
環境
- PHP 8.3.12
ツールを作る
symfony/console
パッケージのインストール
composer require symfony/console
この記事を書いた時点ではバージョンは7.1.5です。
symfony/console 7.1.5 Eases the creation of beautiful and testable command line interfaces
コマンドクラスの作成
とりあえず、中身は後回しでクラスだけ作ります。
<?php
declare(strict_types=1);
namespace Sample;
use Symfony\Component\Console\Command\Command;
class SampleCommand extends Command
{
}
適当にネームスペースを付けたのでcomposer.jsonのオートロードに設定を追加しておきます。
"autoload": {
"psr-4": {
"Sample\\": "src/"
}
}
設定を追加したので、composer dump-autoload
コマンドを実行しておきます。
composer dump-autoload
コマンド名と説明文を追加
Symfony\Component\Console\Attribute\AsCommand
を使うことでコマンド名と説明文を追加することができる。
さきほどのSampleCommand
に追記すると次ようになる。
<?php
declare(strict_types=1);
namespace Sample;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
#[AsCommand(
name: 'sample',
description: 'サンプルコマンド'
)]
class SampleCommand extends Command
{
}
コマンド実行時の引数を追加
Symfony\Component\Console\Command\Command
に定義しているconfigure
メソッドをオーバーライドする。
引数の指定はaddArgument
メソッドを使う。
- 第1引数はキー
- 第2引数は必須or任意
- 第3引数はキーの説明文(未指定なら空白)
- 第4引数はデフォルト値(未指定ならnull)
<?php
declare(strict_types=1);
namespace Sample;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
#[AsCommand(
name: 'sample',
description: 'サンプルコマンド'
)]
class SampleCommand extends Command
{
protected function configure()
{
$this->addArgument('text', InputArgument::REQUIRED) // 必須
->addArgument('sub-text', InputArgument::OPTIONAL, '', 'empty'); // 任意
}
}
実行時の処理を追加
Symfony\Component\Console\Command\Command
に定義しているexecute
メソッドをオーバーライドする。
今回は入力された値を表示するだけなので、OutputInterface
のwriteln
メソッドを使う。
<?php
declare(strict_types=1);
namespace Sample;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
#[AsCommand(
name: 'sample',
description: 'サンプルコマンド'
)]
class SampleCommand extends Command
{
protected function configure()
{
$this->addArgument('text', InputArgument::REQUIRED)
->addArgument('sub-text', InputArgument::OPTIONAL, '', 'empty');
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$output->writeln('Sample Command!');
$output->writeln($input->getArgument('text'));
$output->writeln($input->getArgument('sub-text'));
return Command::SUCCESS;
}
}
実行ファイルの作成
実行するファイルも用意します。
#!/usr/bin/env php
<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use Sample\SampleCommand;
use Symfony\Component\Console\Application;
$application = new Application();
$application->add(new SampleCommand());
$application->run();
動作確認
作成したコマンドを動かしてみる。
php bin/console sample サンプル
Sample Command!
サンプル
empty
1つ目の引数の内容は表示して、2つ目の引数は未指定なのでデフォルト値が表示できました。
php bin/console sample サンプル1 サンプル2
Sample Command!
サンプル1
サンプル2
2つ目の引数も指定すると表示できましたね。
まとめ
symfony/console
を使うことで、少しコマンドらしいツールに変化しました。
今回のようなコマンドならパッケージを使う必要もないかもしれないけど、サンプルなのでそこは気にしないです。
おまけ
今回くらいの内容なら、SingleCommandApplication
[1]クラスを使って書いてもいいかもしれない。
#!/usr/bin/env php
<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\SingleCommandApplication;
(new SingleCommandApplication())
->addArgument('text', InputArgument::REQUIRED)
->addArgument('sub-text', InputArgument::OPTIONAL, '', 'empty')
->setCode(function (InputInterface $input, OutputInterface $output): int {
$output->writeln('Sample Command!');
$output->writeln($input->getArgument('text'));
$output->writeln($input->getArgument('sub-text'));
return SingleCommandApplication::SUCCESS;
})
->run();
実行してみると、
php bin/console サンプル1 サンプル2
Sample Command!
サンプル1
サンプル2
同じように表示できましたね。
また、コマンドは1つしかないないので、コマンド名は指定しないでも実行可能です。
参考
Discussion