symfony book 日本語ドキュメント

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

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

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

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

ロギング

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

PHPのログ

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

リスト16-1 - エラーレポートのレベルを設定する(myapp/config/settings.yml)

prod:
 .settings:
    error_reporting:  257

dev:
  .settings:
    error_reporting:  4095

数字はエラーレベルを手短に表現する方法です(エラーレポートの詳細についてはPHPドキュメントを参照)。基本的に、4095E_ALL | E_STRICTのショットカットで、257E_ERROR | E_USER_ERRORを表します(すべての新しい環境のためのデフォルト値)。

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

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

symfonyのログ

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

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

リスト16-2 - symfonyのログファイルの内容のサンプル(log_myapp_dev.php)

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のログレベルの設定

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

リスト16-3 - デフォルトのロギング設定(myapp/config/logging.yml)

prod:
  enabled: off
  level:   err
  rotate:  on
  purge:   off

dev:

test:

#all:
#  enabled:  on
#  level:    debug
#  rotate:   off
#  period:   7
#  history:  10
#  purge:    on

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

ログに記録されるメッセージのタイプを制限するためにlogging.ymlファイルのなかでそれぞれの環境に対してロギングレベルを変更できます。rotateperiodhistorypurge設定はつぎの"ログファイルをパージしてローテーションを決める"のセクションで説明します。

TIP ロギングパラメーターの値はsf_logging_prefixのプレフィックスを持つsfConfigオブジェクトを通して実行期間にアクセス可能です。たとえば、ロギングが有効であることを確認するには、sfConfig::get('sf_ logging_enabled')を呼び出します。

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

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

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

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

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

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

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

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

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

SIDEBAR ロギングをカスタマイズする

symfonyのロギングシステムはとてもシンプルですが、カスタマイズも簡単です。sfLogger::getInstance()->registerLogger()を呼び出すことで独自のロギングオブジェクトを指定できます。たとえば、PEAR::Logを使いたい場合、つぎのコードをアプリケーションのconfig.phpに追加するだけですみます:

[php]
require_once('Log.php');
$log = Log::singleton('error_log', PEAR_LOG_TYPE_SYSTEM, 'symfony');
sfLogger::getInstance()->registerLogger($log);

独自のロガークラスを追加したい場合、唯一の必須条件はlog()メソッドを定義しなければならないことです。symfonyは2つのパラメーター、$message(ログに記録されるメッセージ)と$priority(レベル)でこのメソッドを呼び出します。

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

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

> symfony log-purge

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

リスト16-6 - ログのローテーションを設定する(myapp/config/logging.yml)

prod:
  rotate:  on
  period:  7       ## デフォルトでログファイルは7日ごとにローテーションを行う
  history: 10      ## 10のログファイルの最大履歴が保存される

ログのローテーションを実施するには、定期的にlog-rotateタスクを実行します。このタスクはrotateonになっているファイルを消去します。タスクを呼び出すときに1つのアプリケーションと環境の名前を指定できます:

> symfony log-rotate myapp prod

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

デバッグする

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

symfonyのデバッグモード

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

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

デバッグモードはフロントコントローラー内でアプリケーションごとに有効になります。リスト16-7で示されるように、デバッグモードはSF_DEBUG定数の値でコントロールします。

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

[php]
<?php

define('SF_ROOT_DIR',    realpath(dirname(__FILE__).'/..'));
define('SF_APP',         'myapp');
define('SF_ENVIRONMENT', 'dev');
define('SF_DEBUG',       true);

require_once(SF_ROOT_DIR.DIRECTORY_SEPARATOR.'apps'.DIRECTORY_SEPARATOR.SF_APP.DIRECTORY_SEPARATOR.'config'.DIRECTORY_SEPARATOR.'config.php');

sfContext::getInstance()->getController()->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-8はphp.iniファイルに追加しなければならないXdebugの設定の例を示しています。

リスト16-8 - 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-9で示されるように、dev環境のデフォルト値はonに設定されています。

リスト16-9 - Webデバッグツールバーを有効にする(myapp/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-4はカスタムログメッセージ構文の例を示しています)。カスタムメッセージは、たとえばテンプレートからの変数の値をチェックするためのよい方法です。リスト16-10はテンプレートからの開発者のフィードバックのためのWebデバッグツールバーを使う方法を示しています(アクションからも$this->logMessage()を利用できます)。

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

