You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
545 lines
20 KiB
545 lines
20 KiB
<?php |
|
/********************************** |
|
* Created on: 2007-2-2 |
|
* File Name : lib.json.php |
|
* Copyright : 2005 Michal Migursk |
|
* License : http://www.opensource.org/licenses/bsd-license.php |
|
*********************************/ |
|
|
|
class Json |
|
{ |
|
const SERVICES_JSON_SLICE = 1; |
|
const SERVICES_JSON_IN_STR = 2; |
|
const SERVICES_JSON_IN_ARR = 3; |
|
const SERVICES_JSON_IN_OBJ = 4; |
|
const SERVICES_JSON_IN_CMT = 5; |
|
const SERVICES_JSON_LOOSE_TYPE = 16; |
|
const SERVICES_JSON_SUPPRESS_ERRORS = 32; |
|
const E_JSONTYPE = 'json decode error'; |
|
|
|
/** |
|
* 将utf16转换为utf8 |
|
* |
|
* @access private |
|
* @param string $utf16 utf16字符串 |
|
* @return string |
|
*/ |
|
private static function utf162utf8($utf16) |
|
{ |
|
if (function_exists('mb_convert_encoding')) { |
|
return mb_convert_encoding($utf16, 'UTF-8', 'UTF-16'); |
|
} |
|
$bytes = (ord($utf16{0}) << 8) | ord($utf16{1}); |
|
|
|
switch (true) { |
|
case ((0x7F & $bytes) == $bytes): |
|
return chr(0x7F & $bytes); |
|
|
|
case (0x07FF & $bytes) == $bytes: |
|
return chr(0xC0 | (($bytes >> 6) & 0x1F)) |
|
. chr(0x80 | ($bytes & 0x3F)); |
|
|
|
case (0xFFFF & $bytes) == $bytes: |
|
return chr(0xE0 | (($bytes >> 12) & 0x0F)) |
|
. chr(0x80 | (($bytes >> 6) & 0x3F)) |
|
. chr(0x80 | ($bytes & 0x3F)); |
|
} |
|
return ''; |
|
} |
|
|
|
/** |
|
* 将utf8转换为utf16 |
|
* |
|
* @access private |
|
* @param string $utf8 utf8字符串 |
|
* @return string |
|
*/ |
|
private static function utf82utf16($utf8) |
|
{ |
|
if (function_exists('mb_convert_encoding')) { |
|
return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8'); |
|
} |
|
|
|
switch (strlen($utf8)) { |
|
case 1: |
|
return $utf8; |
|
|
|
case 2: |
|
return chr(0x07 & (ord($utf8{0}) >> 2)) |
|
. chr((0xC0 & (ord($utf8{0}) << 6)) |
|
| (0x3F & ord($utf8{1}))); |
|
case 3: |
|
return chr((0xF0 & (ord($utf8{0}) << 4)) |
|
| (0x0F & (ord($utf8{1}) >> 2))) |
|
. chr((0xC0 & (ord($utf8{1}) << 6)) |
|
| (0x7F & ord($utf8{2}))); |
|
} |
|
return ''; |
|
} |
|
|
|
/** |
|
* 判断错误 |
|
* |
|
* @access private |
|
* @param mixed $data 错误对象 |
|
* @param string $code 错误代码 |
|
* @return boolean |
|
*/ |
|
private static function _is_error($data, $code = null) |
|
{ |
|
if (is_object($data) && (get_class($data) == 'services_json_error' || |
|
is_subclass_of($data, 'services_json_error'))) { |
|
return true; |
|
} |
|
|
|
return false; |
|
} |
|
|
|
/** |
|
* 清除特殊格式 |
|
* |
|
* @access private |
|
* @param string $str 待处理字符串 |
|
* @return string |
|
*/ |
|
private static function _reduce_string($str) |
|
{ |
|
$str = preg_replace(array( |
|
'#^\s*//(.+)$#m', |
|
'#^\s*/\*(.+)\*/#Us', |
|
'#/\*(.+)\*/\s*$#Us' |
|
), '', $str); |
|
return trim($str); |
|
} |
|
|
|
/** |
|
* 将对象转换为json串 |
|
* |
|
* @access public |
|
* @param mixed $var 需要转换的对象 |
|
* @return string |
|
*/ |
|
public static function _encode($var) |
|
{ |
|
switch (gettype($var)) { |
|
case 'boolean': |
|
return $var ? 'true' : 'false'; |
|
|
|
case 'NULL': |
|
return 'null'; |
|
|
|
case 'integer': |
|
return (int) $var; |
|
|
|
case 'double': |
|
case 'float': |
|
return (float) $var; |
|
|
|
case 'string': |
|
$ascii = ''; |
|
$strlen_var = strlen($var); |
|
|
|
for ($c = 0; $c < $strlen_var; ++$c) { |
|
|
|
$ord_var_c = ord($var{$c}); |
|
|
|
switch (true) { |
|
case $ord_var_c == 0x08: |
|
$ascii .= '\b'; |
|
break; |
|
case $ord_var_c == 0x09: |
|
$ascii .= '\t'; |
|
break; |
|
case $ord_var_c == 0x0A: |
|
$ascii .= '\n'; |
|
break; |
|
case $ord_var_c == 0x0C: |
|
$ascii .= '\f'; |
|
break; |
|
case $ord_var_c == 0x0D: |
|
$ascii .= '\r'; |
|
break; |
|
|
|
case $ord_var_c == 0x22: |
|
case $ord_var_c == 0x2F: |
|
case $ord_var_c == 0x5C: |
|
$ascii .= '\\'.$var{$c}; |
|
break; |
|
|
|
case (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)): |
|
$ascii .= $var{$c}; |
|
break; |
|
|
|
case (($ord_var_c & 0xE0) == 0xC0): |
|
$char = pack('C*', $ord_var_c, ord($var{$c + 1})); |
|
$c += 1; |
|
$utf16 = self::utf82utf16($char); |
|
$ascii .= sprintf('\u%04s', bin2hex($utf16)); |
|
break; |
|
|
|
case (($ord_var_c & 0xF0) == 0xE0): |
|
$char = pack('C*', $ord_var_c, |
|
ord($var{$c + 1}), |
|
ord($var{$c + 2})); |
|
$c += 2; |
|
$utf16 = self::utf82utf16($char); |
|
$ascii .= sprintf('\u%04s', bin2hex($utf16)); |
|
break; |
|
|
|
case (($ord_var_c & 0xF8) == 0xF0): |
|
$char = pack('C*', $ord_var_c, |
|
ord($var{$c + 1}), |
|
ord($var{$c + 2}), |
|
ord($var{$c + 3})); |
|
$c += 3; |
|
$utf16 = self::utf82utf16($char); |
|
$ascii .= sprintf('\u%04s', bin2hex($utf16)); |
|
break; |
|
|
|
case (($ord_var_c & 0xFC) == 0xF8): |
|
$char = pack('C*', $ord_var_c, |
|
ord($var{$c + 1}), |
|
ord($var{$c + 2}), |
|
ord($var{$c + 3}), |
|
ord($var{$c + 4})); |
|
$c += 4; |
|
$utf16 = self::utf82utf16($char); |
|
$ascii .= sprintf('\u%04s', bin2hex($utf16)); |
|
break; |
|
|
|
case (($ord_var_c & 0xFE) == 0xFC): |
|
$char = pack('C*', $ord_var_c, |
|
ord($var{$c + 1}), |
|
ord($var{$c + 2}), |
|
ord($var{$c + 3}), |
|
ord($var{$c + 4}), |
|
ord($var{$c + 5})); |
|
$c += 5; |
|
$utf16 = self::utf82utf16($char); |
|
$ascii .= sprintf('\u%04s', bin2hex($utf16)); |
|
break; |
|
} |
|
} |
|
|
|
return '"'.$ascii.'"'; |
|
|
|
case 'array': |
|
if (is_array($var) && count($var) && (array_keys($var) !== range(0, sizeof($var) - 1))) { |
|
$properties = array_map(array('Json', '_name_value'), |
|
array_keys($var), |
|
array_values($var)); |
|
|
|
foreach ($properties as $property) { |
|
if (self::_is_error($property)) { |
|
return $property; |
|
} |
|
} |
|
|
|
return '{' . join(',', $properties) . '}'; |
|
} |
|
|
|
// treat it like a regular array |
|
$elements = array_map(array('Json', '_encode'), $var); |
|
|
|
foreach ($elements as $element) { |
|
if (self::_is_error($element)) { |
|
return $element; |
|
} |
|
} |
|
|
|
return '[' . join(',', $elements) . ']'; |
|
|
|
case 'object': |
|
$vars = get_object_vars($var); |
|
|
|
$properties = array_map(array('Json', '_name_value'), |
|
array_keys($vars), |
|
array_values($vars)); |
|
|
|
foreach ($properties as $property) { |
|
if (self::_is_error($property)) { |
|
return $property; |
|
} |
|
} |
|
|
|
return '{' . join(',', $properties) . '}'; |
|
|
|
default: |
|
return false; |
|
} |
|
} |
|
|
|
/** |
|
* 解码json字符串 |
|
* |
|
* @access public |
|
* @param string $str 解码字符串 |
|
* @return mixed |
|
*/ |
|
public static function _decode($str) |
|
{ |
|
$str = self::_reduce_string($str); |
|
|
|
switch (strtolower($str)) { |
|
case 'true': |
|
return true; |
|
|
|
case 'false': |
|
return false; |
|
|
|
case 'null': |
|
return null; |
|
|
|
default: |
|
$m = array(); |
|
|
|
if (is_numeric($str)) { |
|
return ((float)$str == (integer)$str) |
|
? (integer)$str |
|
: (float)$str; |
|
|
|
} elseif (preg_match('/^("|\').*(\1)$/s', $str, $m) && $m[1] == $m[2]) { |
|
$delim = substr($str, 0, 1); |
|
$chrs = substr($str, 1, -1); |
|
$utf8 = ''; |
|
$strlen_chrs = strlen($chrs); |
|
|
|
for ($c = 0; $c < $strlen_chrs; ++$c) { |
|
|
|
$substr_chrs_c_2 = substr($chrs, $c, 2); |
|
$ord_chrs_c = ord($chrs{$c}); |
|
|
|
switch (true) { |
|
case $substr_chrs_c_2 == '\b': |
|
$utf8 .= chr(0x08); |
|
++$c; |
|
break; |
|
case $substr_chrs_c_2 == '\t': |
|
$utf8 .= chr(0x09); |
|
++$c; |
|
break; |
|
case $substr_chrs_c_2 == '\n': |
|
$utf8 .= chr(0x0A); |
|
++$c; |
|
break; |
|
case $substr_chrs_c_2 == '\f': |
|
$utf8 .= chr(0x0C); |
|
++$c; |
|
break; |
|
case $substr_chrs_c_2 == '\r': |
|
$utf8 .= chr(0x0D); |
|
++$c; |
|
break; |
|
|
|
case $substr_chrs_c_2 == '\\"': |
|
case $substr_chrs_c_2 == '\\\'': |
|
case $substr_chrs_c_2 == '\\\\': |
|
case $substr_chrs_c_2 == '\\/': |
|
if (($delim == '"' && $substr_chrs_c_2 != '\\\'') || |
|
($delim == "'" && $substr_chrs_c_2 != '\\"')) { |
|
$utf8 .= $chrs{++$c}; |
|
} |
|
break; |
|
|
|
case preg_match('/\\\u[0-9A-F]{4}/i', substr($chrs, $c, 6)): |
|
$utf16 = chr(hexdec(substr($chrs, ($c + 2), 2))) |
|
. chr(hexdec(substr($chrs, ($c + 4), 2))); |
|
$utf8 .= self::utf162utf8($utf16); |
|
$c += 5; |
|
break; |
|
|
|
case ($ord_chrs_c >= 0x20) && ($ord_chrs_c <= 0x7F): |
|
$utf8 .= $chrs{$c}; |
|
break; |
|
|
|
case ($ord_chrs_c & 0xE0) == 0xC0: |
|
$utf8 .= substr($chrs, $c, 2); |
|
++$c; |
|
break; |
|
|
|
case ($ord_chrs_c & 0xF0) == 0xE0: |
|
$utf8 .= substr($chrs, $c, 3); |
|
$c += 2; |
|
break; |
|
|
|
case ($ord_chrs_c & 0xF8) == 0xF0: |
|
$utf8 .= substr($chrs, $c, 4); |
|
$c += 3; |
|
break; |
|
|
|
case ($ord_chrs_c & 0xFC) == 0xF8: |
|
$utf8 .= substr($chrs, $c, 5); |
|
$c += 4; |
|
break; |
|
|
|
case ($ord_chrs_c & 0xFE) == 0xFC: |
|
$utf8 .= substr($chrs, $c, 6); |
|
$c += 5; |
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
return $utf8; |
|
|
|
} elseif (preg_match('/^\[.*\]$/s', $str) || preg_match('/^\{.*\}$/s', $str)) { |
|
if ($str {0} == '[') { |
|
$stk = array(self::SERVICES_JSON_IN_ARR); |
|
$arr = array(); |
|
} else { |
|
$stk = array(self::SERVICES_JSON_IN_OBJ); |
|
$obj = new stdClass(); |
|
} |
|
|
|
array_push($stk, array('what' => self::SERVICES_JSON_SLICE, |
|
'where' => 0, |
|
'delim' => false)); |
|
|
|
$chrs = substr($str, 1, -1); |
|
$chrs = self::_reduce_string($chrs); |
|
|
|
if ($chrs == '') { |
|
if (reset($stk) == self::SERVICES_JSON_IN_ARR) { |
|
return $arr; |
|
|
|
} else { |
|
return $obj; |
|
|
|
} |
|
} |
|
|
|
$strlen_chrs = strlen($chrs); |
|
for ($c = 0; $c <= $strlen_chrs; ++$c) { |
|
|
|
$top = end($stk); |
|
$substr_chrs_c_2 = substr($chrs, $c, 2); |
|
|
|
if (($c == $strlen_chrs) || (($chrs {$c} == ',') && ($top['what'] == self::SERVICES_JSON_SLICE))) { |
|
$slice = substr($chrs, $top['where'], ($c - $top['where'])); |
|
array_push($stk, array('what' => self::SERVICES_JSON_SLICE, 'where' => ($c + 1), 'delim' => false)); |
|
|
|
if (reset($stk) == self::SERVICES_JSON_IN_ARR) { |
|
array_push($arr, self::_decode($slice)); |
|
|
|
} elseif (reset($stk) == self::SERVICES_JSON_IN_OBJ) { |
|
$parts = array(); |
|
|
|
if (preg_match('/^\s*(["\'].*[^\\\]["\'])\s*:\s*(\S.*),?$/Uis', $slice, $parts)) { |
|
$key = self::_decode($parts[1]); |
|
$val = self::_decode($parts[2]); |
|
$obj->$key = $val; |
|
} elseif (preg_match('/^\s*(\w+)\s*:\s*(\S.*),?$/Uis', $slice, $parts)) { |
|
$key = $parts[1]; |
|
$val = self::_decode($parts[2]); |
|
$obj->$key = $val; |
|
} |
|
|
|
} |
|
|
|
} elseif ((($chrs {$c} == '"') || ($chrs{$c} == "'")) && ($top['what'] != self::SERVICES_JSON_IN_STR)) { |
|
array_push($stk, array('what' => self::SERVICES_JSON_IN_STR, 'where' => $c, 'delim' => $chrs{$c})); |
|
|
|
} elseif (($chrs {$c} == $top['delim']) && |
|
($top['what'] == self::SERVICES_JSON_IN_STR) && |
|
((strlen(substr($chrs, 0, $c)) - strlen(rtrim(substr($chrs, 0, $c), '\\'))) % 2 != 1)) { |
|
array_pop($stk); |
|
|
|
} elseif (($chrs {$c} == '[') && |
|
in_array($top['what'], array(self::SERVICES_JSON_SLICE, self::SERVICES_JSON_IN_ARR, self::SERVICES_JSON_IN_OBJ))) { |
|
array_push($stk, array('what' => self::SERVICES_JSON_IN_ARR, 'where' => $c, 'delim' => false)); |
|
} elseif (($chrs {$c} == ']') && ($top['what'] == self::SERVICES_JSON_IN_ARR)) { |
|
array_pop($stk); |
|
} elseif (($chrs {$c} == '{') && |
|
in_array($top['what'], array(self::SERVICES_JSON_SLICE, self::SERVICES_JSON_IN_ARR, self::SERVICES_JSON_IN_OBJ))) { |
|
array_push($stk, array('what' => self::SERVICES_JSON_IN_OBJ, 'where' => $c, 'delim' => false)); |
|
} elseif (($chrs {$c} == '}') && ($top['what'] == self::SERVICES_JSON_IN_OBJ)) { |
|
array_pop($stk); |
|
} elseif (($substr_chrs_c_2 == '/*') && |
|
in_array($top['what'], array(self::SERVICES_JSON_SLICE, self::SERVICES_JSON_IN_ARR, self::SERVICES_JSON_IN_OBJ))) { |
|
array_push($stk, array('what' => self::SERVICES_JSON_IN_CMT, 'where' => $c, 'delim' => false)); |
|
$c++; |
|
} elseif (($substr_chrs_c_2 == '*/') && ($top['what'] == self::SERVICES_JSON_IN_CMT)) { |
|
array_pop($stk); |
|
$c++; |
|
for ($i = $top['where']; $i <= $c; ++$i) |
|
$chrs = substr_replace($chrs, ' ', $i, 1); |
|
} |
|
|
|
} |
|
|
|
if (reset($stk) == self::SERVICES_JSON_IN_ARR) { |
|
return $arr; |
|
|
|
} elseif (reset($stk) == self::SERVICES_JSON_IN_OBJ) { |
|
return $obj; |
|
|
|
} |
|
|
|
} |
|
} |
|
} |
|
|
|
/** |
|
* 对配对值编码 |
|
* |
|
* @access public |
|
* @param string $name 名称 |
|
* @param mixed $value 值 |
|
* @return string |
|
*/ |
|
public static function _name_value($name, $value) |
|
{ |
|
$encoded_value = self::_encode($value); |
|
|
|
if (self::_is_error($encoded_value)) { |
|
return $encoded_value; |
|
} |
|
|
|
return self::_encode(strval($name)) . ':' . $encoded_value; |
|
} |
|
|
|
/** |
|
* 对变量进行json编码 |
|
* |
|
* @access public |
|
* @param mixed $var 需要处理的对象 |
|
* @return string |
|
*/ |
|
public static function encode($var) |
|
{ |
|
if (function_exists('json_encode')) { |
|
/** from php 5.1 */ |
|
return json_encode($var); |
|
} else { |
|
return self::_encode($var); |
|
} |
|
} |
|
|
|
/** |
|
* 对字符串进行json解码 |
|
* |
|
* @access public |
|
* @param string $var 需要解码的字符串 |
|
* @param boolean $assoc 是否强制解释为数组 |
|
* @return mixed |
|
*/ |
|
public static function decode($var, $assoc = false) |
|
{ |
|
if (function_exists('json_decode')) { |
|
/** from php 5.1 */ |
|
return json_decode($var, $assoc); |
|
} else { |
|
$result = self::_decode($var); |
|
} |
|
|
|
if ($assoc && is_object($result)) { |
|
return (array) $result; |
|
} else if (!$assoc && is_array($result)) { |
|
return (object) $result; |
|
} |
|
|
|
return $result; |
|
} |
|
} |
|
|
|
|