escaping_methodのESC_ENTITIESには注意が必要

初投稿のhnwです。よろしくお願いします。

symfonyでescaping_methodをESC_ENTITIESにすると問題がありますよ、という指摘です。

symfonyには標準でHTMLの出力エスケープ機構が組み込まれています。これは、テンプレートに渡された値に関して、スカラ、配列、オブジェクトのメソッド呼び出し結果など、全ての値が勝手にescapeされるようになっています。

このescapingに関する設定として、settings.ymlでescaping_strategy(デフォルト値はbc)をbothに変更するのはsymfonyの定石です。しかし、escaping_methodはsymfony 1.0のデフォルト値のESC_ENTITIESのまま、という人は多いのではないでしょうか。

escaping_methodをESC_ENTITIESに設定した場合、escapeにhtmlentities関数が使われます。実はこの関数に問題があります。

htmlentitiesの問題点

htmlentities関数は、文字列中の文字参照に変換可能な文字を全て文字参照に変換する、という関数です。これは、例えばcharset=”us-ascii”なHTMLでヨーロッパ圏の文字を表示する場合には大活躍するのだと思われます。フランス発のフレームワークであるsymfonyでは、標準のescape関数としてhtmlentitiesを採用するのは自然なことだったのでしょう。

しかし、この関数でマルチバイト文字を扱うとトラブルの元になることがあります。実は、htmlentitiesはHTML 4.0またはXHTML 1.0で未定義の文字参照を作り出すことがあるのです。

この問題は「PHP Bugs: #46478: htmlentities() uses obsolete mapping table for character entity references」でPHP本家に報告済みです。具体的な内容を紹介すると、第3引数が”UTF-8″の場合にhtmlentitiesは「∵」(U+2235)を「∵」に変換するのですが、これはHTML 4.0でもXHTML 1.0でも定義されていない文字参照です。全部は調べきれていませんが、他にも該当する文字がありそうです。

そもそもの原因は、htmlentitiesで利用している文字参照とunicodeの対応表が古いか、HTML/XHTMLではなくSGMLの対応表であるか、そのどちらかまたは両方のようです。

この問題の対処がどうなるかはわかりませんが、後方互換性などを考えるとPHP側でも対応しにくいようです。現状、マルチバイト圏でHTML出力する場合はこの関数を避けるしか手が無いと思います。

対策

この問題はsf_charsetがUTF-8の場合のみ起こります。内部エンコーディングがEUC-JPのプロジェクトであれば問題は起きないはずです。

また、symfony 1.2を利用している場合には、ESC_SPECIALCHARSというhtmlspecialchars関数を利用したescaping methodが存在します。symfony 1.2ではこれがデフォルト値ですので、明示的に変更しない限りトラブルは起きないはずです。もちろん、ESC_ENTITIESを指定すると同じ問題が起こりますので注意してください。

symfony 1.0に関して言うと、symfony 1.2のESC_SPECIALCHARSをコピーしてくれば問題なく動作します。

<?php
 
/**
 * Runs the PHP function htmlspecialchars on the value passed.
 *
 * @param  string $value  the value to escape
 * @return string the escaped value
 */
function esc_specialchars($value)
{
  // Numbers and boolean values get turned into strings which can cause problems
  // with type comparisons (e.g. === or is_int() etc).
  return is_string($value) ? htmlspecialchars($value, ENT_QUOTES, sfConfig::get('sf_charset')) : $value;
}
 
define('ESC_SPECIALCHARS', 'esc_specialchars');

これをEscSpecialcharsHelper.phpとして保存し、settings.ymlで下記のように指定すればhtmlspecialcharsでescapeするようになります。

all:
  .settings:
    escaping_strategy:  both
    escaping_method:  ESC_SPECIALCHARS
    standard_helpers:  [Partial, Cache, Form, EscSpecialchars]

この程度のことでファイルを増やすのも悲しい気がするので、symfonyのhelper/EscapingHelper.phpを書き換えても良いと思います。

まとめ

  • symfonyアプリケーションの動作確認用文字列:┌|∵|┘
  • symfony 1.0ではESC_SPECIALCHARSをsymfony 1.2からパクろう

Leave a Reply

Name (required)
Mail (will not be published) (required)

Your Comments:

Spam Protection by WP-SpamFree