[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-11を確認してください。

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

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

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

データベースを投入する

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

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

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

リスト16-12 - フィクスチャファイルのサンプル(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ファイルから由来するので、実行するにはアプリケーションの名前が必要です。オプションとして、環境名を指定できます(デフォルトではdev)。

> symfony propel-load-data frontend

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

> symfony propel-load-data frontend append

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

> symfony propel-load-data frontend data/myfixtures/myfile.yml

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

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

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

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

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

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

リスト16-13 - 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つずつ定義したように行います。データファイルは完全に解析されるので、レコードが書かれた順番が重要です。

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

propel-load-dataタスクは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/ディレクトリにコピーするために使われます。プロジェクトは一種のサンドボックス、もしくは独立したスタンドアロンのアプリケーションになります。

> symfony freeze

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

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

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

> symfony 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-14は運用サーバーのための接続設定の例を示しています。同期化をするまえに独自の運用サーバーの設定をこのファイルに書きます。独自のrsyncコマンドラインパラメーターを提供する単独のパラメーター設定を定義することもできます。

リスト16-14 - サーバーの同期化のための接続設定のサンプル(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つのコマンドだけでこのプロセスを自動化します:

> symfony sync production

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

> symfony sync production go

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

TIP 開発環境では現れなかったバグが運用環境で現れることがあります。90%のケースにおいて、バージョンの違い(PHP、Webサーバー、もしくはデータベース)、もしくは設定の違いが原因です。喜ばしくないサプライズを避けるために、開発環境が同じ設定を適用できるようにするためにアプリケーションのphp.ymlファイルでPHPのターゲットの設定を定義します。この設定に関して詳細な情報は19章を参照してください。

-

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-15で例をご覧ください。

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

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

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

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

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

> symfony clear-cache

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

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

> symfony disable APPLICATION_NAME ENVIRONMENT_NAME

デフォルトでは、無効にされたアプリケーションはdefault/unavailableアクション(symfonyに保存されます)を表示しますが、この場合、使われるモジュールとアクションをsettings.ymlファイルでカスタマイズできます。リスト16-16は例を示しています。

リスト16-16 - 利用できないアプリケーションのために実行するアクションを設定する(myapp/config/settings.yml)

all:
  .settings:
    unavailable_module:     mymodule
    unavailable_action:     maintenance

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

> symfony enable APPLICATION_NAME ENVIRONMENT_NAME

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

settings.ymlファイルのなかでcheck_lockパラメーターをonに設定すると、キャッシュがクリアされているときsymfonyはアプリケーションをロックするので、キャッシュが最終的にクリアされるまえに到達したすべてのリクエストはアプリケーションが一時的に利用できないことを伝えるページにリダイレクトされます。キャッシュが大きいとクリアするための遅延時間はミリ秒単位よりも長いので、サイトのトラフィックが大きい場合、この設定が推奨されます。

この一時的に利用できないことを伝えるページは無効にされたsymfonyを呼び出すときに表示されるものと同じではありません(キャッシュがクリアされている一方で、symfonyは正常に動かないからです)。このページは$sf_symfony_data_dir/web/errors/ディレクトリに設置されていますが、プロジェクトのweb/errors/ディレクトリ内に独自のunavailable.phpファイルを作ると、symfonyはそのファイルを代わりに使います。check_lockパラメーターはデフォルトでは無効です。パフォーマンスに対して非常に微妙なネガティブな影響があるからです。

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

> symfony clear-controllers

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

> symfony fix-perms

-

SIDEBAR 運用サーバーでsymfonyコマンドにアクセスする

PEARでsymfonyを運用サーバーにインストールした場合、symfonyコマンドラインはすべての場所から利用可能で開発環境と同じように動作します。しかしながら、凍結したプロジェクトに対しては、タスクを起動させるsymfonyコマンドのまえにphpを追加する必要があります:

// PEAR経由でインストールしたsymfonyで
> symfony [options] <TASK> [parameters]

// プロジェクトで凍結もしくはシンボリックリンクされたsymfonyで
> php symfony [options] <TASK> [parameters]

まとめ

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

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