トップ  > メモ一覧  > カテゴリ「いろいろ」の絞り込み結果 : 13件

13件中 1 〜 10 表示  1 | 2  次の3件> 最後»

No.1929 jobeetの完成版のダウンロード

svn export http://svn.jobeet.org/doctrine/tags/release_day_24/ jobeet

引用元

更新:2009/12/26 15:54 カテゴリ: symfony  > いろいろ ▲トップ

No.1919 symfony 1.3/1.4 の新しい機能

このチュートリアルでは symfony 1.3/1.4 のための技術的な内容をおおまかに紹介します。このチュートリアルは symfony 1.2 ですでに作業をしており、symfony 1.3/1.4 の新しい機能を速く学びたい開発者向けです。

最初に、symfony 1.3/1.4 は PHP 5.2.4 とそれ以降と互換性があることにご注意ください。

1.2からアップグレードしたいのであれば、symfony の配布ファイルの中で見つかる"プロジェクトを1.2から1.3/1.4にアップグレードする"ファイルをご覧ください。プロジェクトを symfony 1.3/1.4 に安全にアップグレードするために必要なすべての情報が手に入ります。

メーラー

symfony 1.3/1.4 では SwiftMailer 4.1 に基づく新しい標準メーラーが用意されました。

メールの送信はシンプルでアクションから composeAndSend() メソッドを使うだけです:

$this->getMailer()->composeAndSend('from@example.com', 'to@example.com', 'Subject', 'Body');

より柔軟性をもたせる必要があれば、compose() メソッドを使い後で送信することもできます。添付ファイルをメッセージに追加する方法は次のとおりです:

$message = $this->getMailer()->
  compose('from@example.com', 'to@example.com', 'Subject', 'Body')->
  attach(Swift_Attachment::fromPath('/path/to/a/file.zip'))
;
$this->getMailer()->send($message);

メーラーはとても強力なので、詳細な情報は公式マニュアルを参照してください。

セキュリティ

generate:app タスクで新しいアプリケーションを作るとき、セキュリティの設定項目はデフォルトで有効になるようになりました:

  • escaping_strategy: デフォルトではこの値は true です (--escaping-strategy オプションで無効にできます)。

  • csrf_secret: デフォルトでランダムなパスワードが生成されます。CSRF 防止機能は標準で有効です (--csrf-secret オプションで無効にできます)。settings.yml 設定ファイルを編集するか、--csrf-secret オプションを使うことで、初期パスワードを変更することを強くおすすめします。

ウィジット

標準のラベル

ラベルがフィールド名で自動生成される場合、サフィックスの _id は削除されます:

  • first_name => First name (以前と同じ)
  • author_id => Author (以前は "Author id" )

sfWidgetFormInputText

sfWidgetFormInput クラスは抽象クラスになりました。テキスト入力フィールドは sfWidgetFormInputText クラスで作られます。この変更によってフォームクラスのイントロスペクションはより簡単になりました。

国際化ウィジェット

次のウィジェットが追加されました:

  • sfWidgetFormI18nChoiceLanguage
  • sfWidgetFormI18nChoiceCurrency
  • sfWidgetFormI18nChoiceCountry
  • sfWidgetFormI18nChoiceTimezone

これらの最初の3つは廃止予定の sfWidgetFormI18nSelectLanguagesfWidgetFormI18nSelectCurrencysfWidgetFormI18nSelectCountry ウィジェットの置き換えです。

流れるようなインターフェイス

ウィジットは次のような流れるようなインターフェイスを実装するようになりました:

  • sfWidgetForm: setDefault()setLabel()setIdFormat()setHidden()

  • sfWidget: addRequiredOption()addOption()setOption()setOptions()setAttribute()setAttributes()

  • sfWidgetFormSchema: setDefault()setDefaults()addFormFormatter()setFormFormatterName()setNameFormat()setLabels()setLabel()setHelps()setHelp()setParent()

  • sfWidgetFormSchemaDecorator: addFormFormatter()setFormFormatterName()setNameFormat()setLabels()setHelps()setHelp()setParent()setPositions()

バリデーター

sfValidatorRegex

sfValidatorRegex は新しい must_match オプションを持つようになりました。このオプションが false にセットされる場合、正規表現は渡すバリデーターにマッチしません。

sfValidatorRegexpattern オプションは呼び出し時に正規表現を返す sfCallable のインスタンスにしなければならなくなりました。

sfValidatorUrl

sfValidatorUrl は新しい protocols オプションを持つようになりました。次のように特定のプロトコルを許可することができるようになりました:

$validator = new sfValidatorUrl(array('protocols' => array('http', 'https')));

デフォルトでは次のプロトコルが許可されています:

  • http
  • https
  • ftp
  • ftps

sfValidatorSchemaCompare

sfValidatorSchemaCompare クラスは2つの新しいコンパレーターを持つようになりました:

  • IDENTICAL、は===と同等;
  • NOT_IDENTICAL、は!==と同等;

sfValidatorChoicesfValidatorPropelChoicesfValidatorDoctrineChoice

sfValidatorChoicesfValidatorPropelChoice そして sfValidatorDoctrineChoice バリデーターは multiple オプションが true の場合のみ有効になるふたつの新しいオプションを持ちます:

  • min 選択される必要がある最小の数
  • max 選択される必要がある最大の数

国際化バリデーター

次のバリデーターが追加されました:

  • sfValidatorI18nTimezone

標準のエラーメッセージ

次のように sfForm::setDefaultMessage() メソッドを使うことでグローバル領域で標準のエラーメッセージを定義できるようになりました:

sfValidatorBase::setDefaultMessage('required', 'This field is required.');

上記のコードはすべてのバリデーターのためにデフォルトの'Required.'メッセージをオーバーライドします。標準のメッセージはどのバリ データーが作成される前に定義しておかなければならないことに注意してください (コンフィグレーションクラスがよい場所です)。

setRequiredMessage()setInvalidMessage() メソッドは廃止予定なので、新しい setDefaultMessage() メソッドを呼び出します。

symfony がエラーを表示するとき、使われるエラーメッセージは次のように決定されます:

  • symfony はバリデーターが作成されたときに渡されたメッセージを探します (バリデーターのコンストラクターの第2引数経由);

  • 定義されていなければ、setDefaultMessage() メソッドで定義される初期メッセージを探します;

  • もし、定義されていなければ、(メッセージが addMessage() メソッドで追加されているとき) バリデーター自身で定義された初期メッセージへ戻ります。

流れるようなインターフェイス

バリデーターは次のような流れるようなインターフェイスを実装するようになりました:

  • sfValidatorSchema: setPreValidator()setPostValidator()

  • sfValidatorErrorSchema: addError()addErrors()

  • sfValidatorBase: addMessage()setMessage()setMessages()addOption()setOption()setOptions()addRequiredOption()

sfValidatorFile

php.inifile_uploads が無効な場合 sfValidatorFile のインスタンスを作成するときに例外が投げられます。

フォーム

sfForm::useFields()

新しい sfForm::useFields() メソッドはフォームから引数として提供されるもの以外、隠しフィールドではないフィールドすべてを削除します。状況によって不要なフィールドの割り当てを 解除する代わりにフォームで維持したいフィールドを明示的に指示するのが簡単になります。たとえば、新しいフィールドを基底フォームに追加するとき、これ らは明示的に追加されるまでフォームで自動的に現われなくなります (モデルフォームで新しいカラムを関連テーブルに追加する場合を考えてください)。

class ArticleForm extends BaseArticleForm
{
  public function configure()
  {
    $this->useFields(array('title', 'content'));
  }
}

デフォルトでは、フィールドの配列はフィールドの順序を変更するためにも使われます。自動的な順序づけを無効にするには、useFields() に2番目の引数として false を 渡します。

sfForm::getEmbeddedForm($name)

->getEmbeddedForm() メソッドを使って特定の組み込みフォームにアクセスできます。

sfForm::renderHiddenFields()

->renderHiddenFields() メソッドは埋め込みフォームから隠しフィールドをレンダリングします。再帰を無効にする引数が追加されました。これはフォーマッターを使って埋め込みフォームをレンダリングする場合に便利です。

// 埋め込みフォームからのフィールドを含めて、すべての隠しフィールドをレンダリングする
echo $form->renderHiddenFields();
 
// 再帰なしで隠しフィールドをレンダリングする
echo $form->renderHiddenFields(false);

sfFormSymfony

新しい sfFormSymfony クラスはイベントディスパッチャーを symfony フォームに導入します。self::$dispatcher を通してフォームクラス内部のディスパッチャーにアクセスできます。次のフォームイベントは symfony によって通知されます:

  • form.post_configure: このイベントはフォームが設定された後で通知される
  • form.filter_values: このイベントは、マージされ汚染されたパラメーターと、バインドする直前のファイルの配列をフィルタリングする
  • form.validation_error: フォームバリデーションが通らないときこのイベントが通知される
  • form.method_not_found: 不明なメソッドが呼び出されるときにこのイベントが通知される

