display_errorsが謎の副作用を持っている箇所について
昨日 id:narusase さんよりお便りをいただきました。
はじめまして narusaseです。
↓このあたりに軽くまとめてありますが・・・
http://d.hatena.ne.jp/narusase/20091203
http://d.hatena.ne.jp/narusase/20091204htmlspecialcharsのdisplay_errorsの解釈が逆転している問題があるようです。
↓UTF-8で下記のようなコードを書き実行すると
<?php $str = 'メガネ'; $hoge = htmlspecialchars($str, ENT_QUOTES, 'SJIS'); printf("%s\n",var_export($hoge, true)); ?>htmlspecialcharsの箇所でエラーが起こります。
これ自体は問題ないのですがphp.iniの設定が「display_errors = Off」にした場合にPHP Warning.が発生します。
逆に、「display_errors = On」場合このエラーは表示されません。これは、動作としては逆になるべきで誤っていると思います。
↓このあたりにdisplay_errors云々のそれっぽい報告はあるようですが、意図的にやっているので対応する気は無いと言っている様に見えます。
[http://ml.php.gr.jp/pipermail/php-dev/2009-December/001501.html:title=[PHP-dev 1500] htmlspecialcharsのdisplay_errorsの解釈が逆転している問題]
#・・・対応する気もなさそうだしこれも投げるだけ無駄なのかなぁ〜〜?
http://bugs.php.net/bug.php?id=47494
http://bugs.php.net/bug.php?id=49579
はい、これを htmlspecialchars() のバグと看做すには、まずは他の箇所で似たようなことをやっていないかどうかを確認し、それらの妥当性を検討しないといけません。
そこで探してみると…まあ、あることはあるようです。
main/php_variable.c (135-):
if(++nest_level > PG(max_input_nesting_level)) { HashTable *ht; /* too many levels of nesting */ if (track_vars_array) { ht = Z_ARRVAL_P(track_vars_array); zend_hash_del(ht, var, var_len + 1); } else if (PG(register_globals)) { ht = EG(active_symbol_table); zend_hash_del(ht, var, var_len + 1); } zval_dtor(val); /* do not output the error message to the screen, this helps us to to avoid "information disclosure" */ if (!PG(display_errors)) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Input variable nesting level exceeded %ld. To increase the limit change max_input_nesting_level in php.ini.", PG(max_input_nesting_level)); } efree(var_orig); retuk$a }
これはリクエストパラメータで foo[]...=
のように連想配列を指定したときに、何階層までのネストを許すかを設定する設定項目である max_input_nesting_level *1 をハンドルする箇所でございますが、さてコメントに注目。「information disclosure」って書いてあるけど一体なんの情報が流出するというの?ファイルアップロードでテンポラリファイルが作成できないときは普通にエラーが出るというのに。
で、次は json の拡張モジュールですか。
ext/json.c (298-):
len = utf8_to_utf16(utf16, s, len); if (len <= 0) { if (utf16) { efree(utf16); } if (len < 0) { JSON_G(error_code) = PHP_JSON_ERROR_UTF8; if (!PG(display_errors)) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid UTF-8 sequence in argument"); } smart_str_appendl(buf, "null", 4); } else { smart_str_appendl(buf, "\"\"", 2); } return; }
あちゃー、これもひどいですね。何を考えているんでしょう。
そして、今回ご指摘のあった htmlspecialchars() の実装がある ext/standard/html.c ですね。
ext/standard/html.c (1136-):
if(status == FAILURE) { /* invalid MB sequence */ if (quote_style & ENT_HTML_IGNORE_ERRORS) { continue; } efree(replaced); if(!PG(display_errors)) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid multibyte sequence in argument"); } *newlen = 0; return STR_EMPTY_ALLOC(); }
さっきの json のにせよ、この if 文を付け加えた開発者は相当重度のアホとしかいいようがありません。svn blame & log を使ってアホを暴き出しましょう。
main/php_variables.c
236894 iliaa if (!PG(display_errors)) { 236894 iliaa php_error_docref(NULL TSRMLS_CC, E_WARNING, "Input variable nesting level more than allowed %ld (change max_input_nesting_level in php.ini to increase the limit)", PG(max_input_nesting_level)); 236894 iliaa } 236894 iliaa return;
ext/json/json.c:
251684 stas if(len < 0) { 251684 stas if(!PG(display_errors)) { 251684 stas php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid UTF-8 sequence in argument"); 251684 stas } 251684 stas smart_str_appendl(buf, "null", 4); 251684 stas } else { 251684 stas smart_str_appendl(buf, "\"\"", 2); 251684 stas }
ext/standard/html.c:
243425 stas if(status == FAILURE) { 243425 stas /* invalid MB sequence */ 243425 stas efree(replaced); 243425 stas if(!PG(display_errors)) { 243425 stas php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid multibyte sequence in argument"); 243425 stas } 243425 stas *newlen = 0; 243425 stas return STR_EMPTY_ALLOC(); 243425 stas }
はああ。。。
*1:マニュアルにはなぜか 5.0.0 で削除されたと書いてあるけど...