スカラー値をテンプレートに渡す際にエスケープしない方法

明日リリース日で、現在徹夜で作業中なので日本語がおかしくても気にしないでください。

symfonyで自動エスケープ設定を有効にしている場合、アクションからテンプレートに変数をセットする際にオブジェクトならばsfOutputEscaperクラスでラッピングされ、スカラーであれば直接エスケープされてテンプレートに渡されます。オブジェクトの場合は$espacedObject->getRawValue()で生の値を取ってくることができるのですが、スカラーの場合は既にエスケープされた状態の値になってしまっているためテンプレートに渡ってきた時点では生の値を取得できません。

// sampleActions.class.php
<?php
class sampleActions...
{
  public function executeSample(sfWebRequest $request)
  {
    $this->string = 'Foo&Bar';
  }
}
?>

//sampleSuccess.php
<?php var_dump($string) #=> 'Foo&amp;Bar' ?>

エスケープしないようにするためにはsfOutputEscaperSafeというクラスでラッピングしてあげる必要があります。ラッピングする方法は単純で、sfOutputEscaperSafeをインスタンス化する際に第1引数として渡してあげるだけです。

またアクションからテンプレートに渡すとき基本的に$this->variableのようにインスタンス変数に格納していますが、実はこのとき上記よりもう少し簡単な方法でsfOutputEscaperSafeオブジェクトでラッピングすることができます。

// sampleActions...
<?php
public function executeSample(..)
{
  $this->setVar('string', 'Foo&Bar', true); // 第3引数にtrueを渡す
}
?>

//sampleSuccess.php
<?php var_dump($string) #=> 'Foo&Bar' ?>

アクションからテンプレートに渡す際のロジックを実際にみてみると、__getマジックメソッドを使用していることがわかります。その中で$this->varHolderというsfParameterHolderのインスタンスにsetして、varHolderをテンプレートに渡すということを行っています。このvarHolderに値をセットするsetVarというメソッドがsfAction(実際にはsfComponent)クラスに定義されており、通常このメソッドの第3引数はfalseなのですが、trueを渡した際にsfOutputEscaperSafeでラッピングするということを行っています。

アクションからテンプレートに渡すときだけでなく、テンプレートからパーシャルなどの別のテンプレートに渡す際にも同様にエスケープが行われますが、その際エスケープされたくないという場合は、

<?php include_partial('show', array('string' => new sfOutputEscaperSafe($string))) ?>

先ほどと同じでsfOutputEscaperSafeでラッピングしてあげることでエスケープされないで渡すことが可能です。渡した先のテンプレート側ではsfOutputEscaperSafeのままではなく、sfOutputEscaperSafeに渡した値が自動的に取り出されますのでそっちでは特に意識する必要はありません。

別にスカラーではなくてオブジェクトでもたぶんできるのですが、最初にも言ったとおりgetRawValueで対応はできるのであんまり使わないかと思います。


ついでなので書いておきますが、オブジェクトをエスケープしないようにする方法として特定のクラスだけエスケープの対象外にするということも可能です。例えば商品情報の中の色々な情報にHTMLデータが格納されているとしましょう。商品情報は管理者しか手を加えられないし、毎回エスケープを外すのが面倒!という状況だったとします。そこでこの商品情報(Productクラス)のインスタンスはどこでテンプレートに渡そうが一切エスケープの対象にしないように設定します。

<?php
class ProjectConfiguration...
{
  public function setup()
  {
    sfOutputEscaper::markClassAsSafe('Product');
  }
}

上記のようにsfOutputEscaper::markClassAsSafeというメソッドに指定したクラスは以後エスケープされないようになります。このメソッドは別にProjectConfigurationクラスでなくとも、applicationのConfigurationだったり各アクションだったりActionsのpreExecuteだったり、要するにエスケープ処理が行われる前であればどこに書いても大丈夫です。ちなみにmarkClassesAsSafe($classes)というメソッドもあり、複数指定する場合はこのメソッドを使用すればいいと思います。use_helperみたいにfunc_get_argsで取るようにして1メソッドにまとめりゃいいのに。

というわけで変数をエスケープしないようにするTIPSをいくつか書いてみました。皆様の開発のお役に立てればと思います。くれぐれもエスケープ漏れには注意してください。

ちなみにsymfony1.2を前提に書いています。1.0だとどちらの機能もありませんので使いたければアップグレードしましょう。



切実に寝たい。