BaseForm

symfony 1.3/1.4 のすべての新しいプロジェクトには Form コンポーネントを拡張するもしくはプロジェクト固有の機能を追加するために使うことができる BaseForm クラスが入ります。 sfDoctrinePluginsfPropelPlugin によって生成されるフォームは自動的にこのクラスを継承します。追加のフォームクラスを作るのであれば sfForm よりも BaseForm を継承すべきです。

sfForm::doBind()

汚染されたパラメーターのクリーニングは開発者にわかりやすい ->doBind() メソッドに隔離されました。このメソッドは ->bind() からのパラメーターとファイルのマージされる配列を受け取ります。

sfForm(Doctrine|Propel)::doUpdateObject()

Doctrine と Propel のフォームクラスに開発者が扱いやすい ->doUpdateObject() メソッドが加えられました。このメソッドは すでに ->processValues() によって処理された ->updateObject() から値の配列を受けとります。

sfForm::enableLocalCSRFProtection()sfForm::disableLocalCSRFProtection()

sfForm::enableLocalCSRFProtection()sfForm::disableLocalCSRFProtection() メソッドを使うとき、あなたのクラスの configure() メソッドから簡単に CSRF 防止機能を設定することができます。

CSRF 防止機能を無効にするには、次のような行を configure() メソッドに追加します:

$this->disableLocalCSRFProtection();

disableLocalCSRFProtection() を呼び出すことによって、フォームインスタンスを作成するときに CSRF 対策の秘密の文字列を渡したとしても CSRF 防止機能は無効になります。

流れるようなインターフェイス

sfForm メソッドは次のような流れるインターフェイスを実装するようになりました: addCSRFProtection()setValidators()setValidator()setValidatorSchema()setWidgets()setWidget()setWidgetSchema()setOption()setDefault()、そして setDefaults()

オートローダー

symfony のすべてのオートローダーは大文字と小文字を区別しないようになりました。PHP が大文字と小文字を区別をしないので、symfony はそれに合わせることにしたからです。

sfAutoloadAgain (実験的な機能)

デバッグモードでの用途を目的とする特殊なオートローダーが追加されました。新しい sfAutoloadAgain クラスは symfony の標準オートローダーをリロードし問題のクラスを求めてファイルシステムを検索します。純粋な効果は新しいクラスをプロジェクトに追加した後に symfony cc を実行する必要がなくなることです。

テスト

テストのスピードアップ

大規模なテストスイートの場合、変更するたびに全てのテストを起動するのにとても時間をかかる可能性があります。特にテストが通らない場合などはそ うでしょう。なぜならテストを修正するたびに、何も壊していないことを確認するためにテストスイート全体を再度実行することになるからです。しかし、テス トが修正されない限り、全てのテストを再実行する必要はありません。symfony 1.3/1.4 では test:allsymfony:test タスクは前回の実行時に通らなかったテストだけを再実行する --only-failed (-f がショートカットになります) オプションを持つようになりました:

$ php symfony test:all --only-failed

どのように動作するかを説明します: まず最初に、全てのテストはいつもどおりに実行されます。しかし引き続きテストを実行しても、最後のテストで通らなかったものだけが実行されます。コード を修正したら、テストは通り次回以降の実行からは除外されます。再び全てのテストが通ったら、あなたは完全なテストスイートを実行し、洗い流し繰り返すこ とができます。

機能テスト

リクエストが例外を生成するとき、レスポンステスターの debug() メソッドは HTML 標準出力の代わりに、人間が読める例外のテキストの説明を出力するようになりました。より簡単にデバッグできるようになります。

sfTesterResponse はレスポンスの内容全体に対して正規表現で検索を行える新しい matches() メソッドを持つようになりました。XML のようなレスポンスではないもの、それは checkElement() が使えないようなレスポンスですが、そういった場合にとても役立ちます。ひ弱だった contains() メソッドの代わりにも使うことができます:

$browser->with('response')->begin()->
  matches('/I have \d+ apples/')->    // 引数として正規表現をとる
  matches('!/I have \d+ apples/')->   // 冒頭の ! は正規表現がマッチしてはならないことを意味する
  matches('!/I have \d+ apples/i')->  // 追加の正規表現の修正子を追加することもできる
end();

JUnit と互換性のある XML 出力

テストタスクは --xml オプションを使うことで JUnit と互換性のある XML ファイルも出力できるようになりました:

$ php symfony test:all --xml=log.xml

簡単なデバッグ

テストが通らなかったことをテストハーネスが報告するときにデバッグを簡単にするために、通らないものについて詳細な出力ができる --trace オプションを渡すことができるようになりました:

$ php symfony test:all -t

lime による出力の色づけ

symfony 1.3/1.4 では、lime は色づけを正しく行うようになりました。これが意味することは、ほとんどの場合において lime_test の lime コンストラクターの第2引数を省略できるということです:

$t = new lime_test(1);

sfTesterResponse::checkForm()

フォームの全てのフィールドが正しくレンダリング処理されてレスポンスに含まれているかどうかをより簡単に確かめられるメソッドがレスポンステスターに収録されました:

$browser->with('response')->begin()->
  checkForm('ArticleForm')->
end();

もしくは、望むのであれば、フォームオブジェクトを渡すことができます:

$browser->with('response')->begin()->
  checkForm($browser->getArticleForm())->
end();

レスポンスが複数のフォームを含む場合は、どの DOM 部分をテストするかをピンポイントで指定する CSS セレクターを提供するオプションがあります:

$browser->with('response')->begin()->
  checkForm('ArticleForm', '#articleForm')->
end();

sfTesterResponse::isValid()

レスポンスが整形式の XML であるかをレスポンステスターの ->isValid() メソッドでチェックできます:

$browser->with('response')->begin()->
  isValid()->
end();

引数として true を渡すことでドキュメントの種類に対するレスポンスをバリデートすることもできます:

$browser->with('response')->begin()->
  isValid(true)->
end();

代わりに、バリデートする XSD もしくは RelaxNG スキーマがある場合、このファイルへのパスを提供できます:

$browser->with('response')->begin()->
  isValid('/path/to/schema.xsd')->
end();

context.load_factories をリスニングする

context.load_factories イベントのリスナーを機能テストに追加できるようになりました。これは symfony の以前のバージョンではできませんでした。

$browser->addListener('context.load_factories', array($browser, 'listenForNewContext'));

よりよい ->click()

CSS セレクターを->click() メソッドに 渡すことが可能で、セマンティックにしたい要素をターゲットにするのがはるかに楽になります。

$browser
  ->get('/login')
  ->click('form[action$="/login"] input[type="submit"]')
;

タスク

symfony の CLI はターミナルウィンドウの幅を検出することを試み、ラインのフォーマットを合わせようとします。検出できない場合 CLI は幅をデフォルトの78カラムに合わせようとします。

sfTask::askAndValidate()

ユーザーに質問をして得られた入力内容をバリデートする sfTask::askAndValidate() メソッドが新しくできました:

$anwser = $this->askAndValidate('What is you email?', new sfValidatorEmail());

このメソッドはオプションの配列を受けることもできます (より詳しい情報は API ドキュメントを参照)。

symfony:test

ときどき、開発者は特定のプラットフォームで symfony が正しく動作するのかをチェックするために symfony のテストスイートを実行する必要があります。従来は、symfony に附属している prove.php スクリプトを実行し確認しなければなりませんでした。symfony 1.3/1.4 では組み込みのタスク、コマンドラインから symfony のコアテストスイートを起動できる symfony:test タスクが用意され、ほかのタスクと同じように使うことができます:

$ php symfony symfony:test

php test/bin/prove.php に慣れていれば、同等の php data/bin/symfony symfony:test コマンドを使います。

project:deploy

project:deply タスクはちょっと改良されました。リアルタイムでファイルの転送状況を表示するようになりました。ただし、-t オプションが渡されたときだけです。もしオプションが指定されていなければタスクは何も表示しません、もちろんエラーは除きます。エラー時には、エラーの情報を出力し簡単に問題を認識できるように赤色の背景に出力します。

generate:project

symfony1.3/1.4 では、generate:project タスクを実行するとき、初期設定では ORM は Doctrine になります:

$ php /path/to/symfony generate:project foo

Propel のプロジェクトを生成するには、--orm オプションを使います:

$ php /path/to/symfony generate:project foo --orm=Propel

Propel もしくは Doctrine のどちらも使いたくない場合は、--orm オプションに none を渡します:

$ php /path/to/symfony generate:project foo --orm=none

新しい --installer オプションのおかげで新しく生成されるプロジェクトをかなりカスタマイズできる PHP スクリプトを指定することができます。スクリプトはタスクで実行され、タスクのメソッドで使うことができます。次のようなより便利なメソッドがあります: installDir()runTask()ask()askConfirmation()askAndValidate()reloadTasks()enablePlugin() そして disablePlugin()

