Httpで出力させているものをCLIでみたいとの要望があったので対応した。色々躓いた点があったのでメモ。
#CLI対応の流れ
- symfony/consoleコンポーネントをインストール
- Commandファイルを作成
- composerのautoloaderにCommandファイルの名前空間を指定し読み込まれるようにする
- 実行ファイル(app/console)を作成
- 実行
#詳細
##1. symfony/consoleコンポーネントをインストール
{
"require":{
"symfony/http-foundation":"2.1.x-dev",
"pimple/pimple": "1.0.0",
"guzzlehttp/guzzle": "~5.0",
"symfony/console": "2.4.*" ←追加
}
}
その後composerを実行。
$ composer install
//もしくは下記で実行してもok。下記はautoloadだけを更新するのでよりスマート。
$ composer dumpautoload
ちゃんとコンポーネントがインストールされているかチェック。
$ vendor/symfony/console
これでok。つぎにこれらコンポーネントが自動読み込みされるようにbootstrap.phpに追加。composerに追加されたコンポーネントはすべてvendor/autoload.phpを読み込むことで自動読み込みされる。
require 'vendor/autoload.php'; //←追加
require 'config.php';
require 'model.php';
require 'controller.php';
require 'service/apiService.php';
##2. Commandファイルを作成
つぎにCommandファイルを作成する。
$ mkdir Command
$ vim Command/AnalizeCommand.php
コマンドファイルの接尾語はxxxCommand.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クラスをつくりそれを継承する形で実装した。
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のクックブックを参照のこと。
コンソール/コマンドラインツールとしてのコマンドの作成方法
##3. composerのautoloaderにCommandファイルの名前空間を指定し読み込まれるようにする
Commandファイル中で名前空間を利用しているので、これを認識させる必要がある。ここでもcomposerの自動読み込みを使う。
{
"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への追加の仕方は、名前空間の指定、クラスマップの指定、ファイル自体の指定などができる。そちらに関してはこちらのブログを参照。
composer.jsonに登録したらアップデートしてやる。
$ composer dumpautoload
実際に登録されたかどうかを確認するには下記をみてみる。
// 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)を作成
最後に実行ファイルを作成する。
$ mkdir app
$ vim app/console
#!/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. 実行
$ app/console traintrack:analize
これできちんと実行できればok。実行場所はappと同じディレクトリであること。