コマンドライン / CLI / コンソール

aura/framework-project / aura/cli-projectaura/cli-kernel に統合されました。

Aura.Cliの機能

Context

Context オブジェクトはCLI環境の情報、フラグやオプションなどあらゆるものを提供します。 (これはコマンドライン環境において Web Request オブジェクトに相当するものです。)

$_ENV$_SERVER$argvはそれぞれ、$env$server$argvとしてアクセスすることができます。(Note: これらのプロパティは Context がインストールされた時の状態のスーパーグローバル変数が格納されます。)また、これらの値が存在しない時に利用されるデフォルト値を渡すこともできます。

<?php
// スーパーグローバル変数をコピー
$env    = $context->env->get();
$server = $context->server->get();
$argv   = $context->argv->get();

// これは以下と同様です:
// $value = isset($_ENV['key']) ? $_ENV['key'] : null;
$value = $context->env->get('key');

// これは以下と同様です:
// $value = isset($_ENV['key']) ? $_ENV['key'] : 'other_value';
$value = $context->env->get('key', 'other_value');
?>

Getopt サポート

Context オブジェクトはコマンドラインのオプションやパラメータ、positional arguments の取得をサポートします。

オプションやコマンドラインからパースされた $argv 引数を取得するには、 Context オブジェクトの getopt() メソッドを利用します。このメソッドは GetoptValues オブジェクトを返却します。

オプションやパラメータの定義

getopt() は渡された array of option definition によってコマンドラインオプションを認識しています。The definition array と似たフォーマットを持っていますが、正確には違います。こちらはgetopt()によって利用されます。 その代わりに、文字列のshort flagsと 配列に分割された long options 、それらは両方とも単一配列の要素として定義されます。 *がオプション名のあとに示されると、それを何度も利用することできます。その値は配列として保存されます。

<?php
$options = array(
    'a',        // short flag -a です。パラメータの指定を許可しません。
    'b:',       // short flag -b です。パラメータの指定は必須です。
    'c::',      // short flag -c です。パラメータの指定は任意です。
    'foo',      // long option --foo です。 パラメータの指定を許可しません。
    'bar:',     // long option --bar です。 パラメータの指定は必須です。
    'baz::',    // long option --baz パラメータの指定は任意です。
    'g*::',     // short flag -g です。 パラメータの指定は任意で複数指定できます。
);

$getopt = $context->getopt($options);
?>

ここで “必須” と記載する場合、それは 「そのオプションが指定されたときパラメータを持っていなくてはいけない」ことを表します。「そのオプションを指定しないといけない」とは 違います。 それらはあくまでオプションであるからです。もし特定の値を指定することを強制したい場合、positional arguments の利用を検討すべきです。

Option valuesから取得された GetoptValues オブジェクトの get()メソッドを利用してみましょう。Optionが存在しない場合のデフォルト値も設定することができます。

<?php
$a   = $getopt->get('-a', false); // -a が指定されている場合は true, 指定されていなければfalseです。
$b   = $getopt->get('-b');
$c   = $getopt->get('-c', 'default value');
$foo = $getopt->get('--foo', 0); // --foo が指定されていれば true, 逆の場合 false です。
$bar = $getopt->get('--bar');
$baz = $getopt->get('--baz', 'default value');
$g   = $getopt->get('-g', []);
?>

もしオプション名のエイリアスを設定したい場合、カンマ区切りで2つの名前を指定することができます。値はそれぞれの名前で保存されます。

<?php
// -fは--fooのエイリアスです。
$options = array(
    'foo,f:',  // long option --foo か shot flag -f で指定可能、パラメータは必須です。
);

$getopt = $context->getopt($options);

$foo = $getopt->get('--foo'); // -f も --foo も同じ値です。
$f   = $getopt->get('-f'); // -f も --foo も同じ値です。
?>

もし何度も同じオプションを指定することを許可したい場合、オプション名の最後に ‘*’ を付与します。

<?php
$options = array(
    'f*',
    'foo*:'
);

$getopt = $context->getopt($options);

// もし script が以下の指定とともに実行された場合 :
// php script.php --foo=foo --foo=bar --foo=baz -f -f -f
$foo = $getopt->get('--foo'); // ['foo', 'bar', 'baz']
$f   = $getopt->get('-f'); // [true, true, true]
?>