より詳しい情報は公式ブログの記事にあります。

プロジェクトを生成するとき、2番目の引数として著者の名前を渡すことができます。これは symfony が新しいクラスを生成するときに phpdoc の @author タグに使う値を指定します。

$ php /path/to/symfony generate:project foo "Joe Schmo"

sfFileSystem::execute()

sfFileSystem::execute() メソッドは sfFileSystem::sh() メソッドをより強力な機能で置き換えます。このメソッドは stdoutstderr 出力のリアルタイム処理のコールバックをとります。また両方の出力を配列として返すこともできます。sfProjectDeployTask クラスで使い方の例を見つけることができます。

task.test.filter_test_files

test:* タスクはこれらのタスクが実行される前に task.test.filter_test_files イベントを通過するようになりました。このイベントには argumentsoptions パラメーターがあります。

sfTask::run() の強化

sfTask:run() に次のような引数の連想配列とオプションを渡すことができるようになりました:

$task = new sfDoctrineConfigureDatabaseTask($this->dispatcher, $this->formatter);
$task->run(
  array('dsn' => 'mysql:dbname=mydb;host=localhost',
), array(
  'name' => 'master',
));

これまでのバージョンでは、次のようにすればまだ動きます:

$task->run(
  array('mysql:dbname=mydb;host=localhost'),
  array('--name=master')
);

sfBaseTask::setConfiguration()

PHP から sfBaseTask を継承するタスクを呼び出すとき、->run()--application--env オプションを渡す必要はもうありません。その代わりに、ただ ->setConfiguration() を呼び出すだけで設定オブジェクトを直接セットすることができます。

$task = new sfDoctrineLoadDataTask($this->dispatcher, $this->formatter);
$task->setConfiguration($this->configuration);
$task->run();

これまでのバージョンでは、次のようにすればまだ動きます:

$task = new sfDoctrineLoadDataTask($this->dispatcher, $this->formatter);
$task->run(array(), array(
  '--application='.$options['application'],
  '--env='.$options['env'],
));

project:disableproject:enable

project:disableproject:enableタスクを使うことで、環境全体を無効、または有効にすることができるようになりました:

$ php symfony project:disable prod
$ php symfony project:enable prod

環境においてどのアプリケーションを無効にするかを指定することもできます:

$ php symfony project:disable prod frontend backend
$ php symfony project:enable prod frontend backend

これらのタスクはこれまでの機能と後方互換性があります:

$ php symfony project:disable frontend prod
$ php symfony project:enable frontend prod

helplist

helplist タスクは情報を XML 形式で表示することができるようになりました:

$ php symfony list --xml
$ php symfony help test:all --xml

この出力は新しい sfTask::asXml() メソッドにもとづいており、これはタスクオブジェクトの XML 表現を返します。

たいていの場合において XML 出力は IDE のようなサードパーティーにとても役立つでしょう。

project:optimize

このタスクを実行すればアプリケーションのテンプレートファイルの位置をキャッシュすることで実行時のディスクの読み込み回数を減らします。このタスクは運用サーバーでのみ使われます。プロジェクトを変更するたびにタスクを再実行することをお忘れなく。

$ php symfony project:optimize frontend

generate:app

generate:app タスクはコアに搭載されるデフォルトのスケルトンディレクトリの代わりにプロジェクトの data/skeleton/app ディレクトリのスケルトンディレクトリをチェックします。

タスクからメールを送信する

getMailer() メソッドを使うことでタスクからメールを簡単に送信することができます。

タスクでルーティングを使う

getRouting() メソッドを使うことでタスクからルーティングオブジェクトを簡単に取得できます。

例外

オートローディング

オートロードの間に例外が投げられるとき、symfony はこれらを捕まえエラーをユーザーに出力します。これはいくつかの"真っ白な"ページの問題を解決します。

Web デバッグツールバー

可能であれば、開発環境の例外ページで Web デバッグツールバーが表示されます。

Propel 統合

Propel はバージョン1.4にアップグレードされました。Propel のアップグレードに関する詳しい情報は公式サイトを訪問してくださるようお願いします。

Propel のビヘイビア

Propel を拡張するために symfony が依存するカスタムのビルダークラスは Propel 1.4 の新しいビヘイビアシステムに移植されました。

propel:insert-sql

propel:insert-sql がデータベースから全てのデータを削除する前に確認を行います。このタスクは複数のデータベースからデータを削除することができるので、関連するデータベースの接続名も表示するようになりました。

propel:generate-modulepropel:generate-adminpropel:generate-admin-for-route

propel:generate-modulepropel:generate-adminpropel:generate-admin-for-route タスクは生成モジュールのアクション基底クラスのコンフィギュレーションを可能にする --actions-base-class オプションをとります。

Propel のビヘイビア

Propel 1.4 はビヘイビアの実装を導入しました。カスタムの symfony ビルダーはこの新しいシステムに移植されました。

Propel モデルネイティブなビヘイビアを Propel モデルに追加したい場合、schema.yml でも可能です:

classes:
Article:
propel_behaviors:
timestampable: ~

もしくは、古い schema.yml 構文を使う場合:

propel:
article:
_propel_behaviors:
timestampable: ~

フォーム生成を無効にする

Propel の symfony ビヘイビアにパラメーターを渡すことで特定のモデルでのフォーム生成を無効にできます:

classes:
UserGroup:
propel_behaviors:
symfony:
form: false
filter: false

この設定が考慮される前にモデルをリビルドしなければならないことに注意してください。これはふるまいがモデルに添付されこれをリビルドした後でのみ存在するからです。

異なるバージョンの Propel を使う

Propel の異なるバージョンを使うのは簡単で ProjectConfiguration のなかで sf_propel_runtime_pathsf_propel_generator_path 設定変数をセットするだけです:

// config/ProjectConfiguration.class.php
public function setup()
{
  $this->enablePlugins('sfPropelPlugin');
 
  sfConfig::set('sf_propel_runtime_path', '/path/to/propel/runtime');
  sfConfig::set('sf_propel_generator_path', '/path/to/propel/generator');
}

ルーティング

デフォルトの要件

column オプションがデフォルトの id になっているとき、デフォルトの必須要件の \d+sfObjectRouteCollection にだけ適用されるようになりました。(slug のような) 数字でないカラムが指定されているとき代わりの必須要件を用意する必要はないということです。

sfObjectRouteCollection オプション

新しい default_params オプションが sfObjectRouteCollection に追加されました。これはそれぞれの生成ルートに登録されるデフォルトパラメーターを可能にします:

forum_topic:
  class: sfDoctrineRouteCollection
  options:
    default_params:
      section: forum

CLI

出力の色づけ

symfony の CLI を使うとき、symfony はあなたが利用しているコンソールが色つき出力をサポートしているかどうかを推測しようとします。しかし、symfony は推測を間違える場合があります; たとえば、Cygwin を使っているときです (Windows プラットフォームではカラー出力は常に切られているからです)。

symfony 1.3/1.4 では、グローバルオプションの --color を渡すことで色つき出力することを強制できるようになりました。

国際化(I18N)

データの更新

すべての国際化オペレーションに使われるデータは ICU プロジェクトから更新されました。symfony には約330のロケールファイルが付属しており、symfony 1.2 と比べると約70増えています。ですのでたとえば、言語リストの10番目の項目をチェックするテストケースが通らない可能性があることにご注意をお願いし ます。

ユーザーロケールを基準にソートする

このロケールに依存するデータでのすべてのソートもロケールに依存して実行されます。この目的のために sfCultureInfo->sortArray() を使うことができます。

プラグイン

symfony 1.3/1.4 以前では、sfDoctrinePluginsfCompat10Plugin 以外のすべてのプラグインはデフォルトで有効にされました:

class ProjectConfiguration extends sfProjectConfiguration
{
  public function setup()
  {
    // 互換性のために望むプラグインだけ削除および有効にする
    $this->enableAllPluginsExcept(array('sfDoctrinePlugin', 'sfCompat10Plugin'));
  }
}

symfony 1.3/1.4 では、新しく作られたプロジェクトでプラグインを使うためには ProjectConfiguration クラスで明示的に有効にしなければなりません:

class ProjectConfiguration extends sfProjectConfiguration
{
  public function setup()
  {
    $this->enablePlugins('sfDoctrinePlugin');
  }
}

plugin:install タスクはインストールするプラグインを自動的に有効にします (そして plugin:uninstall はプラグインを無効にします)。Subversion 経由でプラグインをインストールする場合、手動で有効にする必要があります。

sfProtoculousPlugin もしくは sfCompat10Plugin のようなコアプラグインを使いたい場合、必要なのは対応する enablePlugins() ステートメントを ProjectConfiguration クラスに追加することだけです。

1.2からプロジェクトをアップグレードする場合、古いふるまいはアクティブなままです。これはアップグレードタスクが ProjectConfiguration ファイルを変更しないからです。このふるまいの変更は symfony 1.3/1.4 の新規プロジェクトのみです。

