symfony book 日本語ドキュメント

第16章 - アプリケーションの運用ツール

開発とデプロイの両方の期間において、開発者はアプリケーションが期待どおりに動いているかを判断するには絶え間なく一貫した診断情報が必要になります。この情報は一般的にロギングとデバッグユーティリティに集約されます。symfonyのようなフレームワークは稼働中のアプリケーションで中心的な役割を果たすので、効率的な開発と運用活動を保証するためにこれらの機能がしっかりと統合されていることは極めて重要です。

アプリケーションが運用サーバーに設置されている期間、アプリケーションの管理者はログのローテーションからアップグレードまで膨大な数のタスクを繰り返します。フレームワークはこれらのタスクを可能なかぎり自動化するツールも提供しなければなりません。

この章では、symfonyアプリケーションの運用ツールがこれらのニーズに対してどのように答えるのかを説明します。

ロギング

リクエストが実行されている間に間違ったことが行われていることを理解する唯一の方法は実行プロセスのトレースを洗い直すことです。幸いにして、このセクションで学ぶように、PHPとsymfonyの両ほうがこの種の膨大なデータをログに記録してくれます。

PHPのログ

PHPにはerror_reportingパラメーターがあります。これはphp.iniのなかで定義され、PHPのイベントが記録される場所を指定します。リスト16-1で示されるように、symfonyのsettings.ymlファイルのなかで、アプリケーションと環境ごとにこの値をオーバーライドできます。

リスト16-1 - frontend/config/settings.ymlのなかで、エラーレポートのレベルを設定する

prod:
 .settings:
    error_reporting:  <?php echo (E_PARSE | E_COMPILE_ERROR | E_ERROR | E_CORE_ERROR | E_USER_ERROR)."\n" ?>

dev:
  .settings:
    error_reporting:  <?php echo (E_ALL | E_STRICT)."\n" ?>

運用環境でパフォーマンスの問題を避けるには、サーバーはPHPの重大なエラーだけをログに記録します。しかしながら、開発環境において、すべてのタイプのイベントがログに記録されるので、開発者はエラーを追跡するために必要なすべての情報を入手できます。

PHPのログファイルの位置はphp.iniの設定によって決まります。この位置を定義しなくてもすむのであれば、おそらくPHPはWebサーバーによって提供される(Apacheのエラーログのような)ロギングファシリティを利用しています。この場合、WebサーバーのログディレクトリのもとでPHPログが見つかります。

symfonyのログ

PHPの標準のログ機能に加えて、symfonyは多くのカスタムイベントログを記録できます。myproject/log/ディレクトリのもとでsymfonyが記録するすべてのログを見ることができます。環境とアプリケーションごとに1つのファイルが存在します。たとえば、frontendアプリケーションの開発環境のログファイルはfrontend_dev.logと名づけられます。運用環境のログファイルはfrontend_prod.logと名づけられます。

symfonyアプリケーションを稼働させているのであれば、ログファイルをご覧ください。構文はシンプルです。1つのイベントごとに1つの行がアプリケーションのログファイルに追加されます。それぞれの行はイベントの正確な時間、イベントの性質、処理されているオブジェクトと追加の関連な詳細内容が含まれます。リスト16-2はログファイルの内容の例を示します。

リスト16-2 - log/frontend_dev.phpのなかの、サンプルのsymfonyログファイルの内容

