MediaWiki
REL1_19
|
00001 <?php 00002 /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ 00003 00062 define('SERVICES_JSON_SLICE', 1); 00063 00067 define('SERVICES_JSON_IN_STR', 2); 00068 00072 define('SERVICES_JSON_IN_ARR', 3); 00073 00077 define('SERVICES_JSON_IN_OBJ', 4); 00078 00082 define('SERVICES_JSON_IN_CMT', 5); 00083 00087 define('SERVICES_JSON_LOOSE_TYPE', 16); 00088 00092 define('SERVICES_JSON_SUPPRESS_ERRORS', 32); 00093 00117 class Services_JSON 00118 { 00135 function __construct($use = 0) 00136 { 00137 $this->use = $use; 00138 } 00139 00140 private static $mHavePear = null; 00146 private static function pearInstalled() { 00147 if ( self::$mHavePear === null ) { 00148 self::$mHavePear = class_exists( 'pear' ); 00149 } 00150 return self::$mHavePear; 00151 } 00152 00164 function utf162utf8($utf16) 00165 { 00166 // oh please oh please oh please oh please oh please 00167 if(function_exists('mb_convert_encoding')) { 00168 return mb_convert_encoding($utf16, 'UTF-8', 'UTF-16'); 00169 } 00170 00171 $bytes = (ord($utf16[0]) << 8) | ord($utf16[1]); 00172 00173 switch(true) { 00174 case ((0x7F & $bytes) == $bytes): 00175 // this case should never be reached, because we are in ASCII range 00176 // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 00177 return chr(0x7F & $bytes); 00178 00179 case (0x07FF & $bytes) == $bytes: 00180 // return a 2-byte UTF-8 character 00181 // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 00182 return chr(0xC0 | (($bytes >> 6) & 0x1F)) 00183 . chr(0x80 | ($bytes & 0x3F)); 00184 00185 case (0xFC00 & $bytes) == 0xD800 && strlen($utf16) >= 4 && (0xFC & ord($utf16[2])) == 0xDC: 00186 // return a 4-byte UTF-8 character 00187 $char = ((($bytes & 0x03FF) << 10) 00188 | ((ord($utf16[2]) & 0x03) << 8) 00189 | ord($utf16[3])); 00190 $char += 0x10000; 00191 return chr(0xF0 | (($char >> 18) & 0x07)) 00192 . chr(0x80 | (($char >> 12) & 0x3F)) 00193 . chr(0x80 | (($char >> 6) & 0x3F)) 00194 . chr(0x80 | ($char & 0x3F)); 00195 00196 case (0xFFFF & $bytes) == $bytes: 00197 // return a 3-byte UTF-8 character 00198 // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 00199 return chr(0xE0 | (($bytes >> 12) & 0x0F)) 00200 . chr(0x80 | (($bytes >> 6) & 0x3F)) 00201 . chr(0x80 | ($bytes & 0x3F)); 00202 } 00203 00204 // ignoring UTF-32 for now, sorry 00205 return ''; 00206 } 00207 00219 function utf82utf16($utf8) 00220 { 00221 // oh please oh please oh please oh please oh please 00222 if(function_exists('mb_convert_encoding')) { 00223 return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8'); 00224 } 00225 00226 switch(strlen($utf8)) { 00227 case 1: 00228 // this case should never be reached, because we are in ASCII range 00229 // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 00230 return $utf8; 00231 00232 case 2: 00233 // return a UTF-16 character from a 2-byte UTF-8 char 00234 // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 00235 return chr(0x07 & (ord($utf8[0]) >> 2)) 00236 . chr((0xC0 & (ord($utf8[0]) << 6)) 00237 | (0x3F & ord($utf8[1]))); 00238 00239 case 3: 00240 // return a UTF-16 character from a 3-byte UTF-8 char 00241 // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 00242 return chr((0xF0 & (ord($utf8[0]) << 4)) 00243 | (0x0F & (ord($utf8[1]) >> 2))) 00244 . chr((0xC0 & (ord($utf8[1]) << 6)) 00245 | (0x7F & ord($utf8[2]))); 00246 00247 case 4: 00248 // return a UTF-16 surrogate pair from a 4-byte UTF-8 char 00249 if(ord($utf8[0]) > 0xF4) return ''; # invalid 00250 $char = ((0x1C0000 & (ord($utf8[0]) << 18)) 00251 | (0x03F000 & (ord($utf8[1]) << 12)) 00252 | (0x000FC0 & (ord($utf8[2]) << 6)) 00253 | (0x00003F & ord($utf8[3]))); 00254 if($char > 0x10FFFF) return ''; # invalid 00255 $char -= 0x10000; 00256 return chr(0xD8 | (($char >> 18) & 0x03)) 00257 . chr(($char >> 10) & 0xFF) 00258 . chr(0xDC | (($char >> 8) & 0x03)) 00259 . chr($char & 0xFF); 00260 } 00261 00262 // ignoring UTF-32 for now, sorry 00263 return ''; 00264 } 00265 00278 function encode($var, $pretty=false) 00279 { 00280 $this->indent = 0; 00281 $this->pretty = $pretty; 00282 $this->nameValSeparator = $pretty ? ': ' : ':'; 00283 return $this->encode2($var); 00284 } 00285 00297 function encode2($var) 00298 { 00299 if ($this->pretty) { 00300 $close = "\n" . str_repeat("\t", $this->indent); 00301 $open = $close . "\t"; 00302 $mid = ',' . $open; 00303 } 00304 else { 00305 $open = $close = ''; 00306 $mid = ','; 00307 } 00308 00309 switch (gettype($var)) { 00310 case 'boolean': 00311 return $var ? 'true' : 'false'; 00312 00313 case 'NULL': 00314 return 'null'; 00315 00316 case 'integer': 00317 return (int) $var; 00318 00319 case 'double': 00320 case 'float': 00321 return (float) $var; 00322 00323 case 'string': 00324 // STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT 00325 $ascii = ''; 00326 $strlen_var = strlen($var); 00327 00328 /* 00329 * Iterate over every character in the string, 00330 * escaping with a slash or encoding to UTF-8 where necessary 00331 */ 00332 for ($c = 0; $c < $strlen_var; ++$c) { 00333 00334 $ord_var_c = ord($var[$c]); 00335 00336 switch (true) { 00337 case $ord_var_c == 0x08: 00338 $ascii .= '\b'; 00339 break; 00340 case $ord_var_c == 0x09: 00341 $ascii .= '\t'; 00342 break; 00343 case $ord_var_c == 0x0A: 00344 $ascii .= '\n'; 00345 break; 00346 case $ord_var_c == 0x0C: 00347 $ascii .= '\f'; 00348 break; 00349 case $ord_var_c == 0x0D: 00350 $ascii .= '\r'; 00351 break; 00352 00353 case $ord_var_c == 0x22: 00354 case $ord_var_c == 0x2F: 00355 case $ord_var_c == 0x5C: 00356 // double quote, slash, slosh 00357 $ascii .= '\\'.$var[$c]; 00358 break; 00359 00360 case (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)): 00361 // characters U-00000000 - U-0000007F (same as ASCII) 00362 $ascii .= $var[$c]; 00363 break; 00364 00365 case (($ord_var_c & 0xE0) == 0xC0): 00366 // characters U-00000080 - U-000007FF, mask 110XXXXX 00367 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 00368 $char = pack('C*', $ord_var_c, ord($var[$c + 1])); 00369 $c += 1; 00370 $utf16 = $this->utf82utf16($char); 00371 $ascii .= sprintf('\u%04s', bin2hex($utf16)); 00372 break; 00373 00374 case (($ord_var_c & 0xF0) == 0xE0): 00375 // characters U-00000800 - U-0000FFFF, mask 1110XXXX 00376 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 00377 $char = pack('C*', $ord_var_c, 00378 ord($var[$c + 1]), 00379 ord($var[$c + 2])); 00380 $c += 2; 00381 $utf16 = $this->utf82utf16($char); 00382 $ascii .= sprintf('\u%04s', bin2hex($utf16)); 00383 break; 00384 00385 case (($ord_var_c & 0xF8) == 0xF0): 00386 // characters U-00010000 - U-001FFFFF, mask 11110XXX 00387 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 00388 // These will always return a surrogate pair 00389 $char = pack('C*', $ord_var_c, 00390 ord($var[$c + 1]), 00391 ord($var[$c + 2]), 00392 ord($var[$c + 3])); 00393 $c += 3; 00394 $utf16 = $this->utf82utf16($char); 00395 if($utf16 == '') { 00396 $ascii .= '\ufffd'; 00397 } else { 00398 $utf16 = str_split($utf16, 2); 00399 $ascii .= sprintf('\u%04s\u%04s', bin2hex($utf16[0]), bin2hex($utf16[1])); 00400 } 00401 break; 00402 } 00403 } 00404 00405 return '"'.$ascii.'"'; 00406 00407 case 'array': 00408 /* 00409 * As per JSON spec if any array key is not an integer 00410 * we must treat the the whole array as an object. We 00411 * also try to catch a sparsely populated associative 00412 * array with numeric keys here because some JS engines 00413 * will create an array with empty indexes up to 00414 * max_index which can cause memory issues and because 00415 * the keys, which may be relevant, will be remapped 00416 * otherwise. 00417 * 00418 * As per the ECMA and JSON specification an object may 00419 * have any string as a property. Unfortunately due to 00420 * a hole in the ECMA specification if the key is a 00421 * ECMA reserved word or starts with a digit the 00422 * parameter is only accessible using ECMAScript's 00423 * bracket notation. 00424 */ 00425 00426 // treat as a JSON object 00427 if (is_array($var) && count($var) && (array_keys($var) !== range(0, sizeof($var) - 1))) { 00428 $this->indent++; 00429 $properties = array_map(array($this, 'name_value'), 00430 array_keys($var), 00431 array_values($var)); 00432 $this->indent--; 00433 00434 foreach($properties as $property) { 00435 if($this->isError($property)) { 00436 return $property; 00437 } 00438 } 00439 00440 return '{' . $open . join($mid, $properties) . $close . '}'; 00441 } 00442 00443 // treat it like a regular array 00444 $this->indent++; 00445 $elements = array_map(array($this, 'encode2'), $var); 00446 $this->indent--; 00447 00448 foreach($elements as $element) { 00449 if($this->isError($element)) { 00450 return $element; 00451 } 00452 } 00453 00454 return '[' . $open . join($mid, $elements) . $close . ']'; 00455 00456 case 'object': 00457 $vars = get_object_vars($var); 00458 00459 $this->indent++; 00460 $properties = array_map(array($this, 'name_value'), 00461 array_keys($vars), 00462 array_values($vars)); 00463 $this->indent--; 00464 00465 foreach($properties as $property) { 00466 if($this->isError($property)) { 00467 return $property; 00468 } 00469 } 00470 00471 return '{' . $open . join($mid, $properties) . $close . '}'; 00472 00473 default: 00474 return ($this->use & SERVICES_JSON_SUPPRESS_ERRORS) 00475 ? 'null' 00476 : new Services_JSON_Error(gettype($var)." can not be encoded as JSON string"); 00477 } 00478 } 00479 00489 function name_value($name, $value) 00490 { 00491 $encoded_value = $this->encode2($value); 00492 00493 if($this->isError($encoded_value)) { 00494 return $encoded_value; 00495 } 00496 00497 return $this->encode2(strval($name)) . $this->nameValSeparator . $encoded_value; 00498 } 00499 00508 function reduce_string($str) 00509 { 00510 $str = preg_replace(array( 00511 00512 // eliminate single line comments in '// ...' form 00513 '#^\s*//(.+)$#m', 00514 00515 // eliminate multi-line comments in '/* ... */' form, at start of string 00516 '#^\s*/\*(.+)\*/#Us', 00517 00518 // eliminate multi-line comments in '/* ... */' form, at end of string 00519 '#/\*(.+)\*/\s*$#Us' 00520 00521 ), '', $str); 00522 00523 // eliminate extraneous space 00524 return trim($str); 00525 } 00526 00539 function decode($str) 00540 { 00541 $str = $this->reduce_string($str); 00542 00543 switch (strtolower($str)) { 00544 case 'true': 00545 return true; 00546 00547 case 'false': 00548 return false; 00549 00550 case 'null': 00551 return null; 00552 00553 default: 00554 $m = array(); 00555 00556 if (is_numeric($str)) { 00557 // Lookie-loo, it's a number 00558 00559 // This would work on its own, but I'm trying to be 00560 // good about returning integers where appropriate: 00561 // return (float)$str; 00562 00563 // Return float or int, as appropriate 00564 return ((float)$str == (integer)$str) 00565 ? (integer)$str 00566 : (float)$str; 00567 00568 } elseif (preg_match('/^("|\').*(\1)$/s', $str, $m) && $m[1] == $m[2]) { 00569 // STRINGS RETURNED IN UTF-8 FORMAT 00570 $delim = substr($str, 0, 1); 00571 $chrs = substr($str, 1, -1); 00572 $utf8 = ''; 00573 $strlen_chrs = strlen($chrs); 00574 00575 for ($c = 0; $c < $strlen_chrs; ++$c) { 00576 00577 $substr_chrs_c_2 = substr($chrs, $c, 2); 00578 $ord_chrs_c = ord($chrs[$c]); 00579 00580 switch (true) { 00581 case $substr_chrs_c_2 == '\b': 00582 $utf8 .= chr(0x08); 00583 ++$c; 00584 break; 00585 case $substr_chrs_c_2 == '\t': 00586 $utf8 .= chr(0x09); 00587 ++$c; 00588 break; 00589 case $substr_chrs_c_2 == '\n': 00590 $utf8 .= chr(0x0A); 00591 ++$c; 00592 break; 00593 case $substr_chrs_c_2 == '\f': 00594 $utf8 .= chr(0x0C); 00595 ++$c; 00596 break; 00597 case $substr_chrs_c_2 == '\r': 00598 $utf8 .= chr(0x0D); 00599 ++$c; 00600 break; 00601 00602 case $substr_chrs_c_2 == '\\"': 00603 case $substr_chrs_c_2 == '\\\'': 00604 case $substr_chrs_c_2 == '\\\\': 00605 case $substr_chrs_c_2 == '\\/': 00606 if (($delim == '"' && $substr_chrs_c_2 != '\\\'') || 00607 ($delim == "'" && $substr_chrs_c_2 != '\\"')) { 00608 $utf8 .= $chrs[++$c]; 00609 } 00610 break; 00611 00612 case preg_match('/\\\uD[89AB][0-9A-F]{2}\\\uD[C-F][0-9A-F]{2}/i', substr($chrs, $c, 12)): 00613 // escaped unicode surrogate pair 00614 $utf16 = chr(hexdec(substr($chrs, ($c + 2), 2))) 00615 . chr(hexdec(substr($chrs, ($c + 4), 2))) 00616 . chr(hexdec(substr($chrs, ($c + 8), 2))) 00617 . chr(hexdec(substr($chrs, ($c + 10), 2))); 00618 $utf8 .= $this->utf162utf8($utf16); 00619 $c += 11; 00620 break; 00621 00622 case preg_match('/\\\u[0-9A-F]{4}/i', substr($chrs, $c, 6)): 00623 // single, escaped unicode character 00624 $utf16 = chr(hexdec(substr($chrs, ($c + 2), 2))) 00625 . chr(hexdec(substr($chrs, ($c + 4), 2))); 00626 $utf8 .= $this->utf162utf8($utf16); 00627 $c += 5; 00628 break; 00629 00630 case ($ord_chrs_c >= 0x20) && ($ord_chrs_c <= 0x7F): 00631 $utf8 .= $chrs[$c]; 00632 break; 00633 00634 case ($ord_chrs_c & 0xE0) == 0xC0: 00635 // characters U-00000080 - U-000007FF, mask 110XXXXX 00636 //see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 00637 $utf8 .= substr($chrs, $c, 2); 00638 ++$c; 00639 break; 00640 00641 case ($ord_chrs_c & 0xF0) == 0xE0: 00642 // characters U-00000800 - U-0000FFFF, mask 1110XXXX 00643 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 00644 $utf8 .= substr($chrs, $c, 3); 00645 $c += 2; 00646 break; 00647 00648 case ($ord_chrs_c & 0xF8) == 0xF0: 00649 // characters U-00010000 - U-001FFFFF, mask 11110XXX 00650 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 00651 $utf8 .= substr($chrs, $c, 4); 00652 $c += 3; 00653 break; 00654 00655 case ($ord_chrs_c & 0xFC) == 0xF8: 00656 // characters U-00200000 - U-03FFFFFF, mask 111110XX 00657 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 00658 $utf8 .= substr($chrs, $c, 5); 00659 $c += 4; 00660 break; 00661 00662 case ($ord_chrs_c & 0xFE) == 0xFC: 00663 // characters U-04000000 - U-7FFFFFFF, mask 1111110X 00664 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 00665 $utf8 .= substr($chrs, $c, 6); 00666 $c += 5; 00667 break; 00668 00669 } 00670 00671 } 00672 00673 return $utf8; 00674 00675 } elseif (preg_match('/^\[.*\]$/s', $str) || preg_match('/^\{.*\}$/s', $str)) { 00676 // array, or object notation 00677 00678 if ($str[0] == '[') { 00679 $stk = array(SERVICES_JSON_IN_ARR); 00680 $arr = array(); 00681 } else { 00682 if ($this->use & SERVICES_JSON_LOOSE_TYPE) { 00683 $stk = array(SERVICES_JSON_IN_OBJ); 00684 $obj = array(); 00685 } else { 00686 $stk = array(SERVICES_JSON_IN_OBJ); 00687 $obj = new stdClass(); 00688 } 00689 } 00690 00691 array_push($stk, array( 'what' => SERVICES_JSON_SLICE, 00692 'where' => 0, 00693 'delim' => false)); 00694 00695 $chrs = substr($str, 1, -1); 00696 $chrs = $this->reduce_string($chrs); 00697 00698 if ($chrs == '') { 00699 if (reset($stk) == SERVICES_JSON_IN_ARR) { 00700 return $arr; 00701 00702 } else { 00703 return $obj; 00704 00705 } 00706 } 00707 00708 //print("\nparsing {$chrs}\n"); 00709 00710 $strlen_chrs = strlen($chrs); 00711 00712 for ($c = 0; $c <= $strlen_chrs; ++$c) { 00713 00714 $top = end($stk); 00715 $substr_chrs_c_2 = substr($chrs, $c, 2); 00716 00717 if (($c == $strlen_chrs) || (($chrs[$c] == ',') && ($top['what'] == SERVICES_JSON_SLICE))) { 00718 // found a comma that is not inside a string, array, etc., 00719 // OR we've reached the end of the character list 00720 $slice = substr($chrs, $top['where'], ($c - $top['where'])); 00721 array_push($stk, array('what' => SERVICES_JSON_SLICE, 'where' => ($c + 1), 'delim' => false)); 00722 //print("Found split at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n"); 00723 00724 if (reset($stk) == SERVICES_JSON_IN_ARR) { 00725 // we are in an array, so just push an element onto the stack 00726 array_push($arr, $this->decode($slice)); 00727 00728 } elseif (reset($stk) == SERVICES_JSON_IN_OBJ) { 00729 // we are in an object, so figure 00730 // out the property name and set an 00731 // element in an associative array, 00732 // for now 00733 $parts = array(); 00734 00735 if (preg_match('/^\s*(["\'].*[^\\\]["\'])\s*:\s*(\S.*),?$/Uis', $slice, $parts)) { 00736 // "name":value pair 00737 $key = $this->decode($parts[1]); 00738 $val = $this->decode($parts[2]); 00739 00740 if ($this->use & SERVICES_JSON_LOOSE_TYPE) { 00741 $obj[$key] = $val; 00742 } else { 00743 $obj->$key = $val; 00744 } 00745 } elseif (preg_match('/^\s*(\w+)\s*:\s*(\S.*),?$/Uis', $slice, $parts)) { 00746 // name:value pair, where name is unquoted 00747 $key = $parts[1]; 00748 $val = $this->decode($parts[2]); 00749 00750 if ($this->use & SERVICES_JSON_LOOSE_TYPE) { 00751 $obj[$key] = $val; 00752 } else { 00753 $obj->$key = $val; 00754 } 00755 } 00756 00757 } 00758 00759 } elseif ((($chrs[$c] == '"') || ($chrs[$c] == "'")) && ($top['what'] != SERVICES_JSON_IN_STR)) { 00760 // found a quote, and we are not inside a string 00761 array_push($stk, array('what' => SERVICES_JSON_IN_STR, 'where' => $c, 'delim' => $chrs[$c])); 00762 //print("Found start of string at {$c}\n"); 00763 00764 } elseif (($chrs[$c] == $top['delim']) && 00765 ($top['what'] == SERVICES_JSON_IN_STR) && 00766 (($chrs[$c - 1] != '\\') || 00767 ($chrs[$c - 1] == '\\' && $chrs[$c - 2] == '\\'))) { 00768 // found a quote, we're in a string, and it's not escaped 00769 array_pop($stk); 00770 //print("Found end of string at {$c}: ".substr($chrs, $top['where'], (1 + 1 + $c - $top['where']))."\n"); 00771 00772 } elseif (($chrs[$c] == '[') && 00773 in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) { 00774 // found a left-bracket, and we are in an array, object, or slice 00775 array_push($stk, array('what' => SERVICES_JSON_IN_ARR, 'where' => $c, 'delim' => false)); 00776 //print("Found start of array at {$c}\n"); 00777 00778 } elseif (($chrs[$c] == ']') && ($top['what'] == SERVICES_JSON_IN_ARR)) { 00779 // found a right-bracket, and we're in an array 00780 array_pop($stk); 00781 //print("Found end of array at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n"); 00782 00783 } elseif (($chrs[$c] == '{') && 00784 in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) { 00785 // found a left-brace, and we are in an array, object, or slice 00786 array_push($stk, array('what' => SERVICES_JSON_IN_OBJ, 'where' => $c, 'delim' => false)); 00787 //print("Found start of object at {$c}\n"); 00788 00789 } elseif (($chrs[$c] == '}') && ($top['what'] == SERVICES_JSON_IN_OBJ)) { 00790 // found a right-brace, and we're in an object 00791 array_pop($stk); 00792 //print("Found end of object at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n"); 00793 00794 } elseif (($substr_chrs_c_2 == '/*') && 00795 in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) { 00796 // found a comment start, and we are in an array, object, or slice 00797 array_push($stk, array('what' => SERVICES_JSON_IN_CMT, 'where' => $c, 'delim' => false)); 00798 $c++; 00799 //print("Found start of comment at {$c}\n"); 00800 00801 } elseif (($substr_chrs_c_2 == '*/') && ($top['what'] == SERVICES_JSON_IN_CMT)) { 00802 // found a comment end, and we're in one now 00803 array_pop($stk); 00804 $c++; 00805 00806 for ($i = $top['where']; $i <= $c; ++$i) 00807 $chrs = substr_replace($chrs, ' ', $i, 1); 00808 00809 //print("Found end of comment at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n"); 00810 00811 } 00812 00813 } 00814 00815 if (reset($stk) == SERVICES_JSON_IN_ARR) { 00816 return $arr; 00817 00818 } elseif (reset($stk) == SERVICES_JSON_IN_OBJ) { 00819 return $obj; 00820 00821 } 00822 00823 } 00824 } 00825 } 00826 00830 function isError($data, $code = null) 00831 { 00832 if ( self::pearInstalled() ) { 00833 //avoid some strict warnings on PEAR isError check (looks like http://pear.php.net/bugs/bug.php?id=9950 has been around for some time) 00834 return @PEAR::isError($data, $code); 00835 } elseif (is_object($data) && (get_class($data) == 'services_json_error' || 00836 is_subclass_of($data, 'services_json_error'))) { 00837 return true; 00838 } 00839 00840 return false; 00841 } 00842 } 00843 00844 00845 // Hide the PEAR_Error variant from Doxygen 00847 if (class_exists('PEAR_Error')) { 00848 00852 class Services_JSON_Error extends PEAR_Error 00853 { 00854 function Services_JSON_Error($message = 'unknown error', $code = null, 00855 $mode = null, $options = null, $userinfo = null) 00856 { 00857 parent::PEAR_Error($message, $code, $mode, $options, $userinfo); 00858 } 00859 } 00860 00861 } else { 00863 00868 class Services_JSON_Error 00869 { 00870 function Services_JSON_Error($message = 'unknown error', $code = null, 00871 $mode = null, $options = null, $userinfo = null) 00872 { 00873 $this->message = $message; 00874 } 00875 00876 function __toString() 00877 { 00878 return $this->message; 00879 } 00880 } 00881 }