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 - symfony ログファイルの内容のサンプル (log/frontend_dev.log)

Nov 15 16:30:25 symfony [info ] {sfAction} call "barActions->executemessages()"
Nov 15 16:30:25 symfony [info ] {sfPropelLogger} 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 にはアプリケーションの開発とデバッグを円滑にするデバッグモードがあります。このモードがオンのとき、つぎのことが起こります:

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

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

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

[php]
<?php

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

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

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

symfony の例外

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

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

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

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

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

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

Xdebug エクステンション

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

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 - Web デバッグツールバーを有効にする (frontend/config/settings.yml)

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/ ディレクトリ、プロジェクトのルート、もしくは 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 の既存のタスクのソースを確認してください。

lib/taskフォルダーで既存のタスクと同じ名前のクラスを作成すると、 そのクラスは既存のタスクをオーバーライドします。 プラグインは 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 はラクダ記法のコンバータ (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 の新しい機能: これは第3のクラスを通して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と同じです; symfony の PEAR パッケージをインストールした場合、このディレクトリは 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

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

> php symfony project:deploy production --go

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

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

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

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

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

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

-

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

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

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

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

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

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

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

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

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

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

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

.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 デバッグツールバーは問題をつきとめるための助けになります。よりデバッグ作業を楽にするにはカスタムメッセージをログファイルもしくはツールバーに挿入します。

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