<?
/*
$Revision: 1.5 $
$Date: 2006/06/12 07:40:58 $
(c) David Mzareulyan
david@hiero.ru
Public interface:
string JSON::encode(mixed $value, boolean $encodeEmptyArrayToArray = true, boolean $encodeNonASCII = false)
$value - value to encode
$encodeEmptyArrayToArray - if true, "array()" encoded as "[]", else as "{}"
$encodeNonASCII - if true, _all_ non-ASCII symbols encoded as "\uXXXX"
mixed JSON::decode(string $string, boolean $decodeObjectToArray = true)
$string - string to decode
$decodeObjectToArray - if true, objects "{...}" decoded to associative arrays, else to "stdClass" instances
*/
if(__FILE__ == $_SERVER["SCRIPT_FILENAME"]) highlight_file(__FILE__);
class JSON {
function encode($value, $encodeEmptyArrayToArray = true, $encodeNonASCII = false) {
switch(gettype($value)) {
case 'boolean': return $value ? 'true' : 'false';
case 'NULL': return 'null';
case 'integer': return (int) $value;
case 'double':
case 'float': return (float) $value;
case 'string':
$value = str_replace(
array("\\","\"","\/","\b","\f","\n","\r","\t"),
array("\\\\","\\\"","\\\/","\\b","\\f","\\n","\\r","\\t"),
$value);
$value = preg_replace_callback('/[\x00-\x1F]/', array("JSON", "_space2code"), $value);
if($encodeNonASCII) $value = preg_replace_callback('{
[\xC0-\xDF][\x80-\xBF]{1}|
[\xE0-\xEF][\x80-\xBF]{2}|
[\xF0-\xF7][\x80-\xBF]{3}|
[\xF8-\xFB][\x80-\xBF]{4}|
[\xFC-\xFD][\x80-\xBF]{5}
}x', array("JSON", "_utf2code"), $value);
return '"'.$value.'"';
case 'array':
if(count($value) == 0) return $encodeEmptyArrayToArray ? "[]" : "{}";
if(array_keys($value) === range(0, count($value) - 1)) {
return '[' . join(',', array_map(array("JSON", 'encode'), $value)) . ']';
} else {
return '{'.join(',', array_map(array("JSON", '_encodeNameValuePair'), array_keys($value), array_values($value))).'}';
}
case 'object':
$vars = get_object_vars($value);
return '{'.join(',', array_map(array("JSON", '_encodeNameValuePair'), array_keys($vars), array_values($vars))).'}';
default: return '';
}
}
function decode($str, $decodeObjectToArray = true) {
if (function_exists('json_decode')) return json_decode($str, $decodeObjectToArray);
$str = trim($str);
switch(strtolower($str)) {
case 'true': return true;
case 'false': return false;
case 'null': return null;
}
if(is_numeric($str)) return ((float)$str == (int)$str) ? (int)$str : (float)$str;
if(preg_match('/^("|\')(.+)\1$/s', $str, $m)) { // string
$str = preg_replace_callback('/\x5C([\'"\x5C\/bfnrt]|u[0-9a-fA-F]{4})/', array("JSON", "_unslash"), $m[2]);
return $str;
}
if (preg_match('/^(\[.*\]|\{.*\})$/s', $str)) { // array or object
$scalarPattern = '/
true|false|null|
(?:-?\d+(?:\.\d+)?(?:e[+-]?\d+)?)|
"(?:[^"\x5C]|\x5C[\x5C"\'\\/bfnrt])*?"|
\'(?:[^\'\x5C]|\x5C[\x5C"\'\\/bfnrt])*?\'
/xis';
$saver =& new JSON__saver($helper);
$result = preg_replace_callback($scalarPattern, array($saver, "saveScalar"), $str);
$result = preg_replace('/[^\d\[\]\{\}:,]/s', '', $result);
while(!is_numeric($result)) {
$l = strlen($result);
$result = preg_replace_callback('/(\d+):(\d+)/', array($saver, "savePair"), $result);
$result = preg_replace_callback('/\[[\d,]*\]/', array($saver, "saveArray"), $result);
$result = preg_replace_callback('/\{[\d,]*\}/', array($saver, "saveObject"), $result);
if($l == strlen($result)) break;
}
$result = (int)preg_replace('/\D/', '', $result);
return JSON::_restore($helper, $result, $decodeObjectToArray);
}
return "?";
}
/* private functions */
function _space2code($a) { return sprintf("\\u%04x", ord($a[0]{0})); }
function _encodeNameValuePair($name, $value) { return JSON::encode(strval($name)).':'.JSON::encode($value); }
function _utf2code($a) {
$utf = $a[0];
$first = ord($utf{0});
$len = strlen($utf);
$code = $first & (0xff >> $len);
for($i=1; $i<$len; $i++) $code = ($code << 6) + (ord($utf{$i}) & 0x3f);
return sprintf("\\u%04x", $code);
}
function _unslash($a) {
$v = $a[1];
$repl = array('"' => '"', "'" => "'", '\\' => '\\', '/' => '/',
'b' => "\x08", 'f' => "\x0C", 'n' => "\x0A", 'r' => "\x0D", 't' => "\x09");
if(isset($repl[$v{0}])) return $repl[$v{0}];
if(strtolower($v{0}) == "u") return JSON::code2utf(hexdec(substr($v, 1)));
return "?";
}
function code2utf($x) {
if($x < 1<<8) return chr($x);
if($x < 1<<12) return chr(0xC0 + (($x>>6) & 0x1F)).
chr(0x80 + ($x & 0x3F));
if($x < 1<<17) return chr(0xE0 + (($x>>12) & 0x0F)).
chr(0x80 + (($x>>6) & 0x3F)).
chr(0x80 + ($x & 0x3F));
if($x < 1<<22) return chr(0xF0 + (($x>>18) & 0x07)).
chr(0x80 + (($x>>12) & 0x3F)).
chr(0x80 + (($x>>6) & 0x3F)).
chr(0x80 + ($x & 0x3F));
return '?';
}
function _restore(&$helper, $index, $decodeObjectToArray) {
if($index >= count($helper)) return null;
$q = $helper[$index];
switch(get_class($q)) {
case "json__scalar": return $q->value;
case "json__array":
$res = array();
foreach($q->value as $item) $res[] = JSON::_restore($helper, $item, $decodeObjectToArray);
return $res;
case "json__object":
$res = $decodeObjectToArray ? array() : new stdClass();
foreach($q->value as $item) {
$pq = $helper[$item];
if(!is_a($pq, "JSON__pair")) continue;
$k = $pq->key; $v = JSON::_restore($helper, $pq->value, $decodeObjectToArray);
if(is_array($res)) $res[$k] = $v; else $res->$k = $v;
}
return $res;
default: return null;
}
}
}
/* private classes */
class JSON__scalar { function JSON__scalar($v) { $this->value = $v; } }
class JSON__array { function JSON__array($v) { $this->value = $v; } }
class JSON__object { function JSON__object($v) { $this->value = $v; } }
class JSON__pair { function JSON__pair($k, $v) { $this->key = $k; $this->value = $v; } }
class JSON__saver {
function JSON__saver(&$helper) { $this->store =& $helper; }
function saveScalar($a) {
$this->store[] = new JSON__scalar(JSON::decode($a[0]));
return (count($this->store)-1);
}
function saveArray($a) {
$v = substr($a[0],1,-1);
$this->store[] = new JSON__array($v ? explode(",", $v) : array());
return (count($this->store)-1);
}
function saveObject($a) {
$v = substr($a[0],1,-1);
$this->store[] = new JSON__object($v ? explode(",", $v) : array());
return (count($this->store)-1);
}
function savePair($a) {
$this->store[] = new JSON__pair($this->store[$a[1]]->value, $a[2]);
return (count($this->store)-1);
}
}
?>