Symfony2コマンドの出力先を変更 : Symfony Advent Calender 2011 JP - 4日目 -
taka512です。Symfony Advent Calendar JP 2011の4日目の記事です。
Symfonyユーザ会の大御所から引き続いての4日目ということで緊張で手がプルプル震えつつ書いてます。
今回はSymfony2コマンドを実行した際の出力先を変更する方法を紹介します。
Symfony2にはコマンドという仕組みがあります。
例えばTestCommand.phpを下記のように作成したとします。
$ vi src/Taka512/TestBundle/Command/TestCommand.php 1 <?php 2 3 namespace Taka512\TestBundle\Command; 4 5 use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; 6 use Symfony\Component\Console\Input\InputInterface; 7 use Symfony\Component\Console\Output\OutputInterface; 8 9 class TestCommand extends ContainerAwareCommand{ 10 protected function execute(InputInterface $input, OutputInterface $output){ 11 $output->writeln('TEST'); 12 $output->writeln('DAYO'); 13 } 14 protected function configure(){ 15 $this->setName('taka512:test'); 16 } 17 }
下記コマンドを実行すると標準出力に出力されます。
$ php app/console taka512:test
TEST
DAYO
さて、ここで自分はcronからこのコマンド実行したいと考えるようになりました。
ただし、cronから実行するには2つの修正したい点があります。
・標準出力には出力しない
・結果は最後に一度メールで通知を受けたい
「writeln」を使わなければ良いというのも良い判断だと思いますが、今回はwritelnの内容をメール送信する方法を探りました。
1.「$output」は何者か確認
まず、writelnが存在するクラスである$outputが何者かを見ていきます
$ vi app/console 1 #!/usr/bin/env php 2 <?php 3 4 require_once __DIR__.'/bootstrap.php.cache'; 5 require_once __DIR__.'/AppKernel.php'; 6 7 use Symfony\Bundle\FrameworkBundle\Console\Application; 8 use Symfony\Component\Console\Input\ArgvInput; 9 10 $input = new ArgvInput(); 11 $env = $input->getParameterOption(array('--env', '-e'), getenv('SYMFONY_ENV' ) ?: 'dev'); 12 $debug = !$input->hasParameterOption(array('--no-debug', '')); 13 14 $kernel = new AppKernel($env, $debug); 15 $application = new Application($kernel); 16 $application->run(); $ vi vendor/symfony/src/Symfony/Component/Console/Application.php 107 public function run(InputInterface $input = null, OutputInterface $output = null) 108 { 109 if (null === $input) { 110 $input = new ArgvInput(); 111 } 112 113 if (null === $output) { 114 $output = new ConsoleOutput(); 115 }
「app/console」の16行目で何も引数がわたされなければ「ConsoleOutput」クラスとなるということがわかりました。
ConsoleOutputクラスをメール送信を行うクラスに置き換えができれば良さそうです。
ConsoleOutputクラスのソースは下記構成となってます。
(1)でコンソールはストリーム出力と設定
(2)でストリームへの出力処理実装
(3)で出力フォーマット処理の実装
(1) ./vendor/symfony/src/Symfony/Component/Console/Output/ConsoleOutput.php ↓を継承 (2) ./vendor/symfony/src/Symfony/Component/Console/Output/StreamOutput.php ↓を継承 (3) ./vendor/symfony/src/Symfony/Component/Console/Output/Output.php ↓を実装 (4) ./vendor/symfony/src/Symfony/Component/Console/Output/OutputInterface.php
2. メール送信クラスの実装
前項の(1)〜(2)は標準出力への出力の実装クラスであるので(3)を継承しつつ新規にメール送信クラスを作成します。
構成は下記となります。ソースは少し長くなるので貼り付けせずにリンクしてます。
ソースは 「./vendor/Taka512/lib/Taka512/Console/Output」ディレクトリを作成してファイルを置いてます。
(オートローダに「'Taka512' => __DIR__ . '/../vendor/Taka512/lib',」を登録する必要があります)
(1)バッファされた結果をメールする
https://github.com/taka512/Symfony2-lib/blob/master/lib/Taka512/Console/Output/MailOutput.php
(2)writelnされた結果をバッファ+バッファをフラッシュするflushメソッドを追加
https://github.com/taka512/Symfony2-lib/blob/master/lib/Taka512/Console/Output/BufferOutput.php
(1) ./vendor/Taka512/lib/Taka512/Console/Output/MailOutput.php ↓を継承 (2) ./vendor/Taka512/lib/Taka512/Console/Output/BufferOutput.php ↓を継承 (3) ./vendor/symfony/src/Symfony/Component/Console/Output/Output.php ↓を実装 (4) ./vendor/symfony/src/Symfony/Component/Console/Output/OutputInterface.php
3. flushメソッドの追加
前項の(2)で実装したflushメソッドを「TestCommand.php」に追加します。
$ vi src/Taka512/TestBundle/Command/TestCommand.php 9 class TestCommand extends ContainerAwareCommand{ 10 protected function execute(InputInterface $input, OutputInterface $output){ 11 $output->writeln('TEST'); 12 $output->writeln('DAYO'); 13 $output->flush(); // 追加 13 }
4. ConsoleOutputクラスをMailOutputクラスに置換
まだ、「$output」はConsoleOutputクラスのままなのでここをMailOutputクラスに置き換えを行います。
「app/console」ファイルは修正すると危険なので「app/batch」ファイルを新規に作成します。
16行目と25、26行目を追加してます。
$ cp app/console app/batch $ vi app/batch 1 #!/usr/bin/env php 2 <?php 3 4 // if you don't want to setup permissions the proper way, just uncomment the following PHP line 5 // read http://symfony.com/doc/current/book/installation.html#configuration-and-setup for more information 6 //umask(0000); 7 8 set_time_limit(0); 9 10 require_once __DIR__.'/bootstrap.php.cache'; 11 require_once __DIR__.'/AppKernel.php'; 12 13 use Symfony\Bundle\FrameworkBundle\Console\Application; 14 use Symfony\Component\Console\Input\ArgvInput; 15 // add 16 use Taka512\Console\Output\MailOutput; 17 18 $input = new ArgvInput(); 19 $env = $input->getParameterOption(array('--env', '-e'), getenv('SYMFONY_ENV') ?: 'dev'); 20 $debug = !$input->hasParameterOption(array('--no-debug', '')); 21 22 $kernel = new AppKernel($env, $debug); 23 $application = new Application($kernel); 24 // need Container 25 $kernel->boot(); 26 $output = new MailOutput($kernel->getContainer()->get('mailer')); 27 $application->run(null, $output);
これでコマンドを実行すると標準出力には何も出力されずにメールが送信されるようになります。
$ php app/batch taka512:test
$
以上長くなってしまったのですが、Symfony2の挙動を変更したい場合の例を紹介してみました。
(実際はメールの送信/受信先をパラメータから取得するとか他にも処理を追加する必要があります)
ということで5日目はたくさんネタを持ってそうなfivestrさんです。よろしくお願いします。