sfPluginConfiguration::connectTests()

新しい setupPlugins() メソッドのなかでプラグインコンフィギュレーションの ->connectTests() メソッドを呼び出すことで test:* タスクにプラグインのテストを接続することができます。

class ProjectConfiguration extends sfProjectConfiguration
{
  public function setupPlugins()
  {
    $this->pluginConfigurations['sfExamplePlugin']->connectTests();
  }
}

設定

sf_file_link_format

symfony 1.3/1.4 は可能であるときにファイルパスをクリック可能なリンクにフォーマットします (すなわちデバッグ例外のテンプレート)。sf_file_link_format がセットされる場合、この目的に使われ、そうでなければ、symfony は PHP コンフィギュレーションの xdebug.file_link_format の値を探します。

たとえば、TextMate でファイルを開きたい場合、次のコードを settings.yml に追加します:

all:
  .settings:
    file_link_format: txmt://open?url=file://%f&line=%l

%f プレースホルダーはファイルの絶対パスに、%l プレースホルダーは行数に置き換えられます。

Doctrine との統合

フォームクラスを生成する

Doctrine は1.2にアップグレードされました。アップグレードに関する詳しい情報は Doctrine のサイトを訪問してくださるようお願いします。

フォームクラスを生成する

symfony の追加オプションを Doctrine の YAML スキーマファイルで指定することが可能になりました。そしてフォームとフィルタークラスの生成を無効にするいくつかのオプションも追加しました。

たとえば、 典型的な多対多のリファレンスモデルでは、フォームもしくはフィルターフォームクラスを生成させる必要はありません。ですので次のようなことができます:

UserGroup:
options:
symfony:
form: false
filter: false
columns:
user_id:
type: integer
primary: true
group_id:
type: integer
primary: true

フォームクラスの継承

あなたのモデルクラスからフォームを生成するとき、モデルクラスは継承を含んでいます。生成された子クラスは継承を尊重し、同じ継承構造に続くフォームを生成します。

新しいタスク

Doctrine で開発するときに手助けしてくれる新しいタスクを導入しました。

モデルテーブルを作成する

指定モデルの配列のために個別のテーブルを作成することができるようになりました。テーブルを削除するときあなたに代わってテーブルを再作成してく れます。既存のプロジェクト/データベースで新しいモデルを開発するとき、データベース全体を一掃したくなく単にテーブル群を再構築したいときに役立ちま す。

$ php symfony doctrine:create-model-tables Model1 Model2 Model3

モデルファイルを削除する

YAML スキーマファイルでモデルや名前を変更したり、使わなくなったモデルを削除したりすることがよくあるでしょう。このようなことを行ったとき、孤児となったモデル、フォームそしてフィルタークラスが現れます。doctrine:delete-model-files タスクを使うことで、モデルに関連する生成ファイルを手動で掃除することができるようになりました。

$ php symfony doctrine:delete-model-files ModelName

上記タスクは関連する生成ファイルを見つけ、そのファイルを削除したいかどうかあなたに確認する前にあなたに報告してくれます。

モデルファイルをクリーンにする

doctrine:clean-model-files タスクで上記プロセスを自動化しどのモデルがディスクに存在するが YAML スキーマファイルに存在しないかを見つけることができます。

$ php symfony doctrine:clean-model-files

上記コマンドは YAML スキーマファイルと生成されたモデルやファイルと比較し、どれを削除すべきかを決定します。これらのモデルは doctrine:delete-model-files タスクに伝えられます。タスクは自動的に削除する前にどのファイルが削除されるのか確認を求めます。

データをリロードする

データフィクスチャを再読み込みするとき完全にデータベースを一掃したいことはよくあることです。doctrine:build-all-reload タスクはこれを行ってくれますが、モデルやフォームやフィルターの生成などほかのタスクを行っているだけです。そして、これは大規模なプロジェクトにおいては時間がかかるでしょう。そこで、単純に doctrine:reload-data タスクを使うことができるようになりました。

次のコマンドです。

$ php symfony doctrine:reload-data

これは次のコマンド群を実行するのと同等です:

$ php symfony doctrine:drop-db
$ php symfony doctrine:build-db
$ php symfony doctrine:insert-sql
$ php symfony doctrine:data-load

何でもビルドする

新しい doctrine:build タスクによって明確に symfony や Doctrine にビルドしてほしいものを何でも指定できます。このより柔軟性のある解決方法に合わせて廃止予定になった既存の多くのタスクを組み合わせることで得られる機能をこのタスクを複製します。

doctrine:build の使い方は次のとおりです:

$ php symfony doctrine:build --db --and-load

これはデータベースを削除 (:drop-db) して作成 (:build-db) し、schema.yml にテーブル設定を作成 (:insert-sql) し、フィクスチャデータを読み込み (:data-load) ます。

$ php symfony doctrine:build --all-classes --and-migrate

これはモデル (:build-model)、フォーム (:build-forms)、フォームフィルター (:build-filters) を生成し、保留されているマイグレーション (:migrate) を実行します。

$ php symfony doctrine:build --model --and-migrate --and-append=data/fixtures/categories.yml

これはモデルを生成 (:build-model) し、データベースのマイグレーション (:migrate) を行い、そしてカテゴリーのフィクスチャデータ (:data-load --append --dir=/data/fixtures/categories.yml)を付け加えます。

より多くの情報は doctrine:build タスクのヘルプページを参照してください。

新しいオプション: --migrate

次のタスクは --migrateオプションを受け入れるようになり、入れ子の doctrine:insert-sql タスクを doctrine:migrate に置き換えます。

  • doctrine:build-all
  • doctrine:build-all-load
  • doctrine:build-all-reload
  • doctrine:build-all-reload-test-all
  • doctrine:rebuild-db
  • doctrine:reload-data

doctrine:generate-migration --editor-cmd

doctrine:generate-migration タスクは --editor-cmd オプションを受け入れるようになりました。このオプションは編集を楽にするためにマイグレーションクラスが作成されると実行します。

$ php symfony doctrine:generate-migration AddUserEmailColumn --editor-cmd=mate

この例はマイグレーションクラスを生成し TextMate で新しいファイルを開いています。

doctrine:generate-migrations-diff

この新しいタスクは新旧のスキーマをもとに、完全なマイグレーションクラスを自動的に生成します。

特定の接続を作成もしくは削除する

doctrine:build-dbdoctrine:drop-db を実行するときにデータベース接続を指定できるようになりました:

$ php symfony doctrine:drop-db master slave1 slave2

引用元

更新:2009/12/24 17:51 カテゴリ: symfony  > いろいろ ▲トップ

No.1845 Webデバッグツールバーにピークメモリを表示する

Webデバッグツールバーにピークメモリを表示する

Written by uechoco 12月 08

知りませんでした。symfonyのWebデバッグツールバーに表示されているメモリ使用量は、メモリの最大使用量ではなかったのです現在の確保されたメモリ量というのが正しいです。これってsymfony使いの常識ですか?

当然、メモリの最大使用量を表示したいですよね。symfony 1.2以上であれば、簡単に実装できます。(symfony 1.0をお使いの方は、sfWebDebugで、より正確なメモリ使用量を見るには - Sooeyをご覧ください。)

symfonyのWebデバッグツールバーのカスタマイズ方法は、Cookbookにしっかりと載っているのです。今回参考にするのは、The symfony Cookbook | Webデバッグツールバーをカスタマイズする方法 | symfony | Web PHP Frameworkです。実際にやってみましょう。

まずはメモリの最大使用量を表示するためのsfWebDebugPanelクラスの派生クラスを作成します。sfWebDebugPanelMemoryクラスをほんの少しだけ変えただけのクラスです。libフォルダなどに作りましょう。

PHP:
  1. <?php
  2.  
  3. /**
  4.  * sfWebDebugPanelPeakMemory adds a panel to the web debug toolbar with the peak memory used by the script.
  5.  *
  6.  * @package    symfony
  7.  * @subpackage debug
  8.  */
  9. class sfWebDebugPanelPeakMemory extends sfWebDebugPanel
  10. {
  11.   public function getTitle()
  12.   {
  13.     if (function_exists('memory_get_peak_usage'))
  14.     {
  15.       $totalMemory = sprintf('%.1f', (memory_get_peak_usage() / 1024));
  16.  
  17.       return '<img src="'.$this->webDebug->getOption('image_root_path').'/memory.png" alt="Peak Memory" /> peak:'.$totalMemory.' KB';
  18.     }
  19.   }
  20.  
  21.   public function getPanelTitle()
  22.   {
  23.   }
  24.  
  25.   public function getPanelContent()
  26.   {
  27.   }
  28. }

お気づきかと思いますが、Webデバッグツールバーの1つ1つの項目はsfWebDebugPanelクラスの派生クラスです。構造化がうまくなされているので、追加も簡単という訳です。

