2011年2月2日水曜日

json_encode()代替関数

以前、php5.2未満のサーバのために、json_encode()の代替となる関数を書いた。
いくつかのプログラムで使用してきたが、3年以上たってバグを発見したので、メモしておく。

自作json_encode()は原形をほぼとどめていないが、
phpのマニュアルの中の02-May-2007 01:55のUser Contributed Notesで
Yi-Ren Chen at NCTU CSIEさんが書いたphp_json_encode()を改造して作ったものだ。

json作成のロジックで、数字だったらそのまま使い、文字ならばクォートして使用する
ような部分があるが、ここで数字の判定を
if (is_numeric($val)) return $val;
と記述していたが、これに
$data = array('id'=>'011')
のような入力を食わせると、
{"id":011}
が出力されてしまう。これはブラウザのjavascriptに8進数として処理され
{id:9}
と解釈される。

とりあえず
if (is_int($val)||is_float($val)) return $val;
のように記述し、
{id:"011"}
と受け取られるようになったことを確認したが、過去作ったシステムが、
異常動作しないかひやひやものだ。

Yi-Ren Chenさんのコードを元にjson_encode相当関数を作った世界中の人たちも
大丈夫だったのかなあと心配してみる。

修正済みコード
http://sourceforge.jp/projects/extwiki/svn/view/trunk/plugin/json_encode.php?view=markup&revision=107&root=extwiki

<?php // $Id: json_encode.php 3 2010-10-02 14:56:27Z mashiki $
/**
 * json_encode: use this  for early version(<5.2) of php
 *     which support "multibyte" but doesn't support "json_encode".
 * 
 * Modified by mashiki 2008,2009,2011
 * 
 * Original written by Yi-Ren Chen at NCTU CSIE as php_json_encode()
 *  User Contributed Notes 02-May-2007 01:55 in PHP online manual
 */
function json_encode($val) {
 if (is_int($val)||is_float($val)) return $val;
 if (is_bool($val)) return $val?'true':'false';
 if (is_array($val)) {
  $len = count($val);
  for($ii=0; $ii<$len && isset($val[$ii]); ++$ii) {}
  $temp = array();
  if($len==$ii) {
   for($i=0;$i<$len;$i++) {
    $temp[] = sprintf("%s", json_encode($val[$i]));
   }
   return '['. implode(",",$temp) .']';
  } else {
   foreach($val as $key => $item) {
    $temp[] = sprintf("%s:%s", json_encode($key), json_encode($item));
   }
   return '{'. implode(",", $temp) .'}';
  }
 }
 // else string
 static $from = array('\\',  "\n", "\r", '"');
 static $to   = array('\\\\','\\n','\\r','\\"');
 static $cmap = array(0x80, 0xFFFF, 0, 0xFFFF);
 return '"'. preg_replace_callback(
  '/&#([0-9]+);/',
  create_function('$match','return sprintf("\\u%04x", $match[1]);'),
  mb_encode_numericentity(str_replace($from, $to, $val), $cmap, 'UTF-8')
 ) . '"';
}
?>

0 件のコメント:

コメントを投稿