PHPのバージョンでZend_Jsonの動作が違っていた件 ― 2008年04月06日 04時05分27秒
Zend_Json::decode()って便利!と思っていたが
Zend_Json::decode()が割と使える。UNICODEエスケープされた文字(\uXXXX形式ね)をデコードできるからだ。
<?php require_once 'Zend/Json.php'; $s = '"\u65e5\u672c\u8a9e"'; echo Zend_Json::decode( $s );とかってすると、「日本語」と出力が得られる(内部エンコードがutf-8じゃないとあかんみたいだが)。
だもんで、クライアント側でJSでescape()したマルチバイト文字を、preg_replace_callbackを絡めて'%uXXXX'を'\uXXXX'に変換した上でダブルクォートで囲ってZend_Json::decode()に渡すようにして復元したりしていた。
が、Zend_Jsonの動きをたいして気にしていなかったため、ちみっとハマった。
PHPのバージョンの違いで、なんかヘン。
ある環境では上記のようなデコード処理がまったく問題なく動いていたのだが、他の環境で動かしたとたんに'\uなんて不正なエスケープだ!'とエラーがでるようになった。Zend Frameworkのバージョンはどちらも「1.0.0」を使っているのに。
文字コードの関連も、実行環境に依存しないように必ずdefault_charsetとmbstring.internal_encoding、mbstring.http_outputをコード中で指定してutf-8にあわせてあるし、違いといえばPHPのバージョン。
ためしに、
<?php require_once 'Zend/Json.php'; $s = '日本語'; echo Zend_Json::encode( $s );なんてのをやってみたところ、正常に動作する環境は
"\u65e5\u672c\u8a9e"とUNICODEエスケープで出力されたが、うまく動かない環境のほうでは
"日本語"と、まんまで出力されている。はて。
ソースを覗いてみたら
エンコード部分で動作に違いがでたので、Zend/Json/Encoder.php(Zend_Json_Encoder)のソースを見てみた。該当するのは _encodeString プロテクトメソッドか。
/** * JSON encode a string value by escaping characters as necessary * * @param $value string * @return string */ protected function _encodeString(&$string) { // Escape these characters with a backslash: // " \ / \n \r \t \b \f $search = array('\\', "\n", "\t", "\r", "\b", "\f", '"'); $replace = array('\\\\', '\\n', '\\t', '\\r', '\\b', '\\f', '\"'); $string = str_replace($search, $replace, $string); // Escape certain ASCII characters: // 0x08 => \b // 0x0c => \f $string = str_replace(array(chr(0x08), chr(0x0C)), array('\b', '\f'), $string); return '"' . $string . '"'; }
じゃあ、実際にコードから叩いているZend_Jsonのほうを見てみるか。
/** * Encode the mixed $valueToEncode into the JSON format * * Encodes using ext/json's json_encode() if available. * * NOTE: Object should not contain cycles; the JSON format * does not allow object reference. * * NOTE: Only public variables will be encoded * * @param mixed $valueToEncode * @param boolean $cycleCheck Optional; whether or not to check for object recursion; off by default * @return string JSON encoded object */ public static function encode($valueToEncode, $cycleCheck = false) { if (function_exists('json_encode') && self::$useBuiltinEncoderDecoder !== true) { return json_encode($valueToEncode); } require_once 'Zend/Json/Encoder.php'; return Zend_Json_Encoder::encode($valueToEncode, $cycleCheck); }
はぁ、PHP5.2.0からだったのねん
同様にZend_Json::decode()部分もjson_decode()が存在していたらそっちを利用するようになっていた。調べてみるとこの2つの関数は、JSON関数として、PHP5.2.0からは標準でインストールされるようになったPECL拡張モジュールで提供されている関数だったと。
先ほどの2つの環境、うまく動かないほうは5.1.6、正常なほうは5.2.5だもんで、なるほどこの通りになるのか。
先ほどのjson拡張モジュール自体はPHP4.3.0以降に適合するので、それをインストールすれば同様の動作になるけど、Zend_Json関連を使うときは一応PHPのバージョンを気にしておいたほうがよいかも。
オマケ
前半部分でescape()した文字のデコード目的で使用、ってな話を書いたけど、「escape()って必ずUNICODEエスケープなのか?」ってのに自信がなくなったので調べてみたら、こんな一覧表が見つかった。
なるほど現在普通に使われるようなブラウザならたいていUNICODEエスケープとみて間違いないかな(MacユーザでiCab使ってる人いたらごめんなさい)。
最近のコメント