次に、Webデバッグツールバーに登録します。今回はプロジェクト全体で適用したいので、ProjectConfiguration.class.phpを変更します。frontendConfiguration.class.phpでも同じコードで動きます。

PHP:
  1. class ProjectConfiguration extends sfProjectConfiguration
  2. {
  3.   public function setup()
  4.   {
  5.     // ...
  6.     $this->dispatcher->connect('debug.web.load_panels', array($this, 'configureWebDebugToolbar'));
  7.   }
  8.  
  9.   public function configureWebDebugToolbar(sfEvent $event)
  10.   {
  11.     $webDebugToolbar = $event->getSubject();
  12.     $webDebugToolbar->setPanel('peak_memory', new sfWebDebugPanelPeakMemory($webDebugToolbar));
  13.     $webDebugToolbar->removePanel('memory');
  14.   }
  15.  
  16. }

字面でなんとなくわかるかもしれませんが、Webデバッグツールバーのロード時にconfigureWebDebugToolbar()メソッドを呼び出 すように登録しています。メソッド内では、さきほど作成した最大使用量を表示するパネルを追加し、代わりに元々あったメモリ表示のパネルを削除していま す。

実際に使用したときのWebデバッグツールバーがこのようになります。
sfWebDebugPanelPeakMemory
上記sfWebDebugPanelPeakMemoryクラスの中で、わかりやすく「peak:」を表示するようにしています。

今一度、symfonyを見直してみるのもいいかもしれませんね。

引用元

更新:2009/12/09 11:00 カテゴリ: symfony  > いろいろ ▲トップ

No.1756 SymfonyComponentsとOutputEscaper17:29

Symfony ComponentsとOutput Escaper 17:29 はてなブックマーク - Symfony ComponentsとOutput Escaper - ゆっくり*ゆっくり Add StarbrtRiverinnx_hidenorisotarok

11月15-18日に行われている International PHP Conferenceで発表されているらしい資料があり、おもしろいことが色々書いてあったのでご紹介したいと思います。

Symfony Components

Symfony Componentsはsymfonyのコアから独立したライブラリ群です。つい先日のKlabさんとの勉強会の時は知らなかった(知る余地もなかった)内容が色々あったのでそのあたりのことを書きます。

現在公開されているSymfony Components(http://components.symfony-project.org/)には5つのコンポーネントがあり、そのうちRequest Handler以外はソースコードが公開されている、という内容を僕は発表していたのですが、状況は少し変わっていました。

まず新たにOutput Escaperが独立してSymfony Components化されています。中をみてみると、とりあえず抜き出しただけって言う感じでした。既に使われているものなのである程度枯れてるから、 十分使いものになる内容だと思います。escaping_helperまわりは少し変わってもおかしくなさそうな気がします。

あとはForm, Command Line Tools(Task), Routing, Security, Cache, DebugなんかもSymfony Componentsの方にいくそうです。 Securityっていうのがよくわからないのですが、2.0関連なのでしょうか。それともUser系のことなのかな。ちょっと気になります。


Symfony 2.0とPHP 5.3

以前Fabien氏が来日した際、名前空間の使用について伺ったところ、「Symfony 2.0では名前空間を使うつもりはない」的なことをおっしゃっていましたおっしゃっていたようなそうでないような。。。(このあたりちょっと記憶があいまいです。)

先日、Blog | Why will Symfony 2.0 finally use PHP 5.3? | symfony | Web PHP Frameworkというブログが投稿され、Symfony 2ではPHP 5.3が必須になる旨のお話しがありました。個人的にはバックスラッシュが可愛く見えてきたので、名前空間も使われたらいいのになあと期待していました。

色々な資料をみていると、どうやらSymfony 2.0では名前空間も採用されるようです。

現在発表されているのが、

この3つです。これは現段階ではFixではないと思いますが、名前空間を使っていくことは間違いなさそうです。

いままではコードが一切なかったのでなんともいえない感じでしたが、ついに先日、2.0のbranchが切られました。svn.symfony-project.org/componentsにOutputEscaperがいないと思ったら、branches/2.0/lib/Symfony/Componentsの中にいました。いよいよbranchが切られたかと思うとちょっと感動です。

現段階ではComponentsとFoundationのディレクトリが lib直下に作成されており、FoundationにはシンプルなClassLoaderが入っていました。各Componentsはいままで単独で Autoloaderを持っていましたが、Foundation\ClassLoaderを使うようになっています。(正確には共通の ClassLoaderにあわせた作りになっているので、各ComponentからAutoloaderはなくなっています。)

Componentsは、名前空間にあわせてディレクトリが切られたりしています。今までは各ComponentのAutoloaderの実装が単純 ― 例えばDependency InjectionであればsfServiceで始まるクラスはAutoloaderと同一ディレクトリにあるものからクラス名.phpファイルをrequireするというもの ― で、ディレクトリの指定とか細かいことは一切しないため、全クラスファイルが同一ディレクトリに入っていました。そのあたりがbranches/2.0の方ではすっきりとした実装になっています。

Frameworkについてはディレクトは存在しておらず、詳細は不明です。いくつかの資料には、\Symfony\Core\Request というようにCore名前空間で書いているものもあったり、実際のところどうなるのかがよくわかってない感じです。個人的にはCoreの方がいいなーと思うんだけどなあ。

あと、Symfony 2.0で用意されるであろうRequest Handlerのイベントが、以前はapplication.load_controllerとかだったけど、core.load_controllerに変わってるっぽいです。こっちの方がどうなるかわかりませんが。。。

とりあえず、現段階で公開されている資料から読み取れるSymfony 2.0のこととかはこんな感じでしょうか。さらにここにDoctrine 2.0が入るわけですが、こっちは先日Alpha3が発表されており、来年の1月にはBeta1がでるとのことでだいぶ固まってきた感じがします。Doctrine 2.0のことはまたおいおい。


Output EscaperのTIPS

何となくOutputEscaperのソースを眺めていて、ふと気づいたことが。OutputEscaperの仕組みって、アクションからテンプレート変数を渡すときに、エスケープ用のDecoratorオブジェクトで値をラッピングスカラは直接エスケープ)をして、生の値を取り出すときは $escapingObject->getRawValue() とやりますよね。

んで見ていたのがOutputEscaper/ObjectDecorator.phpの__call()なのですが、引数の一番最後に 'esc_' で始まる文字列を渡すと、対応するescapingMethodが呼ばれる、という仕組みになっています。

つまり、esc_rawはエスケープしないというescapingMethodなので、

<?php 
echo $escapingObject->getDescription('esc_raw');
echo $escapingObject->getRawValue()->getDescription();

上記のコードは両方ともエスケープを外すコードになります。よく見たら1.2のOutputEscaperでも実装され ていました。だから何ってほどのものではないのですが、もしエスケープの方法を特定のものだけesc_entitiesに変えたい時などはこれでできそう です。

はやくSymfony 2.0つかいたいよー。

引用元

更新:2009/11/20 09:30 カテゴリ: symfony  > いろいろ ▲トップ

No.1744 symfonyでHTTPリクエストパラメーターがsfWebRequestオブジェクトに渡されるところのメモ

symfonyでHTTPリクエストパラメーターがsfWebRequestオブジェクトに渡されるところのメモAdd Star

<?php
// cache/frontend/dev/config/config_factories.yml.php
  $class = sfConfig::get('sf_factory_routing', 'sfPatternRouting');
      $cache = null;

$this->factories['routing'] = new $class($this->dispatcher, $cache, array_merge(array('auto_shutdown' => false, 'context' => $this->factories['request']->getRequestContext()), sfConfig::get('sf_factory_routing_parameters', array (
  'load_configuration' => true,
  'suffix' => '',
  'default_module' => 'default',
  'default_action' => 'index',
  'debug' => '1',
  'logging' => '1',
  'generate_shortest_url' => true,
  'extra_parameters_as_query_string' => true,
  'cache' => NULL,
))));
if ($parameters = $this->factories['routing']->parse($this->factories['request']->getPathInfo()))
{
  $this->factories['request']->addRequestParameters($parameters);
}