Nov 15 16:30:25 symfony [info ] {sfAction} call "barActions->executemessages()"
Nov 15 16:30:25 symfony [debug] SELECT bd_message.ID, bd_message.SENDER_ID, bd_...
Nov 15 16:30:25 symfony [info ] {sfCreole} executeQuery(): SELECT bd_message.ID...
Nov 15 16:30:25 symfony [info ] {sfView} set slot "leftbar" (bar/index)
Nov 15 16:30:25 symfony [info ] {sfView} set slot "messageblock" (bar/mes...
Nov 15 16:30:25 symfony [info ] {sfView} execute view for template "messa...
Nov 15 16:30:25 symfony [info ] {sfView} render "/home/production/myproject/...
Nov 15 16:30:25 symfony [info ] {sfView} render to client

データベースに送られた実際のSQLクエリ、呼び出されたテンプレート、オブジェクト間の呼び出しのチェーンなど、これらのファイル内で多くの情報が見つかります。

symfony 1.1の新しい機能: リスト16-3で示されるようにロギングするファイルのフォーマットはfactories.ymlのなかのformatかつ/もしくはtime_format設定をオーバーライドすることで設定可能です。

リスト16-3 - ログフォーマットを変更する

all:
  logger:
    param:
      sf_file_debug:
        param:
          format:      %time% %type% [%priority%] %message%%EOL%
          time_format: %b %d %H:%M:%S

symfonyのログレベルの設定

symfonyのログメッセージには8つのレベルが存在します: PEAR::Logパッケージ(http://pear.php.net/package/Log/ )と同じく、emergalertcriterrwarningnoticeinfodebugです。リスト16-4で示されているように、それぞれのアプリケーションのlogging.yml設定ファイルのなかでそれぞれの環境でロギングされる最大レベルを設定できます。

リスト16-4 - frontend/config/logging.ymlのなかのデフォルトのロギング設定

prod:
  logger:
    param:
      level: err

デフォルトでは、運用環境を除いたすべての環境において、すべてのメッセージがログに記録されます(もっとも重要度が低いdebugレベルまで)。デフォルトでは、運用環境のロギングは無効です; settings.ymlのなかのlogging_enabledonに変更すると、もっとも重要なメッセージ(critからemergまで)のみがログファイルに現れます。

ロギングされるメッセージのタイプを制限するためにfactories.ymlファイルのなかでそれぞれの環境に対してロギングレベルを変更できます。

TIP ロギングが有効であることを確認するには、 sfConfig::get('sf_logging_enabled')を呼び出します。

ログメッセージを追加する

リスト16-5に記述されているテクニックの1つを使うことでメッセージをsymfonyのログファイルに手動で追加できます。

リスト16-5 - カスタムログメッセージを追加する

[php]
// アクションから
$this->logMessage($message, $level);

// テンプレートから
<?php use_helper('Debug') ?>
<?php log_message($message, $level) ?>

ログメッセージなどで$levelは同じ値を持つことができます。

代わりの方法として、アプリケーションの任意の場所からメッセージをログに書き込むには、リスト16-6で示されるように、sfLoggerメソッドを直接使います。利用可能なメソッドはログレベルと同じ名前を持ちます(上記の"symfonyのログレベルの設定"のセクションを参照)。

リスト16-6 - 任意の場所からカスタムログメッセージを追加する

[php]
if (sfConfig::get('sf_logging_enabled'))
{
  sfContext::getInstance()->getLogger()->info($message);
}

SIDEBAR symfony 1.1の新しい機能: ロギングをカスタマイズする

symfonyのロギングシステムはとてもシンプルなので、カスタマイズも簡単です。 唯一の前提要件はdoLog()メソッドを定義するsfLoggerクラスを拡張するロガークラスです。symfonyは2つのパラメーターで: $message(ロギングされるメッセージ)と$priority(ログのレベル)でdoLog()メソッドを呼び出します

myLoggerはPHPのerror_log 関数を利用してシンプルなロガーを定義します:

[php]
class myLogger extends sfLogger
{
  protected function doLog($message, $priority)
  {
    error_log(sprintf('%s (%s)', $message, sfLogger::getPriorityName($priority)));
  }
}

既存のクラスからロガーを作成するには、logメソッドを定義するsfLoggerInterfaceインターフェイスを実装するだけです。メソッドはdoLog()メソッドと同じ2つのパラメーターを受けとります:

[php]
require_once('Log.php');
require_once('Log/error_log.php');

// symfonyで使いたいロガーのために
// インターフェイスを実装するために薄いラッパーを定義する
class Log_my_error_log extends Log_error_log implements sfLoggerInterface
{
}

ログファイルをパージしてローテーションを決める

アプリケーションのlog/ディレクトリを定期的にパージ(消去)することを忘れないでください。これらのファイルは、トラフィック次第ですが、たいていの場合、数日で数メガバイトに成長するからです。symfonyはこの目的のために特別なlog:clearタスクを提供します。たとえば、つぎのコマンドはsymfonyのログファイルを削除します:

> php symfony log:clear

ベターなパフォーマンスとセキュリティのために、おそらくは1つの大きなファイルの代わりに、symfonyのログをいくつかの小さなファイルに保存したいと思うでしょう。ログファイルのための理想的な保存戦略はメインのログファイルをバックアップして定期的に空にする一方で、制限された数のバックアップだけ維持することです。logging.ymlのなかでこのようなログのローテーションを有効にしてパラメーターを指定できます。たとえば、リスト16-7で示されるように、7日のperiod10history(バックアップ数)によって、1つの有効なログファイルに加えて、それぞれが7日の履歴を含む10のバックアップファイルを扱うことになります。現在の有効なログファイルはバックアップに移動し、もっとも古いバックアップは消去されます。

リスト16-7 - ログのローテーションを起動する

> php symfony log:rotate frontend prod --period=7 --history=10

バックアップログファイルはlogs/history/ディレクトリに保存され、保存された時点の日付がサフィックスとして名前に追加されます。

デバッグする

どんなに熟練したプログラマであれ、symfonyを利用しているとしても、結局は間違いをします。エラーの検出と理解は早くアプリケーションを開発するための重要な要素の1つです。幸いにして、symfonyは開発者のためにいくつかのデバッグツールを提供します。

symfonyのデバッグモード

symfonyにはアプリケーションの開発とデバッグを円滑にするデバッグモードがあります。このモードがonのとき、つぎのことが起こります:

一方で、デバッグモードがoffのとき、プロセスはつぎのように扱われます:

デバッグモードはフロントコントローラー内でアプリケーションごとに有効になります。リスト16-8で示されるように、デバッグモードは getApplicationConfiguration()メソッド呼び出しに渡される3番目の引数の値によってコントロールされます。

リスト16-8 - web/frontend_dev.phpでをデバッグモードをonにしたサンプルのフロントコントローラー

[php]
<?php

require_once(dirname(__FILE__).'/../config/ProjectConfiguration.class.php');

$configuration = ProjectConfiguration::getApplicationConfiguration('frontend', 'dev', true);
sfContext::createInstance($configuration)->dispatch();

CAUTION 運用サーバーにおいて、デバッグモードを有効にすべきではありませんしデバッグモードが有効なフロントコントローラーも利用可能にすべきではありません。デバッグモードはページの配信を遅くするだけでなく、アプリケーションの内部を公開する可能性があるからです。デバッグツールがデータベース接続情報を決して公開しないとしても、例外のスタックトレースは悪意を持った訪問者のための危険な情報で満ちています。

symfonyの例外

デバッグモードで例外が起こる場合、symfonyは問題の原因を見つけるために必要なすべての情報を含む便利な例外の警告を表示します。

例外のメッセージは明確に書かれ、一番問題のありそうなありそうな原因を参照します。これらのメッセージは問題を修正するための有望な解決方法を提供し、もっとも共通の問題のために、例外ページは例外に関する詳細な情報を持つsymfonyのWebサイトページへのリンクも含みます。図16-1で示されるように、メソッド呼び出しのフルスタックと一緒に、(構文をハイライトした状態で)例外ページはPHPのコードのなかでエラーが発生した場所を表示します。問題が発生した最初の呼び出しをトレースできます。メソッドに渡された引数も表示されます。

NOTE 実際にはsymfonyはエラーのレポートに関してPHPの例外に依存しています。PHP 4のアプリケーションの機能よりもはるかに優れています。たとえば404エラーはsfError404Exceptionによって起動します。

図16-1 - symfonyのアプリケーションのためのサンプルの例外メッセージ

symfonyのアプリケーションのためのサンプルの例外メッセージ

開発フェーズの期間において、symfonyの例外はアプリケーションのデバッグに関してとても役に立ちます。

Xdebugの拡張機能

PHPのXdebug拡張機能(http://xdebug.org/)によってWebサーバーに記録される情報量を広げることができるようになります。symfonyはXdebugのメッセージを独自のデバッグのフィードバック情報に統合するので、アプリケーションをデバッグするときにこの拡張機能を有効することはよい考えです。拡張機能のインストール方法はプラットフォームに大いに依存します; 詳細なインストールガイドについてはXdebugのサイトを参照してください。Xdebugをインストールしたあとで、php.iniのなかで手動で有効にする必要があります。*inxに関しては、つぎの行を追加します:

zend_extension="/usr/local/lib/php/extensions/no-debug-non-zts-20041030/xdebug.so"

Windowsプラットフォームに関しては、Xdebugを有効にするにはつぎの行を追加します:

extension=php_xdebug.dll

リスト16-9はphp.iniファイルに追加しなければならないXdebugの設定の例を示しています。

リスト16-9 - サンプルのXdebugの設定

;xdebug.profiler_enable=1
;xdebug.profiler_output_dir="/tmp/xdebug"
xdebug.auto_trace=1             ; enable tracing
xdebug.trace_format=0
;xdebug.show_mem_delta=0        ; memory difference
;xdebug.show_local_vars=1
;xdebug.max_nesting_level=100

Xdebugのモードを有効にするにはWebサーバーを再起動しなければなりません。

CAUTION 運用環境ではXdebugモードを無効にすることを忘れないでください。さもなければすべてのページの実行速度がとても遅くなります。

Webデバッグツールバー

ログファイルは興味深い情報を含みますが、あまり読みやすいものではありません。もっとも基本的なタスクは、特定のリクエストに対して記録された行を見つけることですが、アプリケーションとイベントの長い履歴のイベントを同時に利用する複数のユーザーを抱えている場合はとても扱いにくいです。このようなときにWebデバッグツールバーを使い始めます。

このツールバーは図16-2で示されるように、ウィンドウの右上のコーナーにある、標準内容の上に重ねて設置された半透明のボックスとして現れます。これによってsymfonyのログイベント、現在の設定、リクエストオブジェクトとレスポンスオブジェクトのプロパティ、リクエストによって発行されたデータベースクエリの詳細情報にアクセスできます。

図16-2 - ウィンドウ右上のコーナーに存在するWebデバッグツールバー

ウィンドウ右上のコーナーに存在するWebデバッグツールバー

デバッグツールバーの背景の色はリクエストの間に発行されたログメッセージのもっとも高いレベルに依存します。メッセージがdebugレベルを渡さない場合、ツールバーの背景は灰色になります。単独のメッセージがerrレベルに達した場合、ツールバーの背景は赤色になります。

NOTE デバッグモードとWebデバッグツールバーを混同しないでください。デバッグツールバーはデバッグモードがオフでも表示できますが、その場合、表示される情報は少なくなります。

アプリケーションに対してWebデバッグツールバーを有効にするには、settings.ymlをテキストエディタで開きweb_debugキーを探します。運用環境とテスト環境において、web_debugのデフォルト値はoffなので、使いたい場合は手動で有効にする必要があります。リスト16-10で示されるように、dev環境のデフォルト値はonに設定されています。

リスト16-10 - frontend/config/settings.ymlのなかで、Webデバッグツールバーを有効にする

dev:
  .settings:
    web_debug:              on

表示されるとき、Webデバッグツールバーは多くの情報/インタラクションを提供します:

図16-3 - vars & configセクションはリクエストのすべての変数と定数を示す

vars & configセクションはリクエストのすべての変数と定数を示す

図16-4 - logs & msgsセクションは現在のリクエストのためのログメッセージを表示する

logs & msgsセクションは現在のリクエストのためのログメッセージを表示する

NOTE 現在のアクションがリダイレクトの結果から由来するとき、最新のリクエストのログだけがlogs & msgsのペインに現れます。ですので、ログファイルはよいデバッグのために今もなお不可欠です。

図16-5 - データベースクエリのセクションは現在のリクエストに対して実行されたクエリを表示する

データベースクエリのセクションは現在のリクエストに対して実行されたクエリを表示する

図16-6 - 時計のアイコンはカテゴリごとの実行時間を表示する

クロックアイコンはカテゴリごとの実行時間を表示する

SIDEBAR 独自のタイマーを追加する

symfonyは設定、モデル、アクションとビューに費やされた時間を計算するsfTimerクラスを使います。同じオブジェクトを使うので、Webデバッグツールバー内で、カスタムプロセスを計測しほかのタイマーによって結果を表示できます。これはパフォーマンスの最適化にとり組むときにとても便利です。

特定のコードのフラグメントの時間測定を初期化するには、getTimer()メソッドを呼び出します。これはsfTimerオブジェクトを返し時間測定を始めます。時間測定を停止させるにはこのオブジェクト上でaddTime()メソッドを呼び出します。経過時間はgetElapsedTime()メソッドを通して利用可能であり、他方ではWebデバッグツールバーのなかに表示されます。

[php]
// タイマーを初期化して計測を始める
$timer = sfTimerManager::getTimer('myTimer');

// 何かを行う
...

// タイマーを停止させて経過時間を追加する
$timer->addTime();

// 結果を取得する(まだタイマーが停止していない場合はここで停止させる)
$elapsedTime = $timer->getElapsedTime();

それぞれのタイマーに名前をつける利点は時間を測定するために何度も呼び出すことができることです。たとえば、myTimerタイマーはユーティリティメソッドで使われます。このメソッドはリクエストごとに2回呼び出され、addTime()が最後に呼び出されたときに、getTimer('myTimer')メソッドへの2回目の呼び出しは計算された時点から時間測定を再起動させるので、時間測定は以前のものに追加されます。タイマーオブジェクト上でgetCalls()を呼び出すことでタイマーが起動した回数を得られこのデータはWebデバッグツールバーにも表示されます。

[php]
// タイマーへの呼び出しの回数を得る
$nbCalls = $timer->getCalls();

Xdebugモードにおいて、ログのメッセージはより豊富です。すべてのPHPスクリプトファイルと呼び出される機能はログに記録されるので、symfonyは内部ログでこの情報を記録する方法を知っています。ログメッセージテーブルのそれぞれの行は二重の矢印ボタンを持ち、そのボタンをクリックすると関連するリクエストに関する詳細な情報を見ることができます。何か間違っている場合、Xdebugモードは原因を見つけるための最大限の情報を提供します。

-

NOTE デフォルトではWebデバッグツールバーはAjaxのレスポンスとHTMLではないcontent-typeを持つドキュメントに含まれません。そのほかのページでは、sfConfig::set('sf_web_debug', false)を呼び出すだけでアクションの範囲内から手動でWebデバッグツールバーを無効にできます。

手動でデバッグする

symfonyのデバッグツールにアクセスできることはすばらしいことですが、独自のメッセージをログに記録できればより便利です。リクエストを実行している間にイベントかつ/もしくは値をトレースする作業を助けするために、symfonyはアクションとテンプレートの両方からアクセスできるショートカットを提供します。

カスタムログメッセージは、通常のイベントのようにWebデバッグツールバーと同様に、symfonyのログファイルに現れます(リスト16-5はカスタムログメッセージ構文の例を示しています)。カスタムメッセージは、たとえばテンプレートからの変数の値をチェックするためのよい方法です。リスト16-11はテンプレートからの開発者のフィードバックのためのWebデバッグツールバーを使う方法を示しています(アクションからも$this->logMessage()を利用できます)。

リスト16-11 - デバッグの目的のためにメッセージをログに挿入する

[php]
<?php use_helper('Debug') ?>
...
<?php if ($problem): ?>
  <?php log_message('{sfAction} been there', 'err') ?>
  ...
<?php endif ?>

図16-7で示されるように、errレベルを使うことでイベントがメッセージのリストのなかでわかりやすく見えることが保証されます。

図16-7 - カスタムログメッセージはWebデバッグツールバーのlogs & msgsセクションに現れる

カスタムログメッセージはWebデバッグツールバーのlogs & msgsセクションに現れる

ショットメッセージや値をトレースしたいが行をログに追加したくない場合、log_messageの代わりにdebug_messageを使います。このアクションメソッド(同じ名前のヘルパーも存在する)はlogs & msgsセッションのトップで、メッセージをWebデバッグツールバーに表示します、デバッグメッセージを書き込む方法に関してはリスト16-12を確認してください。

リスト16-12 - メッセージをデバッグツールバーに挿入する

[php]
// アクションから
$this->debugMessage($message);

// テンプレートから
<?php use_helper('Debug') ?>
<?php debug_message($message) ?>

Webの内容の外側からsymfonyを使う

たとえば、Eメールのジョブのバッチを起動させるもしくはプロセスが集中される計算を通してモデルを定期的に更新するために、symfonyのすべてのクラスと機能にアクセスすることでコマンドライン(もしくはcronテーブルを通して)からスクリプトを実行したいことがあります。これを行うためのシンプルな方法は、symfonyが適切に初期化されるようにするために、フロントコントローラーの最初のステップを再現するPHPスクリプトを作ることです。引数の解析と自動化されたデータベースの初期化を利用するために、symfonyのコマンドラインシステムも利用できます。

バッチファイル

symfonyを初期化するには数行のPHPコードが必要なだけです。PHPファイルを作ればすべてのsymfonyの機能を利用できます。たとえばプロジェクトのlib/ディレクトリのもとで、リスト16-13のようなコードで始めます。

リスト16-13 - lib/myScript.phpのなかの、サンプルのバッチスクリプト

[php]
<?php

require_once(dirname(__FILE__).'/../config/ProjectConfiguration.class.php');
$configuration = ProjectConfiguration::getApplicationConfiguration('frontend', 'dev', true);
sfContext::createInstance($configuration);

// データベースレイヤーを使わないのであればつぎの行は削除する
$databaseManager = new sfDatabaseManager($configuration);
$databaseManager->loadConfiguration();

// ここにコードを追加する

これはフロントコントローラーの最初の行ととてもよく似ています(6章を参照)。これらの行は同じことをするからです: symfonyを初期化し、プロジェクトとアプリケーションの設定を解析します。 ProjectConfiguration::getApplicationConfigurationメソッドはつぎの3つのパラメーターを必要とすることに注意してください:

コードを実行するには、コマンドラインからスクリプトを呼び出します:

> php lib/myScript.php

カスタムタスク(symfony 1.1の新しい機能)

symfonyを利用してカスタムコマンドライン機能を作成する別の方法はsymfonyのタスクを書くことです。cache:clearタスクとpropel:build-modelタスクのように、php symfonyでコマンドラインから独自のカスタムタスクを実行できます。カスタムタスクはコマンドラインの引数とオプションを解析する機能は恩恵を受け、独自のヘルプのテキストを埋め込み、既存のタスクを拡張できます。

カスタムタスクはsfBaseTaskを拡張する単なるクラスでlib/task/ディレクトリ、プロジェクトのroot、もしくはpluginディレクトリに設置されます。このファイル名は'Task.class.php'で終わらなければなりません。リスト16-13はサンプルのカスタムタスクを示します。

リスト16-13 - lib/task/testHelloTask.class.phpのなかの、サンプルのタスク

[php]

class testHelloTask extends sfBaseTask
{
  protected function configure()
  {
    $this->namespace = 'test';
    $this->name = 'hello';
    $this->briefDescription = 'Says hello';
  }

  protected function execute()
  {
    // ここにコード
    $this->log('Hello, world!');
  }
}

executeメソッドで書かれたコードは以前のバッチスクリプトのようにsymfonyのライブラリにアクセスできます。違いはカスタムタスクを呼び出す方法です:

> php symfony test:hello

タスクの名前は(クラス名もしくはファイル名からではなく)保護(protected)されたnamespacenameプロパティから由来します。タスクはsymfonyのコマンドラインに統合されるので、つぎのコマンドを入力するだけでこのタスクは一覧に表示されます:

> php symfony

タスクのスケルトンをあなた自身で書くよりも、symfonyのgenerate:taskタスクを利用できます。このタスクは空のタスクを作り、豊富なカスタマイゼーションのオプションを持ちます。つぎのコマンドを呼び出すことでこれらを確認してください:

> php symfony help generate:task

タスクは引数(必須のパラメーターであらかじめ決められた順番で)とオプション(オプションで順不同のパラメーター)を受けとります。リスト16-15はこれらすべての機能を利用するより複雑なタスクを示しています。

リスト16-15 - lib/task/mySecondTask.class.phpのなかで、より完全なサンプルのタスク

[php]
class mySecondTask extends sfBaseTask
{
  protected function configure()
  {
    $this->namespace        = 'foo';
    $this->name             = 'mySecondTask';
    $this->briefDescription = 'Does some neat things, with style';
    $this->detailedDescription = <<<EOF
The [foo:mySecondTask|INFO]タスクはあなたの代わりに作業を実現するプロセスを管理します。
つぎの構文で呼び出します:

  [php symfony foo:mySecondTask frontend|INFO]

[verbose|COMMENT]オプションを使うことで詳細な出力を有効にできます:

  [php symfony foo:mySecondTask frontend --verbose=on|INFO]
EOF;
    $this->addArgument('application', sfCommandArgument::REQUIRED, 'The application name');
    $this->addOption('verbose', null, sfCommandOption::PARAMETER_REQUIRED, 'Enables verbose output', false);
  }

  protected function execute($arguments = array(), $options = array())
  {
    // ここにコードを追加する

  }
}

NOTE タスクがデータベースレイヤーにアクセスする必要がある場合、タスクはsfBaseTaskの代わりにsfPropelBaseTaskを拡張します。タスクの初期化は追加のPropelクラスのロードを考慮します。つぎのようにexecute()メソッドでデータベースに接続することを始めることができます:

$databaseManager = new sfDatabaseManager($this->configuration);

タスクの設定がapplicationenv引数を定義する場合、タスクの設定をビルドするときに、これらは自動的に考慮されるので、タスクはdatabases.ymlのなかで定義されたデータベースの接続を利用できます。デフォルトでは、generate:taskへの呼び出しによって生成されたスケルトンはこの初期化を含みます。

タスクシステムの機能に関する詳細な例に関しては、既存のsymfonyのタスクのソースを確認してください。

既存のタスクと同じ名前のタスクを宣言すると、既存のタスクがオーバーライドされます。このことは、プラグインがsymfonyのタスクをオーバーライドできるので、プロジェクトはプラグインのタスクとsymfonyのタスクをオーバーライドできることを意味します。既存のタスククラスを拡張する機能を追加すれば、とても拡張性のあるコマンドラインシステムを得られます!

データベースを投入する

アプリケーションの開発過程において、しばし開発者はデータベース投入の問題に直面します。いくつかのデータベースのための固有の解決方法がわずかにありますが、オブジェクトリレーショナルマッピングを上回るものはありません。YAMLとsfPropelDataオブジェクトのおかげで、データのためにテキストファイルを書くことはCRUDインターフェイスを利用して手作業でレコードを入力するよりも作業が多いように見えますが、長期的には時間を節約できます。この機能がアプリケーションに対してテストデータを保存し投入するために便利であることがわかります。

フィクスチャファイルの構文

データファイルがdata/fixtures/ディレクトリに設置されているという前提のもとで、symfonyはシンプルなYAML構文に従うデータファイルを読み込むことができます。フィクスチャファイルはクラスによって整理され、それぞれのクラスはクラス名によってヘッダーとして導入されます。それぞれのクラスに対して、ユニークな文字列によってラベルづけされたレコードはfieldname: valueのペアのセットによって定義されます。リスト16-16はデータベース投入のためのデータファイルの例を示しています。

リスト16-16 - data/fixtures/import_data.ymlのなかの、サンプルのフィクスチャファイル

Article:                             ## レコードをblog_articleテーブルに挿入する
  first_post:                        ## 最初のレコードラベル
    title:       最初の記憶
    content: |
      長い間私は早く寝る習慣がありました。時々、ろうそくを消したとき、
      "寝ようとしています"と言わないようにすぐに目を閉じます。


  second_post:                       ## 2番目のレコードラベル
    title:       事態が悪化
    content: |
      時々彼は彼女が苦痛なしで、何らかの自己で死んで欲しいと望みました。
      彼女は朝から晩まで人でごった返す街の大通りにいました。

symfonyはcamelCaseのコンバータ(setTitle()setContent())を利用してカラムキーをセッターメソッドに翻訳します。このことはテーブルがpasswordフィールドを持たない場合でもpasswordキーを定義できることを意味します。Userオブジェクトを用いてsetPassword()メソッドを定義するだけで、パスワードに基づいたほかのカラムを投入できます(たとえば、ハッシュバージョンのパスワード)。

主キーを定義する必要はありません。オートインクリメントのフィールドなので、データベースレイヤーが決定する方法を知っているからです。

created_atカラムを設定する必要はありません。その方法で名づけられたフィールドが作られたときに現在のシステム時間に設定しなければならないことをsymfonyが知っているからです。

インポートを起動する

propel:load-dataタスクはYAMLファイルからデータベースにデータをインポートします。コンフィギュレーション設定はdatabases.ymlファイルから由来するので、実行するにはアプリケーションの名前が必要です。--envオプションを追加することで、オプションとして、環境名を指定できます(デフォルトではdev)。

> php symfony propel:data-load --env=prod frontend

このコマンドはdata/fixtures/ディレクトリからすべてのYAMLフィクスチャを読み込むことが可能で、レコードをデータベースに挿入できます。デフォルトでは、このコマンドは既存のデータベースの内容を置き換えますが、--appendを追加する場合、コマンドは現在のデータを削除しません。

> php symfony propel:data-load --append frontend

呼び出しのなかで別のフィクスチャのディレクトリを指定できます。この場合、プロジェクトのディレクトリへの相対パスを追加します。

> php symfony propel:data-load frontend --dir[]=data/myfixtures

リンクされたテーブルを使う

レコードを単独のテーブルに追加する方法を理解しましたが、外部キーを持つレコードを別のテーブルに追加するにはどうしたらよいでしょうか?主キーはフィクスチャデータに含まれないので、レコードを別のレコードに関連づけるには代わりの方法が必要です。

図16-8に示されるように、blog_articleテーブルがblog_commentテーブルにリンクされる8章の例に戻ってみましょう。

図16-8 - サンプルのデータベースのリレーショナルモデル

サンプルのデータベースリレーショナルモデル

ここでレコードに付与されたラベルが本当に役立ちます。Commentフィールドをfirst_postの記事に追加するには、リスト16-17で示される行をimport_data.ymlデータファイルに追加することだけが必要です。

リスト16-17 - data/fixtures/import\data.ymlのなかで、レコードを関連するテーブルに追加する

Comment:
  first_comment:
    article_id:   first_post
    author:       匿名
    content:      あなたの散文は冗長です。より短い文を書きましょう。

propel:load-dataタスクは以前import_data.yml内で記事に付与したラベルを認識します。そしてarticle_idフィールドを設定するために対応するArticleレコードの外部キーを取得します。レコードのIDを見る必要はありません; これらのラベルでIDをリンクするだけです。この作業はよりシンプルになることはありません。

リンクされたレコードに対する唯一の制限は外部キーに呼び出されたオブジェクトをファイルのなかであらかじめ定義しなければならないことです; つまり、これらを1つずつ定義したように行います。データファイルは上から下へ解析されるので、レコードが書かれた順番が重要です。

symfony 1.1の新しい機能: これは第三のクラスを通して2つのクラスが関連づけされた、多対多のリレーションに対しても機能します。たとえば、1つのArticleは多くのAuthorsを持ち、1つのAuthorは多くのArticlesを持ちます。通常はこのためにArticleAuthorクラスを使うことができます。このクラスはarticle_idカラムとauthor_idカラムを持つarticle_authorテーブルに対応します。リスト16-18はこのモデルで多対多のリレーションを定義するためにフィクスチャファイルを書く方法を示しています。ここでは複数形のテーブル名が使われていることに注目してください。これは真ん中のクラスのために検索を起動させるものです。

リスト16-18 - data/fixtures/import_data.ymlのなかで、レコードを多対多のリレーションで関連づけされたテーブルに追加する

Author:
  first_author:
    name: John Doe
    article_authors: [first_post, second_post]

1つのデータファイルはいくつかのクラスの宣言を含みます。しかし、多くの異なるテーブルに対して多くのデータを挿入する場合、フィクスチャファイルが長すぎて簡単には操作できないことがあります。

propel:data-loadタスクはfixtures/ディレクトリのなかで見つかるすべてのファイルを解析するので、YAMLフィクスチャファイルをより小さなピースに分割することを妨げるものは何もありません。覚えておくべき大事なことは外部キーがテーブルに対して処理の順番を強制することです。これらが正しい順番で解析されたことを確認するには、ファイルのプレフィックスを序数にします。

100_article_import_data.yml
200_comment_import_data.yml
300_rating_import_data.yml

アプリケーションをデプロイする

symfonyは2つのバージョンのWebサイトを同期化する省略記法のコマンドを提供します。多くの場合、これらのコマンドは開発サーバーからインターネットに接続されている最終的なホストにWebサイトをデプロイするために使われます。

FTPで転送するためにプロジェクトを凍結する

プロジェクトを運用サーバーにデプロイ(deploy - 配置)するもっとも共通な方法はFTP(もしくはSFTP)を用いてすべてのファイルを転送することです。しかしながら、symfonyのプロジェクトはsymfonyのライブラリを利用するので、サンドボックス(非推奨)で開発しないかぎり、もしくはsymfonyのlib/data/ディレクトリがsvn:externalsによってリンクされる場合、これらのライブラリはプロジェクトのディレクトリに存在しません。PEARでインストールするもしくはシンボリックリンクを利用するであれ、運用サーバーで同じファイル構造を再現することは時間がかかり大変です。

symfonyがプロジェクトを"freeze"(凍結)するユーティリティを提供するのはそういうわけです。必要なすべてのsymfonyのライブラリをプロジェクトのdata/ディレクトリとweb/ディレクトリにコピーするために使われます。プロジェクトは一種のサンドボックス、もしくは独立したスタンドアロンのアプリケーションになります。

> php symfony project:freeze symfony_data_dir

引数であるsymfony_data_dirはsymfonyのdataディレクトリへのパスです(Subversionもしくはアーカイブを通してsymfonyをインストールした場合、このディレクトリはsymfonyのlibと同じです; PEARパッケージのsymfonyをインストールした場合、このディレクトリはPEARのdataディレクトリに設置されます)。

プロジェクトがいったん凍結されたら、プロジェクトのディレクトリを運用サーバーに転送することが可能で、PEAR、シンボリックリンクなどがなくても動作します。

TIP さまざまな凍結されたプロジェクトは異なるsymfonyのバージョンで同じサーバーで問題なく動作します。

プロジェクトを初期の状態にリバートするには、project:unfreezeタスクを使います。これはdata/symfony/lib/symfony/web/sf/ディレクトリを削除します。

> php symfony project:unfreeze

freezeするまえにインストールしたsymfonyのディレクトリへのシンボリックリンクが存在する場合、symfonyはこれらのシンボリックリンクを記憶し元の場所で再現することを覚えておいてください。

インクリメンタルなファイル転送のためにrsyncを使う

FTPでプロジェクトディレクトリをまるごと転送する方法は最初のうちはよいですが、更新したアプリケーションをアップロードするときにわずかなファイルしか変更されていないのであればFTPは理想的な方法ではありません。プロジェクト全体を再び転送する必要があるので時間と帯域の無駄遣いです。もしくは同じファイルが変更されたことを知っているディレクトリを見て、異なった修正日付を持つものだけを転送する方法がありますが、これも時間がかかる作業でエラーになりがちで、さらに、転送している間はWebサイトが利用できないもしくはバグだらけになります。

symfonyによってサポートされる解決方法はSSHレイヤーを通したrsyncによる同期化です。Rsync(http://samba.anu.edu.au/rsync/)は速いインクリメンタルなファイル転送を提供するコマンドラインユーティリティでオープンソースです。変更されなかったファイルはホストに転送されません。インクリメンタルな転送によって、修正されたデータだけが送られます。ファイルの一部だけが変更された場合は、差分が送られます。主な利点はrsyncの同期化は小さな量のデータのみ転送するのでとても速いことです。

symfonyはデータ転送を安全にするためにrsyncの上にSSHを追加します。より多くの商業用ホストが自身のサーバーの上でファイルのアップロードを安全にするためにSSHトンネルをサポートしていますが、これはセキュリティの欠陥を回避するためのよい慣習です。

symfonyによって呼び出されたSSHクライアントはconfig/properties.iniファイルから接続設定を使います。リスト16-19は運用サーバーのための接続設定の例を示しています。同期化をするまえに独自の運用サーバーの設定をこのファイルに書きます。独自のrsyncコマンドラインパラメーターを提供する単独のパラメーター設定を定義することもできます。

リスト16-19 - myproject/config/properties.iniのなかの、サーバーの同期化のためのサンプルの接続設定

[symfony]
  name=myproject

[production]
  host=myapp.example.com
  port=22
  user=myuser
  dir=/home/myaccount/myproject/

NOTE 運用サーバー(プロジェクトのproperties.iniファイルで定義されるホストサーバー)と運用環境(運用サーバーで使われるフロントコントローラーと設定で、アプリケーションの設定ファイルに参照される)を混同しないでください。

SSHを通してrsyncを行うにはいくつかのコマンドツールが必要で、アプリケーションの生涯において同期化の作業は多くの時間を占めます。幸いにして、symfonyはたった1つのコマンドだけでこのプロセスを自動化します:

> php symfony project:deploy production

このコマンドはdryモードでrsyncコマンドを立ち上げます; すなわち、同期化しなければならないファイルを表示しますが、実際には同期化しません。同期化を行いたい場合、--goオプションを追加して明確にリクエストする必要があります。

> php symfony project:deploy production --go

同期化のあとで運用サーバーでキャッシュをクリアすることを忘れないでください。

TIP symfony 1.1に関しては、the php.yml設定ファイルは除去されました。

手動でチェックしなければならない唯一の設定はlog_errorsで、php.ymlによってonに設定されました。

php.ymlはcheck_configuration.phpユーティリティに置き換えられdata/binフォルダーで見つかります。これはsymfonyの要件に対するあなたの環境をチェックします。どこからでもこのスクリプトを立ち上げることができます:

[php]
$ php /path/to/symfony/data/bin/check_configuration.php

このユーティリティをコマンドラインから利用できるとしても、PHPはコマンドラインインターフェイスとWebのために異なるphp.ini設定ファイルを利用できるのでこのユーティリティをwebのrootにコピーしてwebから起動させることを強くお勧めします。

-

SIDEBAR アプリケーションの公開準備は終わりましたか?

アプリケーションを運用サーバーにデプロイするまえに、公開の準備ができていることを確認します。アプリケーションを実際にデプロイすることを決心するまえにつぎの項目が行われたことを確認してください:

エラーページはアプリケーションの外見に合わせてカスタマイズできます。500エラーのページ、404エラーのページとセキュリティのページをカスタマイズする方法は19章を参照してください。サイトが利用できないときに表示されるページをカスタマイズする方法に関してはこの章の"運用のアプリケーションを運用する"のセクションを参照してください。

defaultモジュールはsettings.ymlenabled_modules配列から除外されるのでsymfonyのページが誤って表示されることはありません。

セッションハンドリングのメカニズムはクライアントサイドのCookieを利用し、このCookieのデフォルトの名前はsymfonyです。アプリケーションをデプロイするまえに、アプリケーションがsymfonyを利用している事実を明言しないようにするためにおそらくこのデフォルトの名前をリネームすることになります。factories.ymlファイルでCookieの名前をカスタマイズする方法は6章を参照してください。

robots.txtファイルはデフォルトでは空でプロジェクトのweb/ディレクトリに設置されます。Webサイトの閲覧できる部分と避けたほうがよい部分の情報をWebスパイダーとほかのWebロボットに知らせるためにカスタマイズします。たいていの場合、このファイルは特定のURLの空間にインデックスとして記録されないようにするために使われます。たとえば、リソースが集中しているページ、インデックスに必要のないページ(バグのアーカイブなど)、もしくはロボットが捕まる無限ループのURL空間などです。

ユーザーが最初にブラウザーでアプリケーションを閲覧するとき、モダンブラウザーは、アドレスバーとブックマークフォルダーのなかのアイコンでアプリケーションを表すためにfavicon.icoファイルをリクエストします。このようなファイルを提供することでアプリケーションの外見が完全なものになり、多くの404エラーがサーバーのログに表示されないようになります。

不適切なファイルを無視する

運用ホストでsymfonyのプロジェクトを同期化する場合、いくつかのファイルとディレクトリを転送すべきではありません:

rsyncの同期化からファイルを除外するには、myproject/config/ディレクトリの下にあるrsync_exclude.txtファイルをテキストエディタで開き編集します。それぞれの行はファイル、ディレクトリ、パターンを含みます。symfonyのファイル構造は論理的に整理され、同期化から手動で除外するファイルもしくはディレクトリの数を最小化するために設計されています。リスト16-20で例をご覧ください。

リスト16-20 - myproject/config/rsync_exclude.txtのなかの、サンプルのrsyncの除外設定

.svn
/cache/*
/log/*
/stats/*
/web/uploads/*
/web/frontend_dev.php

NOTE 開発環境でcache/ディレクトリとlog/ディレクトリを同期化してはなりませんが、これらのディレクトリは少なくとも運用サーバーでは存在します。myproject/プロジェクトのツリー構造内部でこれらのディレクトリが存在しない場合、手動でこれらのディレクトリを作ってください。

運用のアプリケーションを運用する

運用サーバーでもっとも共通で使われるコマンドはclear:cacheです。(たとえばproject:deployタスクを呼び出すなど)symfonyもしくはプロジェクトをアップグレードする、または運用環境で設定を変更するたびにこのコマンドを実行しなければなりません。

> php symfony cache:clear

TIP コマンドラインインターフェイスが運用サーバーで利用できない場合、cache/フォルダーの内容を手動で削除することでキャッシュをクリアできます。

一時的にアプリケーションを無効にできます。たとえば、ライブラリや大量のデータのアップグレードが必要なときです。

> php symfony project:disable APPLICATION_NAME ENVIRONMENT_NAME

デフォルトでは、無効にされたアプリケーションは $sf_symfony_lib_dir/exception/data/unavailable.phpページを表示しますが、プロジェクトのconfig/ディレクトリのなかで独自のunavailable.phpファイルを作る場合、symfonyはそれを代わりに使います。

project:enableタスクはアプリケーションを再び有効にしてキャッシュをクリアします。

> php symfony project:enable APPLICATION_NAME ENVIRONMENT_NAME

SIDEBAR キャッシュをクリアするときに利用できないページを表示する

settings.ymlファイルのなかのcheck_lockパラメーターをonに設定する場合、キャッシュがクリアされている最中にsymfonyはアプリケーションをロックし、キャッシュが最後にクリアされるまえに到達したすべてのリクエストはアプリケーションが一時的に利用できないことを伝えるページにリダイレクトされます。キャッシュが巨大な場合、これをクリアするための遅延は数ミリ秒よりも長くなることがあり、サイトのトラフィックが高い場合、これはお勧めの設定です。この利用できないことをお知らせするページはsymfonyのdisableタスクを呼び出すときに表示されるものと同じです。check_lockパラメーターはデフォルトで無効です。わずかですがパフォーマンスにネガティブな影響があるからです。

project:clear-controllersタスクは運用環境で動作しているもの以外のweb/ディレクトリのすべてのコントローラーをクリアします。 開発環境のフロントコントローラーをrsync_exclude.txtファイルに含めないのであれば、これはバックドアがアプリケーションの内部を暴露しないことを保証します。

> php symfony project:clear-controllers

プロジェクトのファイルとディレクトリのパーミッションはSVNリポジトリからチェックアウトする場合壊れる可能性があります。たとえば、log/ディレクトリとcache/ディレクトリのパーミッションを0777に変更したいのであれば(これらのディレクトリは正しく動作させるためにフレームワークが書き込みできるようにする必要があります)、project:permissionsタスクはディレクトリのパーミッションを修正します。

> php symfony project:permissions

まとめ

PHPのログとsymfonyを組み合わせることで、アプリケーションのモニタリングとデバッグを簡単に行うことができます。開発期間において、デバッグモード、例外、Webデバッグツールバーは問題を突き止めるための助けになります。よりデバッグを簡単にするためにカスタムメッセージをログファイルもしくはツールバーに挿入できます。

開発フェーズと運用フェーズにおいて、コマンドラインインターフェイスはあなたのアプリケーションの運用を円滑にする膨大な数のツールを提供します。これらのツールのなかで、データの投入、凍結、同期化のタスクは多くの時間を節約します。