json_decode()のJSON_BIGINT_AS_STRINGをPHP5.2.xで使う

twitterの投稿IDをjson_decode()できない

PHPjson_decode()が大きい数値をfloat()に変換してしまう現象があり、twitterのIDをdecodeできなくて困った。

floatに変換しない方法は無いものだろうか?

http://www.php.net/manual/ja/function.json-decode.php を見たところ、JSON_BIGINT_AS_STRINGというオプションを指定すると、Stringとして扱ってくれるようなんだけど、その機能は開発中のPHPにしか入っていない。

そのほかにJSONをdecodeする方法は?

pearのServices_JSONも試したけど、遅くて話にならない。

それじゃ、開発中のjson_decode()を使おう

ということで、どうしてもjson_decode()の新しいバージョンが使いたかったので、
PHPの開発中のソースをダウンロードして、新しいバージョンのjson.soをコンパイルしたときのメモでござる。


ここで、ダウンロードするソースを確認。
http://svn.php.net/viewvc/php/php-src/trunk/ext/json/

svnを使ってソースの取得

$ svn co http://svn.php.net/repository/php/php-src/trunk/ext/json php-trunk-20110128-ext-json

取得したソースのディレクトリへ移動して

$ cd php-trunk-20110128-ext-json

さて、extensionのコンパイルでござる。
phpizeをして、configureの作成をします。

$ phpize
Configuring for:
PHP Api Version:         20041225
Zend Module Api No:      20060613
Zend Extension Api No:   220060519

configureができるので、実行するよ。一緒のphp-configの場所を指定するなり。

$ ./configure --with-php-config=/usr/bin/php-config

終わったら、make をしてみて既存のjson.soと置き換えてみる。
CentOSの場合、/usr/lib/php/modules/json.so に合ったので、そこと置き換えました。

テスト用のPHPファイルを作って、

<?php
echo "php version:".phpversion()."\n";
echo "============================\n";

$json = '{"bignumber":100999999999999999999}';
var_dump( json_decode($json));
var_dump( json_decode($json,false,512,JSON_BIGINT_AS_STRING));

実行してみると、

$ php x.php
PHP Warning:  PHP Startup: Unable to load dynamic library '/usr/lib/php/modules/json.so' -
 /usr/lib/php/modules/json.so: undefined symbol: Z_DELREF_P in Unknown on line 0

むむ、こまった。
Z_DELREF_P()って何だ?って思って、ググって見ると、PHP5.2.2からPHP5.2.3になるときにリファレンスカウンタ関係のAPIが変わったらしい。*1
とりあえず、それっぽいAPIに置き換えてみる。
JSON_parser.cにある、Z_DELREF_P()をZVAL_DELREF()に変更して、再度コンパイルをしよう。

さて、再度実行。

$ php x.php
PHP Warning:  PHP Startup: Unable to load dynamic library '/usr/lib/php/modules/json.so' - 
/usr/lib/php/modules/json.so: undefined symbol: zend_parse_parameters_none in Unknown on line 0

こんどは何だ? zend_parse_parameters_none() はjson_last_error()で使っているのかな。

あんまりつかわなそうな関数なので、コメントアウトしてしまいました。

/* {{{ proto int json_last_error()
   Returns the error code of the last json_decode(). */
static PHP_FUNCTION(json_last_error)
{
//  if (zend_parse_parameters_none() == FAILURE) {
    return;
//  }

  RETURN_LONG(JSON_G(error_code));
}
/* }}} */

さて、コンパイルしなおして、実行してみよう。

$ php x.php
php version:5.2.4
============================
object(stdClass)#1 (1) {
  ["bignumber"]=>
  float(1.01E+20)
}
object(stdClass)#1 (1) {
  ["bignumber"]=>
  string(21) "100999999999999999999"
}

ふう、できた。よかったよかった。

あれ?

twitterからidじゃなくて、id_strつかってねって書いてある。泣きたい。><
http://groups.google.com/group/twitter-development-talk/browse_thread/thread/6a16efa375532182?pli=1