kenken999/php
0
1<?php2/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */3 4/**5* Converts to and from JSON format.6*7* JSON (JavaScript Object Notation) is a lightweight data-interchange8* format. It is easy for humans to read and write. It is easy for machines9* to parse and generate. It is based on a subset of the JavaScript10* Programming Language, Standard ECMA-262 3rd Edition - December 1999.11* This feature can also be found in Python. JSON is a text format that is12* completely language independent but uses conventions that are familiar13* to programmers of the C-family of languages, including C, C++, C#, Java,14* JavaScript, Perl, TCL, and many others. These properties make JSON an15* ideal data-interchange language.16*17* This package provides a simple encoder and decoder for JSON notation. It18* is intended for use with client-side Javascript applications that make19* use of HTTPRequest to perform server communication functions - data can20* be encoded into JSON notation for use in a client-side javascript, or21* decoded from incoming Javascript requests. JSON format is native to22* Javascript, and can be directly eval()'ed with no further parsing23* overhead24*25* All strings should be in ASCII or UTF-8 format!26*27* LICENSE: Redistribution and use in source and binary forms, with or28* without modification, are permitted provided that the following29* conditions are met: Redistributions of source code must retain the30* above copyright notice, this list of conditions and the following31* disclaimer. Redistributions in binary form must reproduce the above32* copyright notice, this list of conditions and the following disclaimer33* in the documentation and/or other materials provided with the34* distribution.35*36* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED37* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF38* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN39* NO EVENT SHALL CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,40* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,41* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS42* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND43* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR44* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE45* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH46* DAMAGE.47*48* @category49* @package Services_JSON50* @author Michal Migurski <mike-json@teczno.com>51* @author Matt Knapp <mdknapp[at]gmail[dot]com>52* @author Brett Stimmerman <brettstimmerman[at]gmail[dot]com>53* @copyright 2005 Michal Migurski54* @version CVS: $Id: JSON.php,v 1.31 2006/06/28 05:54:17 migurski Exp $55* @license http://www.opensource.org/licenses/bsd-license.php56* @link http://pear.php.net/pepr/pepr-proposal-show.php?id=19857*/58 59/**60* Marker constant for Services_JSON::decode(), used to flag stack state61*/62define('SERVICES_JSON_SLICE', 1);63 64/**65* Marker constant for Services_JSON::decode(), used to flag stack state66*/67define('SERVICES_JSON_IN_STR', 2);68 69/**70* Marker constant for Services_JSON::decode(), used to flag stack state71*/72define('SERVICES_JSON_IN_ARR', 3);73 74/**75* Marker constant for Services_JSON::decode(), used to flag stack state76*/77define('SERVICES_JSON_IN_OBJ', 4);78 79/**80* Marker constant for Services_JSON::decode(), used to flag stack state81*/82define('SERVICES_JSON_IN_CMT', 5);83 84/**85* Behavior switch for Services_JSON::decode()86*/87define('SERVICES_JSON_LOOSE_TYPE', 16);88 89/**90* Behavior switch for Services_JSON::decode()91*/92define('SERVICES_JSON_SUPPRESS_ERRORS', 32);93 94/**95* Converts to and from JSON format.96*97* Brief example of use:98*99* <code>100* // create a new instance of Services_JSON101* $json = new Services_JSON();102*103* // convert a complexe value to JSON notation, and send it to the browser104* $value = array('foo', 'bar', array(1, 2, 'baz'), array(3, array(4)));105* $output = $json->encode($value);106*107* print($output);108* // prints: ["foo","bar",[1,2,"baz"],[3,[4]]]109*110* // accept incoming POST data, assumed to be in JSON notation111* $input = file_get_contents('php://input', 1000000);112* $value = $json->decode($input);113* </code>114*/115class Services_JSON116{117 /**118 * We suppose that data in project and in database are in the same encoding, 119 * so we can encode data in different encoding, and we use this property to prevent convertion from 120 * utf-8 when data not in utf-8 encoding 121 *122 * @var bool123 */124 var $isUtf8 = true;125 126 /**127 * constructs a new JSON instance128 *129 * @param int $use object behavior flags; combine with boolean-OR130 *131 * possible values:132 * - SERVICES_JSON_LOOSE_TYPE: loose typing.133 * "{...}" syntax creates associative arrays134 * instead of objects in decode().135 * - SERVICES_JSON_SUPPRESS_ERRORS: error suppression.136 * Values which can't be encoded (e.g. resources)137 * appear as NULL instead of throwing errors.138 * By default, a deeply-nested resource will139 * bubble up with an error, so all return values140 * from encode() should be checked with isError()141 */142 function __construct($use = 0, $isUtf8 = true)143 {144 $this->use = $use;145 $this->isUtf8 = $isUtf8;146 }147 148 /**149 * convert a string from one UTF-16 char to one UTF-8 char150 *151 * Normally should be handled by mb_convert_encoding, but152 * provides a slower PHP-only method for installations153 * that lack the multibye string extension.154 *155 * @param string $utf16 UTF-16 character156 * @return string UTF-8 character157 * @access private158 */159 function utf162utf8($utf16)160 {161 // oh please oh please oh please oh please oh please162 if(function_exists('mb_convert_encoding')) {163 return mb_convert_encoding($utf16, 'UTF-8', 'UTF-16');164 }165 166 $bytes = (ord($utf16[0]) << 8) | ord($utf16[1]);167 168 switch(true) {169 case ((0x7F & $bytes) == $bytes):170 // this case should never be reached, because we are in ASCII range171 // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8172 return chr(0x7F & $bytes);173 174 case (0x07FF & $bytes) == $bytes:175 // return a 2-byte UTF-8 character176 // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8177 return chr(0xC0 | (($bytes >> 6) & 0x1F))178 . chr(0x80 | ($bytes & 0x3F));179 180 case (0xFFFF & $bytes) == $bytes:181 // return a 3-byte UTF-8 character182 // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8183 return chr(0xE0 | (($bytes >> 12) & 0x0F))184 . chr(0x80 | (($bytes >> 6) & 0x3F))185 . chr(0x80 | ($bytes & 0x3F));186 }187 188 // ignoring UTF-32 for now, sorry189 return '';190 }191 192 /**193 * convert a string from one UTF-8 char to one UTF-16 char194 *195 * Normally should be handled by mb_convert_encoding, but196 * provides a slower PHP-only method for installations197 * that lack the multibye string extension.198 *199 * @param string $utf8 UTF-8 character200 * @return string UTF-16 character201 * @access private202 */203 function utf82utf16($utf8)204 {205 // oh please oh please oh please oh please oh please206 if(function_exists('mb_convert_encoding')) {207 return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8');208 }209 210 switch(strlen($utf8)) {211 case 1:212 // this case should never be reached, because we are in ASCII range213 // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8214 return $utf8;215 216 case 2:217 // return a UTF-16 character from a 2-byte UTF-8 char218 // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8219 return chr(0x07 & (ord($utf8[0]) >> 2))220 . chr((0xC0 & (ord($utf8[0]) << 6))221 | (0x3F & ord($utf8[1])));222 223 case 3:224 // return a UTF-16 character from a 3-byte UTF-8 char225 // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8226 return chr((0xF0 & (ord($utf8[0]) << 4))227 | (0x0F & (ord($utf8[1]) >> 2)))228 . chr((0xC0 & (ord($utf8[1]) << 6))229 | (0x7F & ord($utf8[2])));230 }231 232 // ignoring UTF-32 for now, sorry233 return '';234 }235 236 /**237 * encodes an arbitrary variable into JSON format238 *239 * @param mixed $var any number, boolean, string, array, or object to be encoded.240 * see argument 1 to Services_JSON() above for array-parsing behavior.241 * if var is a strng, note that encode() always expects it242 * to be in ASCII or UTF-8 format!243 *244 * @return mixed JSON string representation of input var or an error if a problem occurs245 * @access public246 */247 function encode($var)248 {249 switch (gettype($var)) {250 case 'boolean':251 return $var ? 'true' : 'false';252 253 case 'NULL':254 return 'null';255 256 case 'integer':257 return (int) $var;258 259 case 'double':260 case 'float':261 return (float) $var;262 263 case 'string':264 // STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT265 $ascii = '';266 $strlen_var = strlen($var);267 268 /*269 * Iterate over every character in the string,270 * escaping with a slash or encoding to UTF-8 where necessary271 */272 for ($c = 0; $c < $strlen_var; ++$c) {273 274 $ord_var_c = ord($var[$c]);275 276 switch (true) {277 case $ord_var_c == 0x08:278 $ascii .= '\b';279 break;280 case $ord_var_c == 0x09:281 $ascii .= '\t';282 break;283 case $ord_var_c == 0x0A:284 $ascii .= '\n';285 break;286 case $ord_var_c == 0x0C:287 $ascii .= '\f';288 break;289 case $ord_var_c == 0x0D:290 $ascii .= '\r';291 break;292 293 case $ord_var_c == 0x22:294 case $ord_var_c == 0x2F:295 case $ord_var_c == 0x5C:296 // double quote, slash, slosh297 $ascii .= '\\'.$var[$c];298 break;299 300 case !$this->isUtf8 || (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)):301 // characters U-00000000 - U-0000007F (same as ASCII)302 $ascii .= $var[$c];303 break;304 305 case (($ord_var_c & 0xE0) == 0xC0):306 // characters U-00000080 - U-000007FF, mask 110XXXXX307 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8308 $char = pack('C*', $ord_var_c, ord($var[$c + 1]));309 $c += 1;310 $utf16 = $this->utf82utf16($char);311 $ascii .= sprintf('\u%04s', bin2hex($utf16));312 break;313 314 case (($ord_var_c & 0xF0) == 0xE0):315 // characters U-00000800 - U-0000FFFF, mask 1110XXXX316 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8317 $char = pack('C*', $ord_var_c,318 ord($var[$c + 1]),319 ord($var[$c + 2]));320 $c += 2;321 $utf16 = $this->utf82utf16($char);322 $ascii .= sprintf('\u%04s', bin2hex($utf16));323 break;324 325 case (($ord_var_c & 0xF8) == 0xF0):326 // characters U-00010000 - U-001FFFFF, mask 11110XXX327 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8328 $char = pack('C*', $ord_var_c,329 ord($var[$c + 1]),330 ord($var[$c + 2]),331 ord($var[$c + 3]));332 $c += 3;333 $utf16 = $this->utf82utf16($char);334 $ascii .= sprintf('\u%04s', bin2hex($utf16));335 break;336 337 case (($ord_var_c & 0xFC) == 0xF8):338 // characters U-00200000 - U-03FFFFFF, mask 111110XX339 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8340 $char = pack('C*', $ord_var_c,341 ord($var[$c + 1]),342 ord($var[$c + 2]),343 ord($var[$c + 3]),344 ord($var[$c + 4]));345 $c += 4;346 $utf16 = $this->utf82utf16($char);347 $ascii .= sprintf('\u%04s', bin2hex($utf16));348 break;349 350 case (($ord_var_c & 0xFE) == 0xFC):351 // characters U-04000000 - U-7FFFFFFF, mask 1111110X352 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8353 $char = pack('C*', $ord_var_c,354 ord($var[$c + 1]),355 ord($var[$c + 2]),356 ord($var[$c + 3]),357 ord($var[$c + 4]),358 ord($var[$c + 5]));359 $c += 5;360 $utf16 = $this->utf82utf16($char);361 $ascii .= sprintf('\u%04s', bin2hex($utf16));362 break;363 }364 }365 366 return '"'.$ascii.'"';367 368 case 'array':369 /*370 * As per JSON spec if any array key is not an integer371 * we must treat the the whole array as an object. We372 * also try to catch a sparsely populated associative373 * array with numeric keys here because some JS engines374 * will create an array with empty indexes up to375 * max_index which can cause memory issues and because376 * the keys, which may be relevant, will be remapped377 * otherwise.378 *379 * As per the ECMA and JSON specification an object may380 * have any string as a property. Unfortunately due to381 * a hole in the ECMA specification if the key is a382 * ECMA reserved word or starts with a digit the383 * parameter is only accessible using ECMAScript's384 * bracket notation.385 */386 387 // treat as a JSON object388 if (is_array($var) && count($var) && (array_keys($var) !== range(0, sizeof($var) - 1))) {389 $properties = array_map(array($this, 'name_value'),390 array_keys($var),391 array_values($var));392 393 foreach($properties as $property) {394 if(Services_JSON::isError($property)) {395 return $property;396 }397 }398 399 return '{' . join(',', $properties) . '}';400 }401 402 // treat it like a regular array403 $elements = array_map(array($this, 'encode'), $var);404 405 foreach($elements as $element) {406 if(Services_JSON::isError($element)) {407 return $element;408 }409 }410 411 return '[' . join(',', $elements) . ']';412 413 case 'object':414 $vars = get_object_vars($var);415 416 $properties = array_map(array($this, 'name_value'),417 array_keys($vars),418 array_values($vars));419 420 foreach($properties as $property) {421 if(Services_JSON::isError($property)) {422 return $property;423 }424 }425 426 return '{' . join(',', $properties) . '}';427 428 default:429 return ($this->use & SERVICES_JSON_SUPPRESS_ERRORS)430 ? 'null'431 : new Services_JSON_Error(gettype($var)." can not be encoded as JSON string");432 }433 }434 435 /**436 * array-walking function for use in generating JSON-formatted name-value pairs437 *438 * @param string $name name of key to use439 * @param mixed $value reference to an array element to be encoded440 *441 * @return string JSON-formatted name-value pair, like '"name":value'442 * @access private443 */444 function name_value($name, $value)445 {446 $encoded_value = $this->encode($value);447 448 if(Services_JSON::isError($encoded_value)) {449 return $encoded_value;450 }451 452 return $this->encode(strval($name)) . ':' . $encoded_value;453 }454 455 /**456 * reduce a string by removing leading and trailing comments and whitespace457 *458 * @param $str string string value to strip of comments and whitespace459 *460 * @return string string value stripped of comments and whitespace461 * @access private462 */463 function reduce_string($str)464 {465 $str = preg_replace(array(466 467 // eliminate single line comments in '// ...' form468 '#^\s*//(.+)$#m',469 470 // eliminate multi-line comments in '/* ... */' form, at start of string471 '#^\s*/\*(.+)\*/#Us',472 473 // eliminate multi-line comments in '/* ... */' form, at end of string474 '#/\*(.+)\*/\s*$#Us'475 476 ), '', $str);477 478 // eliminate extraneous space479 return trim($str);480 }481 482 /**483 * decodes a JSON string into appropriate variable484 *485 * @param string $str JSON-formatted string486 *487 * @return mixed number, boolean, string, array, or object488 * corresponding to given JSON input string.489 * See argument 1 to Services_JSON() above for object-output behavior.490 * Note that decode() always returns strings491 * in ASCII or UTF-8 format!492 * @access public493 */494 function decode($str)495 {496 $str = $this->reduce_string($str);497 498 switch (strtolower($str)) {499 case 'true':500 return true;501 502 case 'false':503 return false;504 505 case 'null':506 return null;507 508 default:509 $m = array();510 511 if (is_numeric($str)) {512 // Lookie-loo, it's a number513 514 // This would work on its own, but I'm trying to be515 // good about returning integers where appropriate:516 // return (float)$str;517 518 // Return float or int, as appropriate519 return ((float)$str == (integer)$str)520 ? (integer)$str521 : (float)$str;522 523 } elseif (preg_match('/^("|\').*(\1)$/s', $str, $m) && $m[1] == $m[2]) {524 // STRINGS RETURNED IN UTF-8 FORMAT525 $delim = substr($str, 0, 1);526 $chrs = substr($str, 1, -1);527 $utf8 = '';528 $strlen_chrs = strlen($chrs);529 530 for ($c = 0; $c < $strlen_chrs; ++$c) {531 532 $substr_chrs_c_2 = substr($chrs, $c, 2);533 $ord_chrs_c = ord($chrs[$c]);534 535 switch (true) {536 case $substr_chrs_c_2 == '\b':537 $utf8 .= chr(0x08);538 ++$c;539 break;540 case $substr_chrs_c_2 == '\t':541 $utf8 .= chr(0x09);542 ++$c;543 break;544 case $substr_chrs_c_2 == '\n':545 $utf8 .= chr(0x0A);546 ++$c;547 break;548 case $substr_chrs_c_2 == '\f':549 $utf8 .= chr(0x0C);550 ++$c;551 break;552 case $substr_chrs_c_2 == '\r':553 $utf8 .= chr(0x0D);554 ++$c;555 break;556 557 case $substr_chrs_c_2 == '\\"':558 case $substr_chrs_c_2 == '\\\'':559 case $substr_chrs_c_2 == '\\\\':560 case $substr_chrs_c_2 == '\\/':561 if (($delim == '"' && $substr_chrs_c_2 != '\\\'') ||562 ($delim == "'" && $substr_chrs_c_2 != '\\"')) {563 $utf8 .= $chrs[++$c];564 }565 break;566 567 case $this->isUtf8 && preg_match('/\\\u[0-9A-F]{4}/i', substr($chrs, $c, 6)):568 // single, escaped unicode character569 $utf16 = chr(hexdec(substr($chrs, ($c + 2), 2)))570 . chr(hexdec(substr($chrs, ($c + 4), 2)));571 $utf8 .= $this->utf162utf8($utf16);572 $c += 5;573 break;574 575 case !$this->isUtf8 || ($ord_chrs_c >= 0x20) && ($ord_chrs_c <= 0x7F):576 $utf8 .= $chrs[$c];577 break;578 579 case ($ord_chrs_c & 0xE0) == 0xC0:580 // characters U-00000080 - U-000007FF, mask 110XXXXX581 //see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8582 $utf8 .= substr($chrs, $c, 2);583 ++$c;584 break;585 586 case ($ord_chrs_c & 0xF0) == 0xE0:587 // characters U-00000800 - U-0000FFFF, mask 1110XXXX588 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8589 $utf8 .= substr($chrs, $c, 3);590 $c += 2;591 break;592 593 case ($ord_chrs_c & 0xF8) == 0xF0:594 // characters U-00010000 - U-001FFFFF, mask 11110XXX595 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8596 $utf8 .= substr($chrs, $c, 4);597 $c += 3;598 break;599 600 case ($ord_chrs_c & 0xFC) == 0xF8:601 // characters U-00200000 - U-03FFFFFF, mask 111110XX602 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8603 $utf8 .= substr($chrs, $c, 5);604 $c += 4;605 break;606 607 case ($ord_chrs_c & 0xFE) == 0xFC:608 // characters U-04000000 - U-7FFFFFFF, mask 1111110X609 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8610 $utf8 .= substr($chrs, $c, 6);611 $c += 5;612 break;613 614 }615 616 }617 618 return $utf8;619 620 } elseif (preg_match('/^\[.*\]$/s', $str) || preg_match('/^\{.*\}$/s', $str)) {621 // array, or object notation622 623 if ($str[0] == '[') {624 $stk = array(SERVICES_JSON_IN_ARR);625 $arr = array();626 } else {627 if ($this->use & SERVICES_JSON_LOOSE_TYPE) {628 $stk = array(SERVICES_JSON_IN_OBJ);629 $obj = array();630 } else {631 $stk = array(SERVICES_JSON_IN_OBJ);632 $obj = new stdClass();633 }634 }635 636 array_push($stk, array('what' => SERVICES_JSON_SLICE,637 'where' => 0,638 'delim' => false));639 640 $chrs = substr($str, 1, -1);641 $chrs = $this->reduce_string($chrs);642 643 if ($chrs == '') {644 if (reset($stk) == SERVICES_JSON_IN_ARR) {645 return $arr;646 647 } else {648 return $obj;649 650 }651 }652 653 //print("\nparsing [$chrs]\n");654 655 $strlen_chrs = strlen($chrs);656 657 for ($c = 0; $c <= $strlen_chrs; ++$c) {658 659 $top = end($stk);660 $substr_chrs_c_2 = substr($chrs, $c, 2);661 662 if (($c == $strlen_chrs) || (($chrs[$c] == ',') && ($top['what'] == SERVICES_JSON_SLICE))) {663 // found a comma that is not inside a string, array, etc.,664 // OR we've reached the end of the character list665 $slice = substr($chrs, $top['where'], ($c - $top['where']));666 array_push($stk, array('what' => SERVICES_JSON_SLICE, 'where' => ($c + 1), 'delim' => false));667 //print("Found split at [$c]: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");668 669 if (reset($stk) == SERVICES_JSON_IN_ARR) {670 // we are in an array, so just push an element onto the stack671 array_push($arr, $this->decode($slice));672 673 } elseif (reset($stk) == SERVICES_JSON_IN_OBJ) {674 // we are in an object, so figure675 // out the property name and set an676 // element in an associative array,677 // for now678 $parts = array();679 680 if (preg_match('/^\s*(["\'].*[^\\\]["\'])\s*:\s*(\S.*),?$/Uis', $slice, $parts)) {681 // "name":value pair682 $key = $this->decode($parts[1]);683 $val = $this->decode($parts[2]);684 685 if ($this->use & SERVICES_JSON_LOOSE_TYPE) {686 $obj[$key] = $val;687 } else {688 $obj->$key = $val;689 }690 } elseif (preg_match('/^\s*(\w+)\s*:\s*(\S.*),?$/Uis', $slice, $parts)) {691 // name:value pair, where name is unquoted692 $key = $parts[1];693 $val = $this->decode($parts[2]);694 695 if ($this->use & SERVICES_JSON_LOOSE_TYPE) {696 $obj[$key] = $val;697 } else {698 $obj->$key = $val;699 }700 }701 702 }703 704 } elseif ((($chrs[$c] == '"') || ($chrs[$c] == "'")) && ($top['what'] != SERVICES_JSON_IN_STR)) {705 // found a quote, and we are not inside a string706 array_push($stk, array('what' => SERVICES_JSON_IN_STR, 'where' => $c, 'delim' => $chrs[$c]));707 //print("Found start of string at [$c]\n");708 709 } elseif (($chrs[$c] == $top['delim']) &&710 ($top['what'] == SERVICES_JSON_IN_STR) &&711 ((strlen(substr($chrs, 0, $c)) - strlen(rtrim(substr($chrs, 0, $c), '\\'))) % 2 != 1)) {712 // found a quote, we're in a string, and it's not escaped713 // we know that it's not escaped becase there is _not_ an714 // odd number of backslashes at the end of the string so far715 array_pop($stk);716 //print("Found end of string at [$c]: ".substr($chrs, $top['where'], (1 + 1 + $c - $top['where']))."\n");717 718 } elseif (($chrs[$c] == '[') &&719 in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {720 // found a left-bracket, and we are in an array, object, or slice721 array_push($stk, array('what' => SERVICES_JSON_IN_ARR, 'where' => $c, 'delim' => false));722 //print("Found start of array at [$c]\n");723 724 } elseif (($chrs[$c] == ']') && ($top['what'] == SERVICES_JSON_IN_ARR)) {725 // found a right-bracket, and we're in an array726 array_pop($stk);727 //print("Found end of array at [$c]: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");728 729 } elseif (($chrs[$c] == '{') &&730 in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {731 // found a left-brace, and we are in an array, object, or slice732 array_push($stk, array('what' => SERVICES_JSON_IN_OBJ, 'where' => $c, 'delim' => false));733 //print("Found start of object at [$c]\n");734 735 } elseif (($chrs[$c] == '}') && ($top['what'] == SERVICES_JSON_IN_OBJ)) {736 // found a right-brace, and we're in an object737 array_pop($stk);738 //print("Found end of object at [$c]: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");739 740 } elseif (($substr_chrs_c_2 == '/*') &&741 in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {742 // found a comment start, and we are in an array, object, or slice743 array_push($stk, array('what' => SERVICES_JSON_IN_CMT, 'where' => $c, 'delim' => false));744 $c++;745 //print("Found start of comment at [$c]\n");746 747 } elseif (($substr_chrs_c_2 == '*/') && ($top['what'] == SERVICES_JSON_IN_CMT)) {748 // found a comment end, and we're in one now749 array_pop($stk);750 $c++;751 752 for ($i = $top['where']; $i <= $c; ++$i)753 $chrs = substr_replace($chrs, ' ', $i, 1);754 755 //print("Found end of comment at [$c]: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");756 757 }758 759 }760 761 if (reset($stk) == SERVICES_JSON_IN_ARR) {762 return $arr;763 764 } elseif (reset($stk) == SERVICES_JSON_IN_OBJ) {765 return $obj;766 767 }768 769 }770 }771 }772 773 /**774 * @todo Ultimately, this should just call PEAR::isError()775 */776 function isError($data, $code = null)777 {778 if (class_exists('pear')) {779 return PEAR::isError($data, $code);780 } elseif (is_object($data) && (get_class($data) == 'services_json_error' ||781 is_subclass_of($data, 'services_json_error'))) {782 return true;783 }784 785 return false;786 }787}788 789if (class_exists('PEAR_Error')) {790 791 class Services_JSON_Error extends PEAR_Error792 {793 function __construct($message = 'unknown error', $code = null,794 $mode = null, $options = null, $userinfo = null)795 {796 parent::__construct($message, $code, $mode, $options, $userinfo);797 }798 }799 800} else {801 802 /**803 * @todo Ultimately, this class shall be descended from PEAR_Error804 */805 class Services_JSON_Error806 {807 function __construct($message = 'unknown error', $code = null,808 $mode = null, $options = null, $userinfo = null)809 {810 811 }812 }813 814}815 816?>