開発とデプロイの両方の期間において、開発者はアプリケーションが期待どおりに動いているかを判断するには絶え間なく一貫した診断情報が必要になります。この情報は一般的にロギングとデバッグユーティリティに集約されます。symfonyのようなフレームワークは稼働中のアプリケーションで中心的な役割を果たすので、効率的な開発と運用活動を保証するためにこれらの機能がしっかりと統合されていることは極めて重要です。
アプリケーションが運用サーバーに設置されている期間、アプリケーションの管理者はログのローテーションからアップグレードまで膨大な数のタスクを繰り返します。フレームワークはこれらのタスクを可能なかぎり自動化するツールも提供しなければなりません。
この章では、symfonyアプリケーションの運用ツールがこれらのニーズに対してどのように答えるのかを説明します。
リクエストが実行されている間に間違ったことが行われていることを理解する唯一の方法は実行プロセスのトレースを洗い直すことです。幸いにして、このセクションで学ぶように、PHPとsymfonyの両ほうがこの種の膨大なデータをログに記録してくれます。
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ログが見つかります。
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のログメッセージには8つのレベルが存在します: PEAR::Log
パッケージ(http://pear.php.net/package/Log/ )と同じく、emerg
、alert
、crit
、err
、warning
、notice
、info
とdebug
です。リスト16-4で示されているように、それぞれのアプリケーションのlogging.yml
設定ファイルのなかでそれぞれの環境でロギングされる最大レベルを設定できます。
リスト16-4 - frontend/config/logging.yml
のなかのデフォルトのロギング設定
prod:
logger:
param:
level: err
デフォルトでは、運用環境を除いたすべての環境において、すべてのメッセージがログに記録されます(もっとも重要度が低いdebug
レベルまで)。デフォルトでは、運用環境のロギングは無効です; settings.yml
のなかのlogging_enabled
をon
に変更すると、もっとも重要なメッセージ(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
日のperiod
と10
のhistory
(バックアップ数)によって、1つの有効なログファイルに加えて、それぞれが7日の履歴を含む10のバックアップファイルを扱うことになります。現在の有効なログファイルはバックアップに移動し、もっとも古いバックアップは消去されます。
リスト16-7 - ログのローテーションを起動する
> php symfony log:rotate frontend prod --period=7 --history=10
バックアップログファイルはlogs/history/
ディレクトリに保存され、保存された時点の日付がサフィックスとして名前に追加されます。
どんなに熟練したプログラマであれ、symfonyを利用しているとしても、結局は間違いをします。エラーの検出と理解は早くアプリケーションを開発するための重要な要素の1つです。幸いにして、symfonyは開発者のためにいくつかのデバッグツールを提供します。
symfonyにはアプリケーションの開発とデバッグを円滑にするデバッグモードがあります。このモードがonのとき、つぎのことが起こります:
一方で、デバッグモードがoffのとき、プロセスはつぎのように扱われます:
cache/config/
フォルダーに保存されるPHPファイルに変換されます。最初のリクエストの後のすべてのリクエストはYAMLファイルを無視して、代わりにキャッシュされた設定を利用します。結果として、リクエストの処理はより速くなります。デバッグモードはフロントコントローラー内でアプリケーションごとに有効になります。リスト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のWebサイトページへのリンクも含みます。図16-1で示されるように、メソッド呼び出しのフルスタックと一緒に、(構文をハイライトした状態で)例外ページはPHPのコードのなかでエラーが発生した場所を表示します。問題が発生した最初の呼び出しをトレースできます。メソッドに渡された引数も表示されます。
NOTE 実際にはsymfonyはエラーのレポートに関してPHPの例外に依存しています。PHP 4のアプリケーションの機能よりもはるかに優れています。たとえば404エラーは
sfError404Exception
によって起動します。
図16-1 - symfonyのアプリケーションのためのサンプルの例外メッセージ
開発フェーズの期間において、symfonyの例外はアプリケーションのデバッグに関してとても役に立ちます。
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デバッグツールバーを使い始めます。
このツールバーは図16-2で示されるように、ウィンドウの右上のコーナーにある、標準内容の上に重ねて設置された半透明のボックスとして現れます。これによってsymfonyのログイベント、現在の設定、リクエストオブジェクトとレスポンスオブジェクトのプロパティ、リクエストによって発行されたデータベースクエリの詳細情報にアクセスできます。
図16-2 - ウィンドウ右上のコーナーに存在する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セクションはリクエストのすべての変数と定数を示す
図16-4 - 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セクションに現れる
ショットメッセージや値をトレースしたいが行をログに追加したくない場合、log_message
の代わりにdebug_message
を使います。このアクションメソッド(同じ名前のヘルパーも存在する)はlogs & msgsセッションのトップで、メッセージをWebデバッグツールバーに表示します、デバッグメッセージを書き込む方法に関してはリスト16-12を確認してください。
リスト16-12 - メッセージをデバッグツールバーに挿入する
[php]
// アクションから
$this->debugMessage($message);
// テンプレートから
<?php use_helper('Debug') ?>
<?php debug_message($message) ?>
たとえば、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を利用してカスタムコマンドライン機能を作成する別の方法は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)されたnamespace
とname
プロパティから由来します。タスクは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);
タスクの設定が
application
とenv
引数を定義する場合、タスクの設定をビルドするときに、これらは自動的に考慮されるので、タスクは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サイトをデプロイするために使われます。
プロジェクトを運用サーバーにデプロイ(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はこれらのシンボリックリンクを記憶し元の場所で再現することを覚えておいてください。
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.yml
のenabled_modules
配列から除外されるのでsymfonyのページが誤って表示されることはありません。セッションハンドリングのメカニズムはクライアントサイドのCookieを利用し、このCookieのデフォルトの名前は
symfony
です。アプリケーションをデプロイするまえに、アプリケーションがsymfonyを利用している事実を明言しないようにするためにおそらくこのデフォルトの名前をリネームすることになります。factories.yml
ファイルでCookieの名前をカスタマイズする方法は6章を参照してください。
robots.txt
ファイルはデフォルトでは空でプロジェクトのweb/
ディレクトリに設置されます。Webサイトの閲覧できる部分と避けたほうがよい部分の情報をWebスパイダーとほかのWebロボットに知らせるためにカスタマイズします。たいていの場合、このファイルは特定のURLの空間にインデックスとして記録されないようにするために使われます。たとえば、リソースが集中しているページ、インデックスに必要のないページ(バグのアーカイブなど)、もしくはロボットが捕まる無限ループのURL空間などです。ユーザーが最初にブラウザーでアプリケーションを閲覧するとき、モダンブラウザーは、アドレスバーとブックマークフォルダーのなかのアイコンでアプリケーションを表すために
favicon.ico
ファイルをリクエストします。このようなファイルを提供することでアプリケーションの外見が完全なものになり、多くの404エラーがサーバーのログに表示されないようになります。
運用ホストでsymfonyのプロジェクトを同期化する場合、いくつかのファイルとディレクトリを転送すべきではありません:
.svn/
、CVS/
など)とそれらの内容は開発と統合のためだけに必要です。cache/
ディレクトリとlog/
ディレクトリを削除してはなりません。これらのディレクトリは同様に無視しなければなりません。存在するのであればstats/
ディレクトリはおそらく同じ方法で扱うことになります。web/uploads/
ディレクトリに保存することです。このことによって1つだけのディレクトリを指定するだけでこれらのファイルを同期化の対象から除外できるようになります。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デバッグツールバーは問題を突き止めるための助けになります。よりデバッグを簡単にするためにカスタムメッセージをログファイルもしくはツールバーに挿入できます。
開発フェーズと運用フェーズにおいて、コマンドラインインターフェイスはあなたのアプリケーションの運用を円滑にする膨大な数のツールを提供します。これらのツールのなかで、データの投入、凍結、同期化のタスクは多くの時間を節約します。