00001 <?php
00061 define('SERVICES_JSON_INDENT', "\t");
00062
00066 define('SERVICES_JSON_SLICE', 1);
00067
00071 define('SERVICES_JSON_IN_STR', 2);
00072
00076 define('SERVICES_JSON_IN_ARR', 3);
00077
00081 define('SERVICES_JSON_IN_OBJ', 4);
00082
00086 define('SERVICES_JSON_IN_CMT', 5);
00087
00091 define('SERVICES_JSON_LOOSE_TYPE', 16);
00092
00096 define('SERVICES_JSON_SUPPRESS_ERRORS', 32);
00097
00119 class JSON
00120 {
00137 function Services_JSON()
00138 {
00139 $this->use = SERVICES_JSON_LOOSE_TYPE;
00140 }
00141
00153 function utf162utf8($utf16)
00154 {
00155
00156 if(function_exists('mb_convert_encoding')) {
00157 return mb_convert_encoding($utf16, 'UTF-8', 'UTF-16');
00158 }
00159
00160 $bytes = (ord($utf16{0}) << 8) | ord($utf16{1});
00161
00162 switch(true) {
00163 case ((0x7F & $bytes) == $bytes):
00164
00165
00166 return chr(0x7F & $bytes);
00167
00168 case (0x07FF & $bytes) == $bytes:
00169
00170
00171 return chr(0xC0 | (($bytes >> 6) & 0x1F))
00172 . chr(0x80 | ($bytes & 0x3F));
00173
00174 case (0xFFFF & $bytes) == $bytes:
00175
00176
00177 return chr(0xE0 | (($bytes >> 12) & 0x0F))
00178 . chr(0x80 | (($bytes >> 6) & 0x3F))
00179 . chr(0x80 | ($bytes & 0x3F));
00180 }
00181
00182
00183 return '';
00184 }
00185
00197 function utf82utf16($utf8)
00198 {
00199
00200 if(function_exists('mb_convert_encoding')) {
00201 return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8');
00202 }
00203
00204 switch(strlen($utf8)) {
00205 case 1:
00206
00207
00208 return $utf8;
00209
00210 case 2:
00211
00212
00213 return chr(0x07 & (ord($utf8{0}) >> 2))
00214 . chr((0xC0 & (ord($utf8{0}) << 6))
00215 | (0x3F & ord($utf8{1})));
00216
00217 case 3:
00218
00219
00220 return chr((0xF0 & (ord($utf8{0}) << 4))
00221 | (0x0F & (ord($utf8{1}) >> 2)))
00222 . chr((0xC0 & (ord($utf8{1}) << 6))
00223 | (0x7F & ord($utf8{2})));
00224 }
00225
00226
00227 return '';
00228 }
00229
00241 function encode($var)
00242 {
00243 static $indentNr = 0;
00244
00245 $indent = str_repeat(SERVICES_JSON_INDENT,$indentNr);
00246
00247 switch (gettype($var)) {
00248 case 'boolean':
00249 return $var ? 'true' : 'false';
00250
00251 case 'NULL':
00252 return 'null';
00253
00254 case 'integer':
00255 return (int) $var;
00256
00257 case 'double':
00258 case 'float':
00259 return (float) $var;
00260
00261 case 'string':
00262
00263 $ascii = '';
00264 $strlen_var = strlen($var);
00265
00266
00267
00268
00269
00270 for ($c = 0; $c < $strlen_var; ++$c) {
00271
00272 $ord_var_c = ord($var{$c});
00273
00274 switch (true) {
00275 case $ord_var_c == 0x08:
00276 $ascii .= '\b';
00277 break;
00278 case $ord_var_c == 0x09:
00279 $ascii .= '\t';
00280 break;
00281 case $ord_var_c == 0x0A:
00282 $ascii .= '\n';
00283 break;
00284 case $ord_var_c == 0x0C:
00285 $ascii .= '\f';
00286 break;
00287 case $ord_var_c == 0x0D:
00288 $ascii .= '\r';
00289 break;
00290
00291 case $ord_var_c == 0x22:
00292 case $ord_var_c == 0x2F:
00293 case $ord_var_c == 0x5C:
00294
00295 $ascii .= '\\'.$var{$c};
00296 break;
00297
00298 case (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)):
00299
00300 $ascii .= $var{$c};
00301 break;
00302
00303 case (($ord_var_c & 0xE0) == 0xC0):
00304
00305
00306 $char = pack('C*', $ord_var_c, ord($var{$c + 1}));
00307 $c += 1;
00308 $utf16 = $this->utf82utf16($char);
00309 $ascii .= sprintf('\u%04s', bin2hex($utf16));
00310 break;
00311
00312 case (($ord_var_c & 0xF0) == 0xE0):
00313
00314
00315 $char = pack('C*', $ord_var_c,
00316 ord($var{$c + 1}),
00317 ord($var{$c + 2}));
00318 $c += 2;
00319 $utf16 = $this->utf82utf16($char);
00320 $ascii .= sprintf('\u%04s', bin2hex($utf16));
00321 break;
00322
00323 case (($ord_var_c & 0xF8) == 0xF0):
00324
00325
00326 $char = pack('C*', $ord_var_c,
00327 ord($var{$c + 1}),
00328 ord($var{$c + 2}),
00329 ord($var{$c + 3}));
00330 $c += 3;
00331 $utf16 = $this->utf82utf16($char);
00332 $ascii .= sprintf('\u%04s', bin2hex($utf16));
00333 break;
00334
00335 case (($ord_var_c & 0xFC) == 0xF8):
00336
00337
00338 $char = pack('C*', $ord_var_c,
00339 ord($var{$c + 1}),
00340 ord($var{$c + 2}),
00341 ord($var{$c + 3}),
00342 ord($var{$c + 4}));
00343 $c += 4;
00344 $utf16 = $this->utf82utf16($char);
00345 $ascii .= sprintf('\u%04s', bin2hex($utf16));
00346 break;
00347
00348 case (($ord_var_c & 0xFE) == 0xFC):
00349
00350
00351 $char = pack('C*', $ord_var_c,
00352 ord($var{$c + 1}),
00353 ord($var{$c + 2}),
00354 ord($var{$c + 3}),
00355 ord($var{$c + 4}),
00356 ord($var{$c + 5}));
00357 $c += 5;
00358 $utf16 = $this->utf82utf16($char);
00359 $ascii .= sprintf('\u%04s', bin2hex($utf16));
00360 break;
00361 }
00362 }
00363
00364 return '"'.$ascii.'"';
00365
00366 case 'array':
00367
00368
00369
00370
00371
00372
00373
00374
00375
00376
00377
00378
00379
00380
00381
00382
00383
00384
00385
00386 if (is_array($var) && count($var) && (array_keys($var) !== range(0, sizeof($var) - 1))) {
00387 $indentNr++;
00388 $properties = array_map(array($this, 'name_value'),
00389 array_keys($var),
00390 array_values($var));
00391 $indentNr--;
00392
00393 foreach($properties as $property) {
00394 if(JSON::isError($property)) {
00395 return $property;
00396 }
00397 }
00398
00399 return "\n$indent".'{' ."\n$indent".SERVICES_JSON_INDENT. join(','."\n$indent".SERVICES_JSON_INDENT, $properties) ."\n$indent".'}'."\n$indent";
00400 }
00401
00402
00403 $indentNr++;
00404 $elements = array_map(array($this, 'encode'), $var);
00405 $indentNr--;
00406
00407 foreach($elements as $element) {
00408 if(JSON::isError($element)) {
00409 return $element;
00410 }
00411 }
00412
00413 return "\n$indent".'['."\n$indent".SERVICES_JSON_INDENT. join(','."\n$indent".SERVICES_JSON_INDENT, $elements) . "\n$indent".']'."\n$indent";
00414
00415 case 'object':
00416 $vars = get_object_vars($var);
00417
00418 $indentNr++;
00419 $properties = array_map(array($this, 'name_value'),
00420 array_keys($vars),
00421 array_values($vars));
00422 $indentNr--;
00423
00424 foreach($properties as $property) {
00425 if(JSON::isError($property)) {
00426 return $property;
00427 }
00428 }
00429
00430 return "\n$indent".'{' ."\n$indent".SERVICES_JSON_INDENT. join(','."\n$indent".SERVICES_JSON_INDENT, $properties) . "\n$indent".'}'."\n$indent";
00431
00432 default:
00433 return ($this->use & SERVICES_JSON_SUPPRESS_ERRORS)
00434 ? 'null'
00435 : new JSON_Error(gettype($var)." can not be encoded as JSON string");
00436 }
00437 }
00438
00439
00440
00450 function name_value($name, $value )
00451 {
00452 $encoded_value = $this->encode($value);
00453
00454 if(JSON::isError($encoded_value)) {
00455 return $encoded_value;
00456 }
00457
00458 return $this->encode(strval($name)) . ':' . $encoded_value;
00459 }
00460
00469 function reduce_string($str)
00470 {
00471 $str = preg_replace(array(
00472
00473
00474 '#^\s*//(.+)$#m',
00475
00476
00477 '#^\s*/\*(.+)\*/#Us',
00478
00479
00480 '#/\*(.+)\*/\s*$#Us'
00481
00482 ), '', $str);
00483
00484
00485 return trim($str);
00486 }
00487
00500 function decode($str)
00501 {
00502 $str = $this->reduce_string($str);
00503
00504 switch (strtolower($str)) {
00505 case 'true':
00506 return true;
00507
00508 case 'false':
00509 return false;
00510
00511 case 'null':
00512 return null;
00513
00514 default:
00515 $m = array();
00516
00517 if (is_numeric($str)) {
00518
00519
00520
00521
00522
00523
00524
00525 return ((float)$str == (integer)$str)
00526 ? (integer)$str
00527 : (float)$str;
00528
00529 } elseif (preg_match('/^("|\').*(\1)$/s', $str, $m) && $m[1] == $m[2]) {
00530
00531 $delim = substr($str, 0, 1);
00532 $chrs = substr($str, 1, -1);
00533 $utf8 = '';
00534 $strlen_chrs = strlen($chrs);
00535
00536 for ($c = 0; $c < $strlen_chrs; ++$c) {
00537
00538 $substr_chrs_c_2 = substr($chrs, $c, 2);
00539 $ord_chrs_c = ord($chrs{$c});
00540
00541 switch (true) {
00542 case $substr_chrs_c_2 == '\b':
00543 $utf8 .= chr(0x08);
00544 ++$c;
00545 break;
00546 case $substr_chrs_c_2 == '\t':
00547 $utf8 .= chr(0x09);
00548 ++$c;
00549 break;
00550 case $substr_chrs_c_2 == '\n':
00551 $utf8 .= chr(0x0A);
00552 ++$c;
00553 break;
00554 case $substr_chrs_c_2 == '\f':
00555 $utf8 .= chr(0x0C);
00556 ++$c;
00557 break;
00558 case $substr_chrs_c_2 == '\r':
00559 $utf8 .= chr(0x0D);
00560 ++$c;
00561 break;
00562
00563 case $substr_chrs_c_2 == '\\"':
00564 case $substr_chrs_c_2 == '\\\'':
00565 case $substr_chrs_c_2 == '\\\\':
00566 case $substr_chrs_c_2 == '\\/':
00567 if (($delim == '"' && $substr_chrs_c_2 != '\\\'') ||
00568 ($delim == "'" && $substr_chrs_c_2 != '\\"')) {
00569 $utf8 .= $chrs{++$c};
00570 }
00571 break;
00572
00573 case preg_match('/\\\u[0-9A-F]{4}/i', substr($chrs, $c, 6)):
00574
00575 $utf16 = chr(hexdec(substr($chrs, ($c + 2), 2)))
00576 . chr(hexdec(substr($chrs, ($c + 4), 2)));
00577 $utf8 .= $this->utf162utf8($utf16);
00578 $c += 5;
00579 break;
00580
00581 case ($ord_chrs_c >= 0x20) && ($ord_chrs_c <= 0x7F):
00582 $utf8 .= $chrs{$c};
00583 break;
00584
00585 case ($ord_chrs_c & 0xE0) == 0xC0:
00586
00587
00588 $utf8 .= substr($chrs, $c, 2);
00589 ++$c;
00590 break;
00591
00592 case ($ord_chrs_c & 0xF0) == 0xE0:
00593
00594
00595 $utf8 .= substr($chrs, $c, 3);
00596 $c += 2;
00597 break;
00598
00599 case ($ord_chrs_c & 0xF8) == 0xF0:
00600
00601
00602 $utf8 .= substr($chrs, $c, 4);
00603 $c += 3;
00604 break;
00605
00606 case ($ord_chrs_c & 0xFC) == 0xF8:
00607
00608
00609 $utf8 .= substr($chrs, $c, 5);
00610 $c += 4;
00611 break;
00612
00613 case ($ord_chrs_c & 0xFE) == 0xFC:
00614
00615
00616 $utf8 .= substr($chrs, $c, 6);
00617 $c += 5;
00618 break;
00619
00620 }
00621
00622 }
00623
00624 return $utf8;
00625
00626 } elseif (preg_match('/^\[.*\]$/s', $str) || preg_match('/^\{.*\}$/s', $str)) {
00627
00628
00629 if ($str{0} == '[') {
00630 $stk = array(SERVICES_JSON_IN_ARR);
00631 $arr = array();
00632 } else {
00633 if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
00634 $stk = array(SERVICES_JSON_IN_OBJ);
00635 $obj = array();
00636 } else {
00637 $stk = array(SERVICES_JSON_IN_OBJ);
00638 $obj = new stdClass();
00639 }
00640 }
00641
00642 array_push($stk, array('what' => SERVICES_JSON_SLICE,
00643 'where' => 0,
00644 'delim' => false));
00645
00646 $chrs = substr($str, 1, -1);
00647 $chrs = $this->reduce_string($chrs);
00648
00649 if ($chrs == '') {
00650 if (reset($stk) == SERVICES_JSON_IN_ARR) {
00651 return $arr;
00652
00653 } else {
00654 return $obj;
00655
00656 }
00657 }
00658
00659
00660
00661 $strlen_chrs = strlen($chrs);
00662
00663 for ($c = 0; $c <= $strlen_chrs; ++$c) {
00664
00665 $top = end($stk);
00666 $substr_chrs_c_2 = substr($chrs, $c, 2);
00667
00668 if (($c == $strlen_chrs) || (($chrs{$c} == ',') && ($top['what'] == SERVICES_JSON_SLICE))) {
00669
00670
00671 $slice = substr($chrs, $top['where'], ($c - $top['where']));
00672 array_push($stk, array('what' => SERVICES_JSON_SLICE, 'where' => ($c + 1), 'delim' => false));
00673
00674
00675 if (reset($stk) == SERVICES_JSON_IN_ARR) {
00676
00677 array_push($arr, $this->decode($slice));
00678
00679 } elseif (reset($stk) == SERVICES_JSON_IN_OBJ) {
00680
00681
00682
00683
00684 $parts = array();
00685
00686 if (preg_match('/^\s*(["\'].*[^\\\]["\'])\s*:\s*(\S.*),?$/Uis', $slice, $parts)) {
00687
00688 $key = $this->decode($parts[1]);
00689 $val = $this->decode($parts[2]);
00690
00691 if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
00692 $obj[$key] = $val;
00693 } else {
00694 $obj->$key = $val;
00695 }
00696 } elseif (preg_match('/^\s*(\w+)\s*:\s*(\S.*),?$/Uis', $slice, $parts)) {
00697
00698 $key = $parts[1];
00699 $val = $this->decode($parts[2]);
00700
00701 if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
00702 $obj[$key] = $val;
00703 } else {
00704 $obj->$key = $val;
00705 }
00706 }
00707
00708 }
00709
00710 } elseif ((($chrs{$c} == '"') || ($chrs{$c} == "'")) && ($top['what'] != SERVICES_JSON_IN_STR)) {
00711
00712 array_push($stk, array('what' => SERVICES_JSON_IN_STR, 'where' => $c, 'delim' => $chrs{$c}));
00713
00714
00715 } elseif (($chrs{$c} == $top['delim']) &&
00716 ($top['what'] == SERVICES_JSON_IN_STR) &&
00717 ((strlen(substr($chrs, 0, $c)) - strlen(rtrim(substr($chrs, 0, $c), '\\'))) % 2 != 1)) {
00718
00719
00720
00721 array_pop($stk);
00722
00723
00724 } elseif (($chrs{$c} == '[') &&
00725 in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
00726
00727 array_push($stk, array('what' => SERVICES_JSON_IN_ARR, 'where' => $c, 'delim' => false));
00728
00729
00730 } elseif (($chrs{$c} == ']') && ($top['what'] == SERVICES_JSON_IN_ARR)) {
00731
00732 array_pop($stk);
00733
00734
00735 } elseif (($chrs{$c} == '{') &&
00736 in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
00737
00738 array_push($stk, array('what' => SERVICES_JSON_IN_OBJ, 'where' => $c, 'delim' => false));
00739
00740
00741 } elseif (($chrs{$c} == '}') && ($top['what'] == SERVICES_JSON_IN_OBJ)) {
00742
00743 array_pop($stk);
00744
00745
00746 } elseif (($substr_chrs_c_2 == '/*') &&
00747 in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
00748
00749 array_push($stk, array('what' => SERVICES_JSON_IN_CMT, 'where' => $c, 'delim' => false));
00750 $c++;
00751
00752
00753 } elseif (($substr_chrs_c_2 == '*/') && ($top['what'] == SERVICES_JSON_IN_CMT)) {
00754
00755 array_pop($stk);
00756 $c++;
00757
00758 for ($i = $top['where']; $i <= $c; ++$i)
00759 $chrs = substr_replace($chrs, ' ', $i, 1);
00760
00761
00762
00763 }
00764
00765 }
00766
00767 if (reset($stk) == SERVICES_JSON_IN_ARR) {
00768 return $arr;
00769
00770 } elseif (reset($stk) == SERVICES_JSON_IN_OBJ) {
00771 return $obj;
00772
00773 }
00774
00775 }
00776 }
00777 }
00778
00782 function isError($data, $code = null)
00783 {
00784 if (class_exists('pear')) {
00785 return PEAR::isError($data, $code);
00786 } elseif (is_object($data) && (get_class($data) == 'json_error' ||
00787 is_subclass_of($data, 'json_error'))) {
00788 return true;
00789 }
00790
00791 return false;
00792 }
00793 }
00794
00795
00799 class JSON_Error
00800 {
00801 function JSON_Error($message = 'unknown error', $code = null,
00802 $mode = null, $options = null, $userinfo = null)
00803 {
00804
00805 }
00806 }
00807
00808
00809 ?>