もしユーザが定義に従わないオプションを指定した場合、 GetoptValues オブジェクトは解析されたエラーに関する様々なエラーを保持します。それらの場合、 hasErrors()true を返却し、エラーを確認することができます。(そのエラーは実際には AUra\Cli\Exception オブジェクトですが、エラーが起こった時点では throw されません。これはエラーを無視するかどうかを自由に決定するためです。)

<?php
$getopt = $context->getopt($options);
if ($getopt->hasErrors()) {
    $errors = $getopt->getErrors();
    foreach ($errors as $error) {
        // Stdioオブジェクトを使った stderr エラーを発行します。
        $stdio->errln($error->getMessage());
    }
};
?>

positional arguments

コマンドラインに渡されたpositional argumentsを取得する場合、 get メソッドを利用すると特定の引数のpositionを取得することができます。

<?php
$getopt = $context->getopt();

// このスクリプトが以下の指定とともに実行された場合:
// php script.php arg1 arg2 arg3 arg4

$val0 = $getopt->get(0); // script.php
$val1 = $getopt->get(1); // arg1
$val2 = $getopt->get(2); // arg2
$val3 = $getopt->get(3); // arg3
$val4 = $getopt->get(4); // arg4
?>

定義されたオプションは引数から自動で削除されます。

<?php
$options = array(
    'a',
    'foo:',
);

$getopt = $context->getopt($options);

// このscriptが以下の指定とともに実行された場合:
// php script.php arg1 --foo=bar -a arg2
$arg0 = $getopt->get(0); // script.php
$arg1 = $getopt->get(1); // arg1
$arg2 = $getopt->get(2); // arg2
$foo  = $getopt->get('--foo'); // bar
$a    = $getopt->get('-a'); // 1
?>

もし short flag がオプションパラメータを持っていた場合、その引数はオプション値として扱われます。引数としては扱われません。

標準入力 / 出力ストリーム

Stdio オブジェクト は標準入出力ストリームとともに動作します。(これはコマンドライン環境における Web Response オブジェクトと同等のものです。)

標準では php://stdinphp://stdout、そして php://stderr が利用されますが、 好きなストリームを newStdio() メソッドに渡すパラメータとして指定できます。

_Stdio オブジェクトは以下のメソッドを持ちます。

  • getStdin(), getStdout(), getStderr() はそれぞれの Handle オブジェクトをを返却します。
  • outln(), out()stdout に line encoding あり、もしくはなしで出力します。
  • errln(), err()stdout に line encoding あり、もしくはなしで出力します。
  • inln(), in()stdin からユーザからの入力を読み取ります。inln()in() では除去される行末文字も含めます。

出力やエラー文字に対して、テキストカラーやテキストサイズ、背景色などのフォーマットマークアップを設定できます。 こちらも参照してください。 formatter cheat sheet

<?php
// stdoutに出力します
$stdio->outln('これはノーマルテキストです。');

// stderrに出力します
$stdio->errln('<<red>>これは赤色のエラーです。');
$stdio->errln('フォーマットが再度変更されるまで出力は赤色のままです。<<reset>>');
?>

終了コード

本ライブラリは終了ステータスコードを Status クラスが定義しています。可能な限りそれを利用すべきです。 例えば、コマンドが間違った引数の数や不適切なオプションフラグで利用された場合、 Status::USAGE とともに exit() を実行します。終了ステータスコードはこちらで確認できます。 sysexits.h

コマンドの作成

Aura.Cli は 抽象クラスやベースコマンドクラスを利用しませんが、その代わりに簡単にコマンドを実装できます。 これはクラスを使ったロジックと似たスタンドアローンコマンドスクリプトです。hello とファイル名を付けて以下のように実行してみてください。 php hello [-v,--verbose] [name].

<?php
use Aura\Cli\CliFactory;
use Aura\Cli\Status;

require '/path/to/Aura.Cli/autoload.php';

// contextとstdioオブジェクトを取得
$cli_factory = new CliFactory;
$context = $cli_factory->newContext($GLOBALS);
$stdio = $cli_factory->newStdio();

// getoptでオプションと名前付き引数を定義
$options = ['verbose,v'];
$getopt = $context->getopt($options);

// 誰にhelloと出力するか?
$name = $getopt->get(0);
if (! $name) {
    // エラー出力
    $stdio->errln("Please give a name to say hello to.");
    exit(Status::USAGE);
}

// say hello
if ($getopt->get('--verbose')) {
    // 詳細エラー
    $stdio->outln("Hello {$name}, it's nice to see you!");
} else {
    // エラー
    $stdio->outln("Hello {$name}!");
}

// done!
exit(Status::SUCCESS);
?>

コマンドヘルプの作成

コマンドに対して便利なヘルプを出力したい時があると思います。 Aura.Cli では Help オブジェクトが実装すべきコマンドとの分離を行います。継承して利用します。

例えば Help オブジェクトを継承し init() メソッドをオーバーライドします。

<?php
use Aura\Cli\Help;

class MyCommandHelp extends Help
{
    protected function init()
    {
        $this->setSummary('A single-line summary.');
        $this->setUsage('<arg1> <arg2>');
        $this->setOptions(array(
            'f,foo' => "The -f/--foo option description",
            'bar::' => "The --bar option description",
        ));
        $this->setDescr("A multi-line description of the command.");
    }
}
?>

このクラスをインスタンス化して getHelp() を実行すれば Stdio を通して出力されます。

<?php
use Aura\Cli\CliFactory;
use Aura\Cli\Context\OptionFactory;

$cli_factory = new CliFactory;
$stdio = $cli_factory->newStdio();

$help = new MyCommandHelp(new OptionFactory);
$stdio->outln($help->getHelp('my-command'));
?>
  • コマンド名をhelpクラスの外部で保持するのは、そのコマンド名が違うプロジェクトで異なったマッピングをされるかもしれないからです。

  • Help オブジェクトに GetoptParser が渡されるのはオプション定義をパースできるようにするためです。

  • Help オブジェクトの外で getOptions() を使ってオプション定義を取得できるのは、 仮コマンドオブジェクトに対して Help オブジェクトを渡すことを許可していることに加え、定義を再利用するためです。

出力はこのようになります。

SUMMARY
    my-command -- A single-line summary.

USAGE
    my-command <arg1> <arg2>

DESCRIPTION
    A multi-line description of the command.

OPTIONS
    -f
    --foo
        The -f/--foo option description.

    --bar[=<value>]
        The --bar option description.

formatter cheat sheet

POSIXターミナルでは <<markup>> 文字列が表示を変更します。Note: これらはHTMLをタグではありません。この文字列はターミナルコントロールコードに変換されますが、閉じられることはありません。好きなスペース区切りのマークアップコードをdouble angle-brackets(« »)で指定できます。

reset       表示をデフォルトに戻す

black       黒色文字
red         赤色文字
green       緑色文字
yellow      黄色文字
blue        青色文字
magenta     マゼンタ(紫)色文字
cyan        シアン(水)色文字
white       白色文字

blackbg     黒色背景
redbg       赤色背景
greenbg     緑色背景
yellowbg    黄色背景
bluebg      青色背景
magentabg   マゼンタ(紫)背景
cyanbg      シアン(水)色背景
whitebg     白色背景

bold        文字を太字
dim         文字をdim
ul          文字にアンダーライン
blink       文字を点滅
reverse     文字と背景を反転

例えば出力やエラー文字列に、太字と白文字を赤の背景色にセットしたい場合、<<bold white redbg>> を指定します。 標準に戻すには <<reset>> を指定します。

サービス

Aura.Cli_Kernel は次の Container に格納されたサービスオブジェクトを定義します。

  • aura/cli-kernel:dispatcher: Aura\Dispatcher\Dispatcher インスタンス
  • aura/cli-kernel:context: Aura\Cli\Context インスタンス
  • aura/cli-kernel:stdio: Aura\Cli\Stdio インスタンス
  • aura/cli-kernel:help_service: Aura\Cli_Kernel\HelpService インスタンス
  • aura/project-kernel:logger: Monolog\\Logger インスタンス

クイックスタート

Dependency Injection Container は aura framework projectの処理における絶対的な中心です。項目を進める前にこちらを参照してください。 ディペンデンシーインジェクション

Aura.Cli の Context, Stdio, Status オブジェクトを理解するには Dispatching も参照してください。

プロジェクトの設定

全てのAuraプロジェクトは同じ設定方法を用います。こちらも参照してください。

ロギング

ログは自動で {$PROJECT_PATH}/tmp/log/{$mode}.log に出力されます。もしロギングの挙動を変更したい場合、関連する設定ファイル(例えば config/Dev.php)で aura/project-kernel:logger サービスを変更することで設定することが出来ます。

コマンド

コマンドはプロジェクトの config/ により設定されます。もしコマンドが全ての設定モードで必要な場合、プロジェクトの config/Common.php クラスを変更してください。もしそれが特定のモードだけの場合、例えば dev モードの場合そのモードに対する設定ファイルを変更してください。 こちらは2つの異なるスタイルでのコマンド定義です。

マイクロフレームワークスタイル

以下の例は aura/cli-kernel:context サービスと aura/cli-kernel:stdio サービスを使った、通常終了コードを出力するコマンドの例です。(このディスパッチャーにセットされる名前はコマンド名と対になっています。)

<?php
namespace Aura\Famework_Project\_Config;

use Aura\Di\Config;
use Aura\Di\Container;

class Common extends Config
{
    // ...

    public function modifyCliDispatcher(Container $di)
    {
        $context = $di->get('aura/cli-kernel:context');
        $stdio = $di->get('aura/cli-kernel:stdio');
        $dispatcher = $di->get('aura/cli-kernel:dispatcher');
        $dispatcher->setObject(
            'foo',
            function ($id = null) use ($context, $stdio) {
                if (! $id) {
                    $stdio->errln("Please pass an ID.");
                    return \Aura\Cli\Status::USAGE;
                }

                $id = (int) $id;
                $stdio->outln("You passed " . $id . " as the ID.");
            }
        );
    }
?>

このコマンドを実行すると以下の通り出力されます。

cd {$PROJECT_PATH}
php cli/console.php foo 88

(もしID引数を指定していない場合エラーメッセージが表示されるはずです。)

フルスタックスタイル

マイクロフレームワークスタイルからフルスタックスタイルに変更する(もしくは最初からフルスタックで始める)ことも出来ます。 まずはじめに、コマンドクラスの定義そしてプロジェクトの src/ ディレクトリへの配置を行います。

<?php
/**
 * {$PROJECT_PATH}/src/App/Command/FooCommand.php
 */
namespace App\Command;

use Aura\Cli\Stdio;
use Aura\Cli\Context;
use Aura\Cli\Status;

class FooCommand
{
    public function __construct(Context $context, Stdio $stdio)
    {
        $this->context = $context;
        $this->stdio = $stdio;
    }

    public function __invoke($id = null)
    {
        if (! $id) {
            $this->stdio->errln("Please pass an ID.");
            return Status::USAGE;
        }

        $id = (int) $id;
        $this->stdio->outln("You passed " . $id . " as the ID.");
    }
}
?>

次にDI ContainerFooCommand のビルド方法を伝えます。 config/Common.php を編集し、 Container に対して aura/cli-kernel:context サービスと aura/cli-kernel:stdio サービスを FooCommand のコンストラクタに渡すように設定します。そして App\Command\FooCommand オブジェクトを foo という名前のディスパッチャーに対して遅延インストールします。

<?php
namespace Aura\Famework_Project\_Config;

use Aura\Di\Config;
use Aura\Di\Container;

class Common extends Config
{
    public function define(Container $di)
    {
        $di->set('aura/project-kernel:logger', $di->newInstance('Monolog\Logger'));

        $di->params['App\Command\FooCommand'] = array(
            'context' => $di->lazyGet('aura/cli-kernel:context'),
            'stdio' => $di->lazyGet('aura/cli-kernel:stdio'),
        );
    }

    // ...

    public function modifyCliDispatcher(Container $di)
    {
        $dispatcher = $di->get('aura/cli-kernel:dispatcher');

        $dispatcher->setObject(
            'foo',
            $di->lazyNew('App\Command\FooCommand')
        );
    }
?>

このコマンドを実行すると以下の通り出力されます。

cd {$PROJECT_PATH}
    php cli/console.php foo 88

(もしID引数を指定していない場合エラーメッセージが表示されるはずです。)