👋

symfony/consoleで少しコマンドらしいツールを作る

2024/10/21に公開

はじめに

日常的にツールを適当な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

コマンドクラスの作成

とりあえず、中身は後回しでクラスだけ作ります。

src/SampleCommand.php
<?php

declare(strict_types=1);

namespace Sample;

use Symfony\Component\Console\Command\Command;

class SampleCommand extends Command
{
}

適当にネームスペースを付けたのでcomposer.jsonのオートロードに設定を追加しておきます。

composer.json
    "autoload": {
        "psr-4": {
            "Sample\\": "src/"
        }
    }

設定を追加したので、composer dump-autoloadコマンドを実行しておきます。

composer dump-autoload

コマンド名と説明文を追加

Symfony\Component\Console\Attribute\AsCommandを使うことでコマンド名と説明文を追加することができる。

さきほどのSampleCommandに追記すると次ようになる。

src/SampleCommand.php
<?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)
src/SampleCommand.php
<?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メソッドをオーバーライドする。

今回は入力された値を表示するだけなので、OutputInterfacewritelnメソッドを使う。

src/SampleCommand.php
<?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;
    }
}

実行ファイルの作成

実行するファイルも用意します。

bin/console
#!/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]クラスを使って書いてもいいかもしれない。

bin/console
#!/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つしかないないので、コマンド名は指定しないでも実行可能です。

参考

https://zenn.dev/ttskch/articles/ef89d74b9728ad

脚注
  1. https://symfony.com/doc/current/components/console/single_command_tool.html ↩︎

GitHubで編集を提案

Discussion