Symfonyのconsoleコンポーネントを使ってCLIコマンドを作成する

Httpで出力させているものをCLIでみたいとの要望があったので対応した。色々躓いた点があったのでメモ。

#CLI対応の流れ
1. symfony/consoleコンポーネントをインストール
2. Commandファイルを作成
3. composerのautoloaderにCommandファイルの名前空間を指定し読み込まれるようにする
4. 実行ファイル（app/console）を作成
5. 実行

#詳細
##1. symfony/consoleコンポーネントをインストール

```json:composer.json
{
	"require":{
		"symfony/http-foundation":"2.1.x-dev",	
		"pimple/pimple": "1.0.0",
		"guzzlehttp/guzzle": "~5.0",
		"symfony/console": "2.4.*" ←追加
	}
}
```

その後composerを実行。

```linux
$ composer install
//もしくは下記で実行してもok。下記はautoloadだけを更新するのでよりスマート。
$ composer dumpautoload
```

ちゃんとコンポーネントがインストールされているかチェック。

```linux
$ vendor/symfony/console
```

これでok。つぎにこれらコンポーネントが自動読み込みされるようにbootstrap.phpに追加。composerに追加されたコンポーネントはすべてvendor/autoload.phpを読み込むことで自動読み込みされる。

```php:bootstrap.php
require 'vendor/autoload.php'; //←追加
require 'config.php';
require 'model.php';
require 'controller.php';
require 'service/apiService.php';
```


##2. Commandファイルを作成
つぎにCommandファイルを作成する。

```linux 
$ mkdir Command
$ vim Command/AnalizeCommand.php
```

コマンドファイルの接尾語はxxxCommand.phpである必要があるので注意。

```php:AnalizeCommand.php
namespace Command;

use Command\CommandWrapper;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Formatter\OutputFormatterStyle;

class AnalizeCommand extends CommandWrapper
{
	protected function configure()
	{
		$this
			->setName('traintrack:analize')
			->setDescription('Analize location infomation')
		;
	}
	protected function execute(Inputinterface $input, OutputInterface $output)
	{
		$muchData = $this->container['model.allData'];
		foreach($muchData as $data):
			$text = 'id：'.$data['id'].' user_id：'.$data['user_id'];
			$output->writeln('<info>'.$text.'</info>');
		endforeach;
	}
}

```

execute時にcontainerを使いたかったのでCommand\CommandWrapperクラスをつくりそれを継承する形で実装した。

```php:CommandWrapper.php
namespace Command;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

abstract class CommandWrapper extends Command
{
	protected $container;
	public function __construct($container)
	{
		$this->container = $container;
		parent::__construct(null);
	}
}
```

ここらへんはSymfony2のクックブックを参照のこと。
[コンソール/コマンドラインツールとしてのコマンドの作成方法](http://docs.symfony.gr.jp/symfony2/cookbook/console.html)

##3. composerのautoloaderにCommandファイルの名前空間を指定し読み込まれるようにする
Commandファイル中で名前空間を利用しているので、これを認識させる必要がある。ここでもcomposerの自動読み込みを使う。

```json:composer.json
{
	"require":{
		"symfony/http-foundation":"2.1.x-dev",	
		"pimple/pimple": "1.0.0",
		"guzzlehttp/guzzle": "~5.0",
		"symfony/console": "2.4.*"
	},
	"autoload":{
		"classmap":["Command/"] ←追加
	}
}
```

autoloaderへの追加の仕方は、名前空間の指定、クラスマップの指定、ファイル自体の指定などができる。そちらに関しては[こちらのブログ](http://blog.open.tokyo.jp/2014/03/30/composer-autoload.html)を参照。

composer.jsonに登録したらアップデートしてやる。

```linux
$ composer dumpautoload
```

実際に登録されたかどうかを確認するには下記をみてみる。

```php:vendor/composer/autoload_classmap.php
// autoload_classmap.php @generated by Composer

$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);

return array(
    'Command\\AnalizeCommand' => $baseDir . '/Command/AnalizeCommand.php', //←登録済
    'Command\\CommandWrapper' => $baseDir . '/Command/CommandWrapper.php', //←登録済
);
```

これでok。

##4. 実行ファイル（app/console）を作成
最後に実行ファイルを作成する。

```linux
$ mkdir app
$ vim app/console
```

```php:conosole
#!/usr/bin/env php
<?php
set_time_limit(0);

require dirname(__FILE__).'/../bootstrap.php';

use Symfony\Component\Console\Application;
use Command\AnalizeCommand;

$app = new Application();
$app->add(new AnalizeCommand($container));
$app->run();
```

ここでは触れていないが、bootstrap.phpで読み込んでいるconfig.phpでPimpleオブジェクトの$containerを生成している。

ここでいくつか注意点がある。

+ **bootstrapの読み込み**：dirname(__FILE__)は参照元のファイル（console）からの相対パスを表す。これがないと実行元のファイルからの相対パスになってしまい、bootstrapがfile not foundとなってしまう。

+ **app/consoleに実行権限を与える**：chmod +x app/consoleをしてやる必要あり。

+ **shebang（シバン）をつけてやる必要がある**：一行目の `#!/usr/bin/env php` のことをシバンという。シェルなどのインタプリタを起動するためのもの。

##5. 実行

```linux
$ app/console traintrack:analize
```
これできちんと実行できればok。実行場所はappと同じディレクトリであること。