この部分でルーティングのパース結果(sfPatterRouting#parse)の戻り値を使ってaddRequestParametersを呼び出して、リクエストオブジェクトパラメーターを設定しています。

# parseはルーティングのマッチング処理も行います。


ちなみにこのfactories.yml.phpというファイルはsymfonyの中で最も重要なファイルだと私は思うのですが、cacheの中にあります。

symfonyのソースを読み始めたころ、このファイルの存在にしばらく気づかなかったので、何がどこで処理されて動いているのかサッパリ分かりませんでした・・・。

引用元

更新:2009/11/18 10:14 カテゴリ: symfony  > いろいろ ▲トップ

No.1671 conditionalCacheFilterをより一般的に考えてみる

conditionalCacheFilterをより一般的に考えてみる

最初に

symfonyでアプリケーションを作ろうとする際にキャッシュを使っていますか?キャッシュと一言に言ってもいろんなレイヤーでキャッシュを実現する方法があるので、なんとも答えに困る質問ですね。しかし、webアプリを作る際に言われているキャッシュであれば、

  1. apcなどでphpスクリプトをキャッシュ
  2. ページ出力内容を静的ファイルもしくは、メモリ上にキャッシュ
  3. クライアントのブラウザ側に残してもらうキャッシュ

等が一般的に考えられるのではないかと思います。おそらく、opcodeに関しては、多くの人はapcなどを使うとしても、チューニングの設定等を 意識する必要はあると思いますが、実際に中でどう動いているのかを考えることが少ないと思います。「普通のやつらの下を行く」ことを目的とするユーザ(下 のレイヤーで勝負するという意味)はそちらを調べてもらうとして、Webアプリ作成屋としては一番腕の見せ所は2番目ではないかと思います。つまり、どの 出力をキャッシュ化して、どの出力をキャッシュ化しない、といったようなことです。そして、キャッシュを絶妙のタイミングでクリアさせることです。これに よって、データベースへの接続の際のクエリーが激減させることができますので、大幅なパフォーマンスの改善が望めるからです。

今回のポストでは、この2番目の内容をもう少し詳細まで扱おうと思います。

問題

symfonyでは、cacheをサポートしており、cache.ymlで指定させることによって、次に上げるようなページキャッシュが可能となっています。

  1. layoutを含むキャッシュなのか、そうでないのか、という指定
  2. キャッシュの有効期間の指定
  3. partial, component等のページ全体ではなくて、一部のみのキャッシュの指定

これでだいたいの場合はキャッシュの使用として解決ができると思います。しかし実際にアプリケーションを作成していると、もう少し詳細なキャッシュ機構があったらいいな、というときがあります。たとえば、Jobeetのfeedの章で あったようなことです。つまり、同じactionを使用するが、sf_formatの値によって、出力をatomにするのか、htmlにするのか、切り分 ける場合です。atomの出力とhtmlの出力で同じキャッシュの指定をしたいですか?もし、同じでいいのであればいいのですが、私は分けたいと思いまし た。htmlの方ではログインしているかどうかも関係してくるのですが、atomの場合は、ログインしていようがいまいが同じ出力を返すからです。

同様に、ログインしているかどうかによって切り分けるページもあるでしょう。例えば、ログインしていると、ヘッダに、「hogehogeさん」って名前が出てくるようなものです。見せるコンテンツはログインしていようがいまいが同じだけども、ログインしていない際にはページ全体をキャッシュさせたいと思いますし、ログインしていたら、キャッシュ化させる内容は分けたいと思います。現在は、同じページでwith_layoutとwithout_layoutで切り分けることはできません。なので、ログインしてない際にはキャッシュを有効にさせ、ログインしている際にはページを無効にさせるという方法を取ります。

これらの内容は、cache.ymlだけでは実現が不可能ですので、他の方法が必要となります。

方法

symfonyにおいて、キャッシュを使う方法はいくつか用意されています。一番大きな方法としては、cache.ymlで指定することです。次に viewの中でキャッシュを使用するかどうかを指定することでしょう。さらにcacheManagerを通してキャッシュのロジックを変更することができ ます。キャッシュを自分の好きなタイミングでクリアしたり、有効にしたりすることができます。このcacheManagerを通してキャッシュを使用する には、filterが一番相性がいいようです。クリアするのは、データの変更後などになりますので、actionに書くことになってしまいますが、 requestによってキャッシュを使用するしないを変更するにはfilterを使うことになります。The Definitive Guilde to symfonyのcacheの章のconditionalCacheFilterのような方法です。今回のポストでは、このconditionalCacheFilterをもう少し拡張してみることにします。

ソースコード

今回使用したsymfonyのバージョンは、1.3.0-ALPHA2です。1.2系は同じようにできると思いますが、1.0系はおそらく実装が異なると思います。

/apps/your_apps/config/filters.yml

01.rendering: ~
02.security:  ~
03. 
04.# insert your own filters here
05.conditionalCacheFilter:
06.  class: conditionalCacheFilter
07.  param:
08.    pages:
09.#      - { module: post, action: index, format: [atom,xml] }
10.#      - { module: post, action: index, format: [xml], enabled: false }
11.      - { module: post, format: [xml], enabled: true , is_authenticated: false }
12. 
13.cache:     ~
14.execution: ~

/apps/your_app/lib/conditionalCacheFilter.class.php

01.class conditionalCacheFilter extends sfFilter
02.{
03.  protected
04.    $cacheManager    = null,
05.    $request         = null,
06.    $user            = null,
07.    $uri             = null,
08.    $defaultLifetime = null;
09. 
10.  public function initialize($context, $parameters = array())
11.  {
12.    parent::initialize($context, $parameters);
13. 
14.    $this->cacheManager    = $context->getViewCacheManager();
15.    $this->request         = $context->getRequest();
16.    $this->user            = $context->getUser();
17.    $this->uri             = $context->getRouting()->getCurrentInternalUri();
18. 
19.    $lifetime = (isset($this->cacheManager)) ? $this->cacheManager->getLifeTime($this->uri) : 0;
20.    $this->defaultLifetime = ($lifetime > 0) ? $lifetime : 86400;
21.  }
22. 
23.  public function execute($filterChain)
24.  {
25.    if ((!isset($this->cacheManager)) or !($this->getParameter('pages', false))) {
26.      $filterChain->execute();
27.      return;
28.    }
29. 
30.    foreach ($this->getParameter('pages') as $page) {
31. 
32.      $module = isset($page['module']) ? $page['module'] : null;
33. 
34.      $action = isset($page['action']) ? $page['action'] : $this->request->getParameter('action');
35. 
36.      $format = isset($page['format']) ? $page['format'] : array();
37.      if (!is_array($format)) {
38.        $format = array($format);
39.      }
40. 
41.      $is_authenticated = isset($page['is_authenticated']) ? $page['is_authenticated'] : null;
42. 
43.      // maybe better to throw exception?
44.      if (is_null($module)) {
45.        continue;
46.      }
47.      // skip
48.      if ($module !== $this->request->getParameter('module')) {
49.        continue;
50.      }
51.      // skip
52.      if (!empty($format) and !in_array($this->request->getParameter('sf_format', 'html'), $format)) {
53.        continue;
54.      }
55. 
56.      if (is_null($is_authenticated) or ($is_authenticated === $this->user->isAuthenticated())) {
57.        // remove
58.        if (isset($page['enabled']) and $page['enabled'] === false) {
59.          $this->cacheManager->remove($this->uri);
60.          continue;
61.        }
62. 
63.        //add
64.        $this->cacheManager->addCache($module, $action, array(
65.          'lifeTime' => isset($page['lifetime']) ? $page['lifetime'] : $this->defaultLifetime
66.        ));
67.      }
68.    }
69. 
70.    $filterChain->execute();
71.  }
72.}

解説

filters.ymlにて、cache filterが通る前にconditionalCacheFilterを呼びます。filterに渡すことのできるパラメターとしては、The Definitive Guilde to symfonyのcacheの章に あるようにpagesとします。そして、そこに配列として指定したいモジュール名とアクション(ない場合はそのモジュールに属すすべてのアクショ ン)format, enabled, is_authenticated, lifetimeなどのパラメターを指定できるようにしました。is_authenticatedに関しては3つのパターンがありますね。つまり、認証済 みの場合、非認証の場合、認証しているかどうかは関係のない場合です。

また、symfony1.3からは、yamlのバージョンの扱いが1.2になりますので、on/off, yes/noは使えなくなりますので、気をつけてください。true/falseで切り分けるようにしましょう。

そして、conditionalCacheFilter.class.phpにおいては、それぞれのパラメターによってキャッシュの使用について分 けるようにしました。cacheManagerに関しましては、キャッシュを有効化していないとnullを持ちますので、そのチェックが必要です。デフォ ルトではenableとなっているページもこのfilters.ymlでenabledをfalseに指定することにより、毎回キャッシュをクリアするよ うにできますので、より柔軟にキャッシュの変更ができるようになっています。

まとめ

今回のポストでは、symfonyのキャッシュフレームワークを使用する際に役に立つtipsとしてconditionalCacheFilterを一般的な使用方法で使えるように拡張しました。この拡張によって、次の2つが可能となりました。

  1. sf_formatによってキャッシュをするかどうかを切り分ける
  2. ユーザが認証済みかどうかでキャッシュをするかどうかを切り分ける

sf_formatによってキャッシュをするかどうかを切り分けることによって、例えば、feedのようなxml形式のものはある程度キャッシュを しつつ、アプリケーションとしてwebで表示されるviewであるhtml形式のものはキャッシュを使用しなくするということが可能となります。xml以 外にも考えられるものはjsonなどのAPI提供です。これらは特別な理由がない限りキャッシュ化されたものを返すのがいいと考えています。

また、ユーザが認証済みかどうかでキャッシュをするかどうかを切り分けることができることにより、認証をしていないユーザに返すレスポンスに速くな ることでしょう。これはWebサイトに訪問してくれたユーザのみならず、Googleなどの検索エンジンのクローラーにも言えることです。クローラーに よってアクセスさせるものに毎回DBアクセスをさせるのではなく、キャッシュを返して、定期的にそのキャッシュを更新させることによって、負荷を下げま しょう。

拡張しようと思えば、その余地を残してくれているのがsymfonyフレームワークです。今回のconditonalCacheFilterは、特 定のアプリケーションに依存するというよりは、より一般的なアプリケーションに必要な機能だと考えていますので、plugin化して、置いておくのもいい かもしれません。もちろんバグがあることもあると思いますので、それに関しては教えてください。

引用元

更新:2009/10/26 08:56 カテゴリ: symfony  > いろいろ ▲トップ

No.1394 symfony1.2のCSRF対策について

symfony1.2のCSRF対策について

こんにちは、小川です。
symfony1.2ではsfFormクラスを用いてフォームのレンダリングや入力項目のバリデーションを行います。このsfFormクラスにはCSRF対策も実装されているのはご存じでしょうか。
今回はこのCSRF対策が具体的にどのように行われているかをお話ししたいと思います。

先にどのような手法で対策を行うかですが、フォームごとに異なるトークンをHTML上に埋め込み、その値をバリデーション時にチェックするという方法で対策を行っています。
具体的にどのようにトークンが生成され、どのようにチェックを行っているかは後ほど詳しく説明します。

CSRF対策を有効にするためにはどうすれば良いでしょうか。Jobeetなどでsymfony1.2について学んだ方はご存じかと思います。
CSRF対策は各アプリケーションごとに設定可能で、アプリケーション作成時に以下のようにすることで有効になります。

  1. $ symfony generate:app --escaping-strategy=on --csrf-secret=myUniqueSecret frontend

通常symfonyコマンドのgenerate:appタスクを用いてアプリケーションのスケルトンを生成しますが、タスク実行時にcsrf- secretというオプションを指定します。最初の説明でトークンを埋め込むと説明しましたが、このトークンを生成するときのソルト値として使用する値を 指定します。上記の例ではmyUniqueSecretがソルト値になります。

実際にcsrf-secretというオプションを指定してアプリケーションを生成した場合、アプリケーションのconfigディレクトリ内にあるsettings.ymlという設定ファイルの内容が変化します。

  1. all:
  2.   .settings:
  3.     csrf_secret:            myUniqueSecret

allのcsrf_secretという項目に、先ほどのcsrf-secretオプションの値が入っていると思います。もしgenerate:appタス ク実行時にcsrf-secretを指定しなかった場合はfalseとなり、CSRF対策が有効になりません。もし有効に設定しなかった場合は settings.ymlを直接変更してキャッシュをクリアすれば有効になります。

有効にするだけであればとてもシンプルですが、フォームクラスを利用するときにはどのような注意が必要なのでしょうか。通常フォームクラスには「ウィ ジェット」と「バリデータ」を指定することでフォーム要素の作成とバリデーションを行っていきます。CSRF対策のトークンはどのようにして扱えば良いの でしょうか。実際にコードを書いて説明していきます。

  1. // lib/form/doctrine/UserForm.class.php
  2. <?php
  3. class UserForm extends BaseUserForm
  4. {
  5.   public function configure()
  6.   {
  7.     $this->setWidgets(array(
  8.       'email' => new sfWidgetFormInput(),
  9.     ));
  10.     $this->setValudators(array(
  11.       'email' => new sfValidatorEmail(array(), array(
  12.         'required' => 'メールアドレスを入力してください',
  13.         'invalid'  => 'メールアドレスを正しく入力してください',
  14.       )),
  15.     ));
  16.   }
  17. }

  1. // apps/frontend/modules/user/actions/actions.class.php
  2. <?php 
  3. class userActions extends sfActions
  4. {
  5.   public function executeNew(sfWebRequest $request)
  6.   {
  7.     $this->form = new UserForm();
  8.   }
  9.  
  10.   public function executeCreate(sfWebRequest $request)
  11.   {
  12.     $this->forward404Unless($request->isMethod('post'));
  13.  
  14.     $this->form = new UserForm();
  15.     $this->form->bind($request->getParameter($this->form->getName()));
  16.     if ($this->form->isValid()) {
  17.       $this->form->save();
  18.       $this->redirect('user/show?id='.$this->form->getObject()->getId());
  19.     }
  20.  
  21.     $this->setTemplate('new');
  22.   }
  23. }

  1. // apps/frontend/modules/user/templates/newSuccess.php
  2. <?php echo $form->renderFormTag() ?>
  3.   <?php echo $form->renderGlobalErrors() ?>
  4.   <?php echo $form->renderHiddenFields() ?>
  5.  
  6.   <div>
  7.     メールアドレス:<?php echo $form['email'] ?>
  8.     <?php echo $form['email']->renderError() ?>
  9.   </div>
  10.  
  11.   <p>
  12.     <input type="submit" value="Save!" />
  13.   </p>
  14. </form>

簡単なフォームクラス、アクション、テンプレートを書いてみました。実はこれだけでも既にCSRF対策は行われています。
はじめにHTML上にトークンを埋め込むと言いましたが、テンプレート上のどこにあるのでしょうか?トークンのレンダリング は$form->renderHiddenFields()で行われています。settings.yml上でcsrf-secretが指定されてい る場合、各フォームにデフォルトで_csrf_tokenという名前のhiddenフィールドが自動的に作成されます。
renderHiddenFieldsメソッドは全てのhiddenフィールドをレンダリングするためのメソッドなので、このメソッドをテンプレート内に記述していることでトークンがHTML上に埋め込まれるようになります。

基本的にこのrenderHiddenFieldsメソッドはhiddenフィールドを設定していない場合でも必ずテンプレートに記述するようにしましょう。そうしないとCSRF対策のトークンが送られず、CSRFだと判断してしまいます。

リクエストがCSRFによるものかはバリデーション時に判定しています。$form->isValid()というのがそれです。UserFormの 定義ではemailに対してsfValidatorEmailバリデータを指定しています。基本的にバリデータはウィジェットとセットで定義を行います が、先ほどの_csrf_tokenにもバリデータが定義されています。このトークンにはsfValidatorCSRFTokenという専用のクラスが 用意され、送られてきたトークンが正しいものかのチェックを行っています。

ですのでテンプレート側でrenderHiddenFieldsメソッドを実行してさえいれば、バリデーション時に必ずチェックしてくれるので特に意識することなくCSRF対策が行われるような仕組みになっています。


ではこのトークンはどのようなロジックで生成されているのでしょうか。このトークンはワンタイムトークンと呼ばれているものでは厳密にはありません。 sfFormクラスにそのロジックがありますので実際にみてみましょう。sfFormのコンストラクタでaddCSRFProtectionというメソッ ドが呼ばれており、そこに記述されています。

  1. <?php
  2. public function addCSRFProtection($secret)
  3. {
  4.   if (false === $secret || (is_null($secret) && !self::$CSRFProtection))
  5.   {
  6.     return;
  7.   }
  8.  
  9.   if (is_null($secret))
  10.   {
  11.     if (is_null(self::$CSRFSecret))
  12.     {
  13.       self::$CSRFSecret = md5(__FILE__.php_uname());
  14.     }
  15.  
  16.     $secret = self::$CSRFSecret;
  17.   }
  18.  
  19.   $token = $this->getCSRFToken($secret);
  20.  
  21.   $this->validatorSchema[self::$CSRFFieldName] = new sfValidatorCSRFToken(array('token' => $token));
  22.   $this->widgetSchema[self::$CSRFFieldName] = new sfWidgetFormInputHidden();
  23.   $this->setDefault(self::$CSRFFieldName, $token);
  24. }
  25.  
  26. public function getCSRFToken($secret = null)
  27. {
  28.   if (is_null($secret))
  29.   {
  30.     $secret = self::$CSRFSecret;
  31.   }
  32.  
  33.   return md5($secret.session_id().get_class($this));
  34. }

addCSRFProtectionの引数は通常nullが来ます。これはsfFormのコンストラクタの第3引数に渡した値が使用され、通常指定することはありませんのでコンストラクタのデフォルト値であるnullが使用されます。
引数はnullですが、self::$CSRFProtectionにsettings.ymlで記述しているcsrf_secretの値が既に入ってい る状態なので、このソルト値を元にgetCSRFTokenメソッドから実際にレンダリングに使用されるトークンの値の取得を行っています。

getCSRFTokenメソッドのロジックをみるとわかるとおり、ソルト値・セッションID・クラス名をくっつけた値のMD5ハッシュ値が利用されてい ます。ソルト値とクラス名はアプリケーションのソースコードに手を入れない限り変更されないので、セッションIDが変わらない限りはこのトークンの値は変 わらない仕様になっています。これは、例えばタイムスタンプなどを利用してトークンを生成する方法ではそのトークンを別のところに格納しておく必要があ り、フォームクラスのみで実装することが難しいからだと思われます。
またget_class関数でクラス名を取得していますが、これは__CLASS__定数とは違い実行時に評価されますので、フォームクラスが違えばそのたびにトークンの値も違う値が使用されるようになっています。

上記までで大体の実装方法はおわかりいただけたでしょうか。覚えておく必要があるのは、アプリケーション作成時にcsrf-secretの指定を行うこと と、必ずrenderHiddenFieldsメソッドをテンプレート側で呼び出すことの2つ程度で、後は特に意識せずに行うことが可能です。トークンの フィールドを自動的に作られるようになっていますが、embedForm時などには削除も行われたりしますので自動でつくことに対する副作用は特にないの ではないでしょうか。


symfony1.0のころにもCSRF対策用のプラグインは存在していました。フィルターを用いてHTML内のmethod=postなform要素を 探してtoken用のhiddenを埋め込み、POSTでリクエストが来たときに同じフィルターでチェックをするということをやるものでした。ただしこれ は、link_toのオプションでmethod=>postを指定した場合にトークンを仕込むことができなかったり、CSRFアタックを検知した場 合に例外になったりと画面ごとに制御ができず、どうしたものかと悩むことも多々ありました。

ですが今回、フォームがオブジェクトになったことによりフォーム単位で指定が変えられ、バリデーション時に一緒にCSRFアタックの検知もしてくれるので非常に使い勝手が良くなりました。
プラグインの頃はできなかったlink_to($name, $url, array('method' => 'post'))のようなときにも現在は対応しています。その場合にonclick属性に指定されるJavaScriptを生成しているのが UrlHelperの_method_javascript_functionになります。


  1. <?php
  2. function _method_javascript_function($method)
  3. {
  4.   $function = "var f = document.createElement('form'); f.style.display = 'none'; this.parentNode.appendChild(f); f.method = 'post'; f.action = this.href;";
  5.  
  6.   if ('post' != strtolower($method))
  7.   {
  8.     $function .= "var m = document.createElement('input'); m.setAttribute('type', 'hidden'); ";
  9.     $function .= sprintf("m.setAttribute('name', 'sf_method'); m.setAttribute('value', '%s'); f.appendChild(m);", strtolower($method));
  10.   }
  11.  
  12.   // CSRF protection
  13.   $form = new sfForm();
  14.   if ($form->isCSRFProtected())
  15.   {
  16.     $function .= "var m = document.createElement('input'); m.setAttribute('type', 'hidden'); ";
  17.     $function .= sprintf("m.setAttribute('name', '%s'); m.setAttribute('value', '%s'); f.appendChild(m);", $form->getCSRFFieldName(), $form->getCSRFToken());
  18.   }
  19.  
  20.   $function .= "f.submit();";
  21.  
  22.   return $function;
  23. }

上記のように空のsfFormを作成してCSRF対策が有効になっているか(settings.ymlでcsrf_secretが設定されているか)を判定して自動的にトークンを含めるということを行ってくれています。
これをアクション側で判断するのも簡単です。sfWebRequestにcheckCSRFProtectionメソッドが実装されていますので基本的にはこちらを使用します。

  1. <?php
  2. class sfWebRequest extends sfRequest
  3. {
  4.   public function checkCSRFProtection()
  5.   {
  6.     $form = new sfForm();
  7.     $form->bind($form->isCSRFProtected() ? array($form->getCSRFFieldName() => $this->getParameter($form->getCSRFFieldName())) : array());
  8.  
  9.     if (!$form->isValid())
  10.     {
  11.       throw $form->getErrorSchema();
  12.     }
  13.   }
  14. }

エラーが起こった場合には上記の通り、バリデーションエラーをまとめたsfValidatorErrorSchemaクラスをスローしますので、アクショ ン側でcheckCSRFProtectionメソッドを呼び出して適切にハンドリングしてあげることでCSRF対策が可能です。


symfony1.2では1.0に比べて、あまり目立たないかもしれませんがこういったセキュリティ面でも進化しています。XSS対策用の sfOutputEscaperオブジェクトまわりも1.0にはない機能がいくつか追加されていたり、内部を見ていくと本当に様々なところで進化を実感で きます。
1.3や1.4で大きな変更がないことを考えると、symfony1.xとしての形はだいぶ固まってきた感じがしますね。symfonyのコードもだいぶ みてきましたが、より高度な情報を配信できるようにもっと隅々まで探っていくつもりなので今後ともよろしくお願いいたします。

引用元

更新:2009/08/28 08:13 カテゴリ: symfony  > いろいろ ▲トップ

No.1260 symfony.vim


引用元

更新:2009/08/04 09:01 カテゴリ: symfony  > いろいろ ▲トップ

No.1157 symfony 1.2.8 - rolling back our security fix

Reverting the security fix from 1.2.7

Fabien did revert the fix for the non-visible form fields in Changeset 19819. Here the reasoning from the commit comment:

These changes have been done to plug a security hole in the symfony
admin generator (more information on the [symfony blog](
http://www.symfony-project.org/blog/2009/04/27/symfony-1-2-6-security-fix)).
But a lot of people complained because it causes more harm than
good. The fix disables for instance the possibility to have custom
partials. Moreover, the problem also exists in a normal form if you
don't use the "echo $form" construct and if you forget to display
some non-required fields in the template.

After some discussion with the core team members, it appears that
there is no way to "fix" the problem for the developer. We thought
that we would be able to provide something, but the reality is that
we cannot.

Is it a problem? We think it is not a big problem as the
circumstances under which there can be an exploit are extremely rare
or even inexistent as if you are vulnerable, it also means that your
database data will be corrupted in some way (read the next section
for more information). As a matter of fact, all frameworks around
are vulnerable and have the exact same problem. We already provide
more advanced security features for the form framework than any
other framework around, but we cannot go any further. Security is
also a developer matter, and too much magic is sometimes a bad
thing.

Here is the exact circumstances under which you might be vulnerable:
A field is vulnerable only if you forget to display its widget in
the template and only if it has a non-required validator. Then, an
attacker could potentially inject a valid value for this field, and
it will be saved with the rest of the object. But two things greatly
mitigate the issue: first, if you forget to display a field, each
time you save the form, its value will be lost. So, most of the
time, you will be aware of the problem because the data in the
database will be corrupted. Second, the attacker need to know the
exact field name, which is not easy as no information is contained
in the HTML.

I am sorry for any inconvenience this caused, but we felt it was right to revert something which is a wrong solution, even (or especially) when it was a security fix.

Whats else in 1.2.8

We bumped Doctrine to 1.0.10 and fixed some issues with it alongside. We also put some efforts in to ensure PHP 5.3 compatibility and improved some PHPdoc, which were mainly contributions from 1day1ticket. Besides that a lot smaller issues got resolved, so I heavily recommend checking out the changelog.

How to install

Please upgrade your existing projects by updating the reference to the 1.2.8 subversion tag or by running the PEAR upgrade command:

$ pear upgrade symfony/symfony-1.2.8

If you use the 1.2 branch from our SVN repository, just run the svn update command to upgrade your project.

Last but not least, don't forget to clear your cache by running for doctrine:

$ php symfony doctrine:build-model
$ php symfony doctrine:build-forms
$ php symfony doctrine:build-filters
$ php symfony cache:clear

or when using propel:

$ php symfony propel:build-model
$ php symfony propel:build-forms
$ php symfony propel:build-filters
$ php symfony cache:clear

引用元

更新:2009/07/15 09:45 カテゴリ: symfony  > いろいろ ▲トップ
13件中 1 〜 10 表示  1 | 2  次の3件> 最後»

FuelPHP

Mac

フロントエンド開発

web開発

プロマネ

マネタイズ

プレゼン

webサービス運用

webサービス

Linux

サーバ管理

MySQL

ソース・開発

svn・git

PHP

HTML・CSS

JavaScript

ツール, ライブラリ

ビジネス

テンプレート

負荷・チューニング

Windows

メール

メール・手紙文例

CodeIgniter

オブジェクト指向

UI・フロントエンド

cloud

マークアップ・テキスト

Flash

デザイン

DBその他

Ruby

PostgreSQL

ユーティリティ・ソフト

Firefox

ハードウェア

Google

symfony

OpenPNE全般

OpenPNE2

Hack(賢コツ)

OpenPNE3

リンク

個人開発

その他

未確認

KVS

ubuntu

Android

負荷試験

オープンソース

社会

便利ツール

マネー

Twig

食品宅配

WEB設計

オーディオ

一般常識

アプリ開発

Python

サイトマップ

うずら技術ブログ

たませんSNS

rss2.0