get_html_translation_table()の罠
最近、unhtmlspecialchars() も実装されたようですが、get_html_translation_table() を利用して、htmlspecialchars() の逆関数を作っていた人も多いでしょう。
function my_unhtmlspecialchars( $text , $quotes = ENT_QUOTES )
{
return strtr( $text , array_flip( get_html_translation_table( HTML_SPECIALCHARS , $quotes ) ) ) ;
}
<?php
var_dump( htmlspecialchars( '"\'<>&' , ENT_QUOTES ) ) ;
var_dump( get_html_translation_table( HTML_SPECIALCHARS , ENT_QUOTES ) ) ;
?>
string(25) "& quot;& #039;& lt;& gt;& amp;"
array(5) {
["""]=>
string(6) "& quot;"
["'"]=>
string(5) "& #39;"
["<"]=>
string(4) "& lt;"
[">"]=>
string(4) "& gt;"
["& "]=>
string(5) "& amp;"
}
WYSIWYGエディタを有効にするなら、基本的にHTML表示許可でデータを受け取るしかないのですが、そうするとScriptInsertionが避けられません。
HTMLを再構築してくれるライブラリさえあればなあ、と思っていたら、kentaulsさんが教えてくれました。
HTMLPurifier
http://htmlpurifier.org/
この手のライブラリってあまり信用していなかったのですが、アーカイブ内にあるsmoketestのXSSがあまりにも圧巻で、これはいけるかも! と期待してます。
ただこのライブラリ、手元で一通り試した限りでは、PHP5専用ですね。サイトにはPHP4でも動くと書いてありますし、一応、エラーを吐かずに通過はしますが、惨憺たる変換状況です。
逆に、PHP5だとおかしな動作が見つかりません。EUC-JPを通しても、ちゃんとiconvで内部的にUTF-8に変換してから処理してくれます。もちろん、返り値もEUC-JPとなります。
INSTALLにも書いてありますが、キャッシュの置き場所と、エンコーディング指定だけはちゃんとやった方が良いでしょう。
require_once dirname(__FILE__).'/library/HTMLPurifier.auto.php' ;
$config = HTMLPurifier_Config::createDefault();
$config->set('Cache', 'SerializerPath',dirname(__FILE__).'/cache');
$config->set('Core', 'Encoding', 'EUC-JP');
$purifier = new HTMLPurifier($config);
つい最近ハマったのですが、PHP5.1から、shutdown関数と出力バッファリング関数の両方を使った時の処理順が変更になっているようです。(PHP5.0.x は、PHP4と同じ)
<?php
register_shutdown_function( 'sf' ) ;
ob_start( 'ob' ) ;
echo 'main ' ;
function ob( $s )
{
return $s.'ob ' ;
}
function sf()
{
echo 'sf ' ;
}
?>
main ob sf
main sf ob
<?php
register_shutdown_function( 'sf' ) ;
ob_start( 'ob' ) ;
echo 'main ' ;
function ob( $s )
{
return $s.'ob ' ;
}
function sf()
{
while( @ob_end_flush() ) ;
echo 'sf ' ;
}
?>
PHPコードを書くとき、処理速度の関係から、極力ビルトイン関数を使うようにしているのだが、最近のお気に入りはarray_map()。
これがいろいろな局面で使える。
例えば、フォームにカンマ区切りで、複数の値を入力してもらう時。
1,2,3,4 とか。
これをint型の配列で取得したい場合、explodeしたものをforeach()で回しがちだけど、実はこの1行で済む。
$myarray = array_map( 'intval' , explode( ',' , $post_data ) ) ;
$merged_array = array_map( 'unserialize' , array_unique( array_map( 'serialize' , array_merge( $array1 , $array2 ) ) ) ) ;
前の記事じゃあまりにも、単なる落書きなので、一応まとめてみました。
インデックスが以下の型で指定された場合、すべてintval()された値の整数インデックスになる。
- boolean
- float(double)
- int
ただし、符号付き32bit範囲外の整数については、intval()と挙動が違う。
floatの整数化も、ceil()でもround()でもfloor()でもなく、intval()であるのだが、この知識も何の役にも立たないだろう。
インデックスが文字列で指定された場合は2通りに処理分けされる
/^[-]?[1-9][0-9]*$/