| [ Index ] |
PHP Cross Reference of Wordpress 2.9.1 |
[Summary view] [Print] [Text view]
1 <?php 2 /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ 3 /** 4 * Converts to and from JSON format. 5 * 6 * JSON (JavaScript Object Notation) is a lightweight data-interchange 7 * format. It is easy for humans to read and write. It is easy for machines 8 * to parse and generate. It is based on a subset of the JavaScript 9 * Programming Language, Standard ECMA-262 3rd Edition - December 1999. 10 * This feature can also be found in Python. JSON is a text format that is 11 * completely language independent but uses conventions that are familiar 12 * to programmers of the C-family of languages, including C, C++, C#, Java, 13 * JavaScript, Perl, TCL, and many others. These properties make JSON an 14 * ideal data-interchange language. 15 * 16 * This package provides a simple encoder and decoder for JSON notation. It 17 * is intended for use with client-side Javascript applications that make 18 * use of HTTPRequest to perform server communication functions - data can 19 * be encoded into JSON notation for use in a client-side javascript, or 20 * decoded from incoming Javascript requests. JSON format is native to 21 * Javascript, and can be directly eval()'ed with no further parsing 22 * overhead 23 * 24 * All strings should be in ASCII or UTF-8 format! 25 * 26 * LICENSE: Redistribution and use in source and binary forms, with or 27 * without modification, are permitted provided that the following 28 * conditions are met: Redistributions of source code must retain the 29 * above copyright notice, this list of conditions and the following 30 * disclaimer. Redistributions in binary form must reproduce the above 31 * copyright notice, this list of conditions and the following disclaimer 32 * in the documentation and/or other materials provided with the 33 * distribution. 34 * 35 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED 36 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 37 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN 38 * NO EVENT SHALL CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 39 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 40 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 41 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 42 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 43 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE 44 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 45 * DAMAGE. 46 * 47 * @category 48 * @package Services_JSON 49 * @author Michal Migurski <mike-json@teczno.com> 50 * @author Matt Knapp <mdknapp[at]gmail[dot]com> 51 * @author Brett Stimmerman <brettstimmerman[at]gmail[dot]com> 52 * @copyright 2005 Michal Migurski 53 * @version CVS: $Id: JSON.php 288200 2009-09-09 15:41:29Z alan_k $ 54 * @license http://www.opensource.org/licenses/bsd-license.php 55 * @link http://pear.php.net/pepr/pepr-proposal-show.php?id=198 56 */ 57 58 /** 59 * Marker constant for Services_JSON::decode(), used to flag stack state 60 */ 61 define('SERVICES_JSON_SLICE', 1); 62 63 /** 64 * Marker constant for Services_JSON::decode(), used to flag stack state 65 */ 66 define('SERVICES_JSON_IN_STR', 2); 67 68 /** 69 * Marker constant for Services_JSON::decode(), used to flag stack state 70 */ 71 define('SERVICES_JSON_IN_ARR', 3); 72 73 /** 74 * Marker constant for Services_JSON::decode(), used to flag stack state 75 */ 76 define('SERVICES_JSON_IN_OBJ', 4); 77 78 /** 79 * Marker constant for Services_JSON::decode(), used to flag stack state 80 */ 81 define('SERVICES_JSON_IN_CMT', 5); 82 83 /** 84 * Behavior switch for Services_JSON::decode() 85 */ 86 define('SERVICES_JSON_LOOSE_TYPE', 16); 87 88 /** 89 * Behavior switch for Services_JSON::decode() 90 */ 91 define('SERVICES_JSON_SUPPRESS_ERRORS', 32); 92 93 /** 94 * Converts to and from JSON format. 95 * 96 * Brief example of use: 97 * 98 * <code> 99 * // create a new instance of Services_JSON 100 * $json = new Services_JSON(); 101 * 102 * // convert a complexe value to JSON notation, and send it to the browser 103 * $value = array('foo', 'bar', array(1, 2, 'baz'), array(3, array(4))); 104 * $output = $json->encode($value); 105 * 106 * print($output); 107 * // prints: ["foo","bar",[1,2,"baz"],[3,[4]]] 108 * 109 * // accept incoming POST data, assumed to be in JSON notation 110 * $input = file_get_contents('php://input', 1000000); 111 * $value = $json->decode($input); 112 * </code> 113 */ 114 class Services_JSON 115 { 116 /** 117 * constructs a new JSON instance 118 * 119 * @param int $use object behavior flags; combine with boolean-OR 120 * 121 * possible values: 122 * - SERVICES_JSON_LOOSE_TYPE: loose typing. 123 * "{...}" syntax creates associative arrays 124 * instead of objects in decode(). 125 * - SERVICES_JSON_SUPPRESS_ERRORS: error suppression. 126 * Values which can't be encoded (e.g. resources) 127 * appear as NULL instead of throwing errors. 128 * By default, a deeply-nested resource will 129 * bubble up with an error, so all return values 130 * from encode() should be checked with isError() 131 */ 132 function Services_JSON($use = 0) 133 { 134 $this->use = $use; 135 } 136 137 /** 138 * convert a string from one UTF-16 char to one UTF-8 char 139 * 140 * Normally should be handled by mb_convert_encoding, but 141 * provides a slower PHP-only method for installations 142 * that lack the multibye string extension. 143 * 144 * @param string $utf16 UTF-16 character 145 * @return string UTF-8 character 146 * @access private 147 */ 148 function utf162utf8($utf16) 149 { 150 // oh please oh please oh please oh please oh please 151 if(function_exists('mb_convert_encoding')) { 152 return mb_convert_encoding($utf16, 'UTF-8', 'UTF-16'); 153 } 154 155 $bytes = (ord($utf16{0}) << 8) | ord($utf16{1}); 156 157 switch(true) { 158 case ((0x7F & $bytes) == $bytes): 159 // this case should never be reached, because we are in ASCII range 160 // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 161 return chr(0x7F & $bytes); 162 163 case (0x07FF & $bytes) == $bytes: 164 // return a 2-byte UTF-8 character 165 // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 166 return chr(0xC0 | (($bytes >> 6) & 0x1F)) 167 . chr(0x80 | ($bytes & 0x3F)); 168 169 case (0xFFFF & $bytes) == $bytes: 170 // return a 3-byte UTF-8 character 171 // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 172 return chr(0xE0 | (($bytes >> 12) & 0x0F)) 173 . chr(0x80 | (($bytes >> 6) & 0x3F)) 174 . chr(0x80 | ($bytes & 0x3F)); 175 } 176 177 // ignoring UTF-32 for now, sorry 178 return ''; 179 } 180 181 /** 182 * convert a string from one UTF-8 char to one UTF-16 char 183 * 184 * Normally should be handled by mb_convert_encoding, but 185 * provides a slower PHP-only method for installations 186 * that lack the multibye string extension. 187 * 188 * @param string $utf8 UTF-8 character 189 * @return string UTF-16 character 190 * @access private 191 */ 192 function utf82utf16($utf8) 193 { 194 // oh please oh please oh please oh please oh please 195 if(function_exists('mb_convert_encoding')) { 196 return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8'); 197 } 198 199 switch(strlen($utf8)) { 200 case 1: 201 // this case should never be reached, because we are in ASCII range 202 // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 203 return $utf8; 204 205 case 2: 206 // return a UTF-16 character from a 2-byte UTF-8 char 207 // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 208 return chr(0x07 & (ord($utf8{0}) >> 2)) 209 . chr((0xC0 & (ord($utf8{0}) << 6)) 210 | (0x3F & ord($utf8{1}))); 211 212 case 3: 213 // return a UTF-16 character from a 3-byte UTF-8 char 214 // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 215 return chr((0xF0 & (ord($utf8{0}) << 4)) 216 | (0x0F & (ord($utf8{1}) >> 2))) 217 . chr((0xC0 & (ord($utf8{1}) << 6)) 218 | (0x7F & ord($utf8{2}))); 219 } 220 221 // ignoring UTF-32 for now, sorry 222 return ''; 223 } 224 225 /** 226 * encodes an arbitrary variable into JSON format (and sends JSON Header) 227 * 228 * @param mixed $var any number, boolean, string, array, or object to be encoded. 229 * see argument 1 to Services_JSON() above for array-parsing behavior. 230 * if var is a strng, note that encode() always expects it 231 * to be in ASCII or UTF-8 format! 232 * 233 * @return mixed JSON string representation of input var or an error if a problem occurs 234 * @access public 235 */ 236 function encode($var) 237 { 238 header('Content-type: application/json'); 239 return $this->_encode($var); 240 } 241 /** 242 * encodes an arbitrary variable into JSON format without JSON Header - warning - may allow CSS!!!!) 243 * 244 * @param mixed $var any number, boolean, string, array, or object to be encoded. 245 * see argument 1 to Services_JSON() above for array-parsing behavior. 246 * if var is a strng, note that encode() always expects it 247 * to be in ASCII or UTF-8 format! 248 * 249 * @return mixed JSON string representation of input var or an error if a problem occurs 250 * @access public 251 */ 252 function encodeUnsafe($var) 253 { 254 return $this->_encode($var); 255 } 256 /** 257 * PRIVATE CODE that does the work of encodes an arbitrary variable into JSON format 258 * 259 * @param mixed $var any number, boolean, string, array, or object to be encoded. 260 * see argument 1 to Services_JSON() above for array-parsing behavior. 261 * if var is a strng, note that encode() always expects it 262 * to be in ASCII or UTF-8 format! 263 * 264 * @return mixed JSON string representation of input var or an error if a problem occurs 265 * @access public 266 */ 267 function _encode($var) 268 { 269 270 switch (gettype($var)) { 271 case 'boolean': 272 return $var ? 'true' : 'false'; 273 274 case 'NULL': 275 return 'null'; 276 277 case 'integer': 278 return (int) $var; 279 280 case 'double': 281 case 'float': 282 return (float) $var; 283 284 case 'string': 285 // STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT 286 $ascii = ''; 287 $strlen_var = strlen($var); 288 289 /* 290 * Iterate over every character in the string, 291 * escaping with a slash or encoding to UTF-8 where necessary 292 */ 293 for ($c = 0; $c < $strlen_var; ++$c) { 294 295 $ord_var_c = ord($var{$c}); 296 297 switch (true) { 298 case $ord_var_c == 0x08: 299 $ascii .= '\b'; 300 break; 301 case $ord_var_c == 0x09: 302 $ascii .= '\t'; 303 break; 304 case $ord_var_c == 0x0A: 305 $ascii .= '\n'; 306 break; 307 case $ord_var_c == 0x0C: 308 $ascii .= '\f'; 309 break; 310 case $ord_var_c == 0x0D: 311 $ascii .= '\r'; 312 break; 313 314 case $ord_var_c == 0x22: 315 case $ord_var_c == 0x2F: 316 case $ord_var_c == 0x5C: 317 // double quote, slash, slosh 318 $ascii .= '\\'.$var{$c}; 319 break; 320 321 case (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)): 322 // characters U-00000000 - U-0000007F (same as ASCII) 323 $ascii .= $var{$c}; 324 break; 325 326 case (($ord_var_c & 0xE0) == 0xC0): 327 // characters U-00000080 - U-000007FF, mask 110XXXXX 328 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 329 if ($c+1 >= $strlen_var) { 330 $c += 1; 331 $ascii .= '?'; 332 break; 333 } 334 335 $char = pack('C*', $ord_var_c, ord($var{$c + 1})); 336 $c += 1; 337 $utf16 = $this->utf82utf16($char); 338 $ascii .= sprintf('\u%04s', bin2hex($utf16)); 339 break; 340 341 case (($ord_var_c & 0xF0) == 0xE0): 342 if ($c+2 >= $strlen_var) { 343 $c += 2; 344 $ascii .= '?'; 345 break; 346 } 347 // characters U-00000800 - U-0000FFFF, mask 1110XXXX 348 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 349 $char = pack('C*', $ord_var_c, 350 @ord($var{$c + 1}), 351 @ord($var{$c + 2})); 352 $c += 2; 353 $utf16 = $this->utf82utf16($char); 354 $ascii .= sprintf('\u%04s', bin2hex($utf16)); 355 break; 356 357 case (($ord_var_c & 0xF8) == 0xF0): 358 if ($c+3 >= $strlen_var) { 359 $c += 3; 360 $ascii .= '?'; 361 break; 362 } 363 // characters U-00010000 - U-001FFFFF, mask 11110XXX 364 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 365 $char = pack('C*', $ord_var_c, 366 ord($var{$c + 1}), 367 ord($var{$c + 2}), 368 ord($var{$c + 3})); 369 $c += 3; 370 $utf16 = $this->utf82utf16($char); 371 $ascii .= sprintf('\u%04s', bin2hex($utf16)); 372 break; 373 374 case (($ord_var_c & 0xFC) == 0xF8): 375 // characters U-00200000 - U-03FFFFFF, mask 111110XX 376 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 377 if ($c+4 >= $strlen_var) { 378 $c += 4; 379 $ascii .= '?'; 380 break; 381 } 382 $char = pack('C*', $ord_var_c, 383 ord($var{$c + 1}), 384 ord($var{$c + 2}), 385 ord($var{$c + 3}), 386 ord($var{$c + 4})); 387 $c += 4; 388 $utf16 = $this->utf82utf16($char); 389 $ascii .= sprintf('\u%04s', bin2hex($utf16)); 390 break; 391 392 case (($ord_var_c & 0xFE) == 0xFC): 393 if ($c+5 >= $strlen_var) { 394 $c += 5; 395 $ascii .= '?'; 396 break; 397 } 398 // characters U-04000000 - U-7FFFFFFF, mask 1111110X 399 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 400 $char = pack('C*', $ord_var_c, 401 ord($var{$c + 1}), 402 ord($var{$c + 2}), 403 ord($var{$c + 3}), 404 ord($var{$c + 4}), 405 ord($var{$c + 5})); 406 $c += 5; 407 $utf16 = $this->utf82utf16($char); 408 $ascii .= sprintf('\u%04s', bin2hex($utf16)); 409 break; 410 } 411 } 412 return '"'.$ascii.'"'; 413 414 case 'array': 415 /* 416 * As per JSON spec if any array key is not an integer 417 * we must treat the the whole array as an object. We 418 * also try to catch a sparsely populated associative 419 * array with numeric keys here because some JS engines 420 * will create an array with empty indexes up to 421 * max_index which can cause memory issues and because 422 * the keys, which may be relevant, will be remapped 423 * otherwise. 424 * 425 * As per the ECMA and JSON specification an object may 426 * have any string as a property. Unfortunately due to 427 * a hole in the ECMA specification if the key is a 428 * ECMA reserved word or starts with a digit the 429 * parameter is only accessible using ECMAScript's 430 * bracket notation. 431 */ 432 433 // treat as a JSON object 434 if (is_array($var) && count($var) && (array_keys($var) !== range(0, sizeof($var) - 1))) { 435 $properties = array_map(array($this, 'name_value'), 436 array_keys($var), 437 array_values($var)); 438 439 foreach($properties as $property) { 440 if(Services_JSON::isError($property)) { 441 return $property; 442 } 443 } 444 445 return '{' . join(',', $properties) . '}'; 446 } 447 448 // treat it like a regular array 449 $elements = array_map(array($this, '_encode'), $var); 450 451 foreach($elements as $element) { 452 if(Services_JSON::isError($element)) { 453 return $element; 454 } 455 } 456 457 return '[' . join(',', $elements) . ']'; 458 459 case 'object': 460 $vars = get_object_vars($var); 461 462 $properties = array_map(array($this, 'name_value'), 463 array_keys($vars), 464 array_values($vars)); 465 466 foreach($properties as $property) { 467 if(Services_JSON::isError($property)) { 468 return $property; 469 } 470 } 471 472 return '{' . join(',', $properties) . '}'; 473 474 default: 475 return ($this->use & SERVICES_JSON_SUPPRESS_ERRORS) 476 ? 'null' 477 : new Services_JSON_Error(gettype($var)." can not be encoded as JSON string"); 478 } 479 } 480 481 /** 482 * array-walking function for use in generating JSON-formatted name-value pairs 483 * 484 * @param string $name name of key to use 485 * @param mixed $value reference to an array element to be encoded 486 * 487 * @return string JSON-formatted name-value pair, like '"name":value' 488 * @access private 489 */ 490 function name_value($name, $value) 491 { 492 $encoded_value = $this->_encode($value); 493 494 if(Services_JSON::isError($encoded_value)) { 495 return $encoded_value; 496 } 497 498 return $this->_encode(strval($name)) . ':' . $encoded_value; 499 } 500 501 /** 502 * reduce a string by removing leading and trailing comments and whitespace 503 * 504 * @param $str string string value to strip of comments and whitespace 505 * 506 * @return string string value stripped of comments and whitespace 507 * @access private 508 */ 509 function reduce_string($str) 510 { 511 $str = preg_replace(array( 512 513 // eliminate single line comments in '// ...' form 514 '#^\s*//(.+)$#m', 515 516 // eliminate multi-line comments in '/* ... */' form, at start of string 517 '#^\s*/\*(.+)\*/#Us', 518 519 // eliminate multi-line comments in '/* ... */' form, at end of string 520 '#/\*(.+)\*/\s*$#Us' 521 522 ), '', $str); 523 524 // eliminate extraneous space 525 return trim($str); 526 } 527 528 /** 529 * decodes a JSON string into appropriate variable 530 * 531 * @param string $str JSON-formatted string 532 * 533 * @return mixed number, boolean, string, array, or object 534 * corresponding to given JSON input string. 535 * See argument 1 to Services_JSON() above for object-output behavior. 536 * Note that decode() always returns strings 537 * in ASCII or UTF-8 format! 538 * @access public 539 */ 540 function decode($str) 541 { 542 $str = $this->reduce_string($str); 543 544 switch (strtolower($str)) { 545 case 'true': 546 return true; 547 548 case 'false': 549 return false; 550 551 case 'null': 552 return null; 553 554 default: 555 $m = array(); 556 557 if (is_numeric($str)) { 558 // Lookie-loo, it's a number 559 560 // This would work on its own, but I'm trying to be 561 // good about returning integers where appropriate: 562 // return (float)$str; 563 564 // Return float or int, as appropriate 565 return ((float)$str == (integer)$str) 566 ? (integer)$str 567 : (float)$str; 568 569 } elseif (preg_match('/^("|\').*(\1)$/s', $str, $m) && $m[1] == $m[2]) { 570 // STRINGS RETURNED IN UTF-8 FORMAT 571 $delim = substr($str, 0, 1); 572 $chrs = substr($str, 1, -1); 573 $utf8 = ''; 574 $strlen_chrs = strlen($chrs); 575 576 for ($c = 0; $c < $strlen_chrs; ++$c) { 577 578 $substr_chrs_c_2 = substr($chrs, $c, 2); 579 $ord_chrs_c = ord($chrs{$c}); 580 581 switch (true) { 582 case $substr_chrs_c_2 == '\b': 583 $utf8 .= chr(0x08); 584 ++$c; 585 break; 586 case $substr_chrs_c_2 == '\t': 587 $utf8 .= chr(0x09); 588 ++$c; 589 break; 590 case $substr_chrs_c_2 == '\n': 591 $utf8 .= chr(0x0A); 592 ++$c; 593 break; 594 case $substr_chrs_c_2 == '\f': 595 $utf8 .= chr(0x0C); 596 ++$c; 597 break; 598 case $substr_chrs_c_2 == '\r': 599 $utf8 .= chr(0x0D); 600 ++$c; 601 break; 602 603 case $substr_chrs_c_2 == '\\"': 604 case $substr_chrs_c_2 == '\\\'': 605 case $substr_chrs_c_2 == '\\\\': 606 case $substr_chrs_c_2 == '\\/': 607 if (($delim == '"' && $substr_chrs_c_2 != '\\\'') || 608 ($delim == "'" && $substr_chrs_c_2 != '\\"')) { 609 $utf8 .= $chrs{++$c}; 610 } 611 break; 612 613 case preg_match('/\\\u[0-9A-F]{4}/i', substr($chrs, $c, 6)): 614 // single, escaped unicode character 615 $utf16 = chr(hexdec(substr($chrs, ($c + 2), 2))) 616 . chr(hexdec(substr($chrs, ($c + 4), 2))); 617 $utf8 .= $this->utf162utf8($utf16); 618 $c += 5; 619 break; 620 621 case ($ord_chrs_c >= 0x20) && ($ord_chrs_c <= 0x7F): 622 $utf8 .= $chrs{$c}; 623 break; 624 625 case ($ord_chrs_c & 0xE0) == 0xC0: 626 // characters U-00000080 - U-000007FF, mask 110XXXXX 627 //see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 628 $utf8 .= substr($chrs, $c, 2); 629 ++$c; 630 break; 631 632 case ($ord_chrs_c & 0xF0) == 0xE0: 633 // characters U-00000800 - U-0000FFFF, mask 1110XXXX 634 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 635 $utf8 .= substr($chrs, $c, 3); 636 $c += 2; 637 break; 638 639 case ($ord_chrs_c & 0xF8) == 0xF0: 640 // characters U-00010000 - U-001FFFFF, mask 11110XXX 641 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 642 $utf8 .= substr($chrs, $c, 4); 643 $c += 3; 644 break; 645 646 case ($ord_chrs_c & 0xFC) == 0xF8: 647 // characters U-00200000 - U-03FFFFFF, mask 111110XX 648 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 649 $utf8 .= substr($chrs, $c, 5); 650 $c += 4; 651 break; 652 653 case ($ord_chrs_c & 0xFE) == 0xFC: 654 // characters U-04000000 - U-7FFFFFFF, mask 1111110X 655 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 656 $utf8 .= substr($chrs, $c, 6); 657 $c += 5; 658 break; 659 660 } 661 662 } 663 664 return $utf8; 665 666 } elseif (preg_match('/^\[.*\]$/s', $str) || preg_match('/^\{.*\}$/s', $str)) { 667 // array, or object notation 668 669 if ($str{0} == '[') { 670 $stk = array(SERVICES_JSON_IN_ARR); 671 $arr = array(); 672 } else { 673 if ($this->use & SERVICES_JSON_LOOSE_TYPE) { 674 $stk = array(SERVICES_JSON_IN_OBJ); 675 $obj = array(); 676 } else { 677 $stk = array(SERVICES_JSON_IN_OBJ); 678 $obj = new stdClass(); 679 } 680 } 681 682 array_push($stk, array('what' => SERVICES_JSON_SLICE, 683 'where' => 0, 684 'delim' => false)); 685 686 $chrs = substr($str, 1, -1); 687 $chrs = $this->reduce_string($chrs); 688 689 if ($chrs == '') { 690 if (reset($stk) == SERVICES_JSON_IN_ARR) { 691 return $arr; 692 693 } else { 694 return $obj; 695 696 } 697 } 698 699 //print("\nparsing {$chrs}\n"); 700 701 $strlen_chrs = strlen($chrs); 702 703 for ($c = 0; $c <= $strlen_chrs; ++$c) { 704 705 $top = end($stk); 706 $substr_chrs_c_2 = substr($chrs, $c, 2); 707 708 if (($c == $strlen_chrs) || (($chrs{$c} == ',') && ($top['what'] == SERVICES_JSON_SLICE))) { 709 // found a comma that is not inside a string, array, etc., 710 // OR we've reached the end of the character list 711 $slice = substr($chrs, $top['where'], ($c - $top['where'])); 712 array_push($stk, array('what' => SERVICES_JSON_SLICE, 'where' => ($c + 1), 'delim' => false)); 713 //print("Found split at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n"); 714 715 if (reset($stk) == SERVICES_JSON_IN_ARR) { 716 // we are in an array, so just push an element onto the stack 717 array_push($arr, $this->decode($slice)); 718 719 } elseif (reset($stk) == SERVICES_JSON_IN_OBJ) { 720 // we are in an object, so figure 721 // out the property name and set an 722 // element in an associative array, 723 // for now 724 $parts = array(); 725 726 if (preg_match('/^\s*(["\'].*[^\\\]["\'])\s*:\s*(\S.*),?$/Uis', $slice, $parts)) { 727 // "name":value pair 728 $key = $this->decode($parts[1]); 729 $val = $this->decode($parts[2]); 730 731 if ($this->use & SERVICES_JSON_LOOSE_TYPE) { 732 $obj[$key] = $val; 733 } else { 734 $obj->$key = $val; 735 } 736 } elseif (preg_match('/^\s*(\w+)\s*:\s*(\S.*),?$/Uis', $slice, $parts)) { 737 // name:value pair, where name is unquoted 738 $key = $parts[1]; 739 $val = $this->decode($parts[2]); 740 741 if ($this->use & SERVICES_JSON_LOOSE_TYPE) { 742 $obj[$key] = $val; 743 } else { 744 $obj->$key = $val; 745 } 746 } 747 748 } 749 750 } elseif ((($chrs{$c} == '"') || ($chrs{$c} == "'")) && ($top['what'] != SERVICES_JSON_IN_STR)) { 751 // found a quote, and we are not inside a string 752 array_push($stk, array('what' => SERVICES_JSON_IN_STR, 'where' => $c, 'delim' => $chrs{$c})); 753 //print("Found start of string at {$c}\n"); 754 755 } elseif (($chrs{$c} == $top['delim']) && 756 ($top['what'] == SERVICES_JSON_IN_STR) && 757 ((strlen(substr($chrs, 0, $c)) - strlen(rtrim(substr($chrs, 0, $c), '\\'))) % 2 != 1)) { 758 // found a quote, we're in a string, and it's not escaped 759 // we know that it's not escaped becase there is _not_ an 760 // odd number of backslashes at the end of the string so far 761 array_pop($stk); 762 //print("Found end of string at {$c}: ".substr($chrs, $top['where'], (1 + 1 + $c - $top['where']))."\n"); 763 764 } elseif (($chrs{$c} == '[') && 765 in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) { 766 // found a left-bracket, and we are in an array, object, or slice 767 array_push($stk, array('what' => SERVICES_JSON_IN_ARR, 'where' => $c, 'delim' => false)); 768 //print("Found start of array at {$c}\n"); 769 770 } elseif (($chrs{$c} == ']') && ($top['what'] == SERVICES_JSON_IN_ARR)) { 771 // found a right-bracket, and we're in an array 772 array_pop($stk); 773 //print("Found end of array at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n"); 774 775 } elseif (($chrs{$c} == '{') && 776 in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) { 777 // found a left-brace, and we are in an array, object, or slice 778 array_push($stk, array('what' => SERVICES_JSON_IN_OBJ, 'where' => $c, 'delim' => false)); 779 //print("Found start of object at {$c}\n"); 780 781 } elseif (($chrs{$c} == '}') && ($top['what'] == SERVICES_JSON_IN_OBJ)) { 782 // found a right-brace, and we're in an object 783 array_pop($stk); 784 //print("Found end of object at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n"); 785 786 } elseif (($substr_chrs_c_2 == '/*') && 787 in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) { 788 // found a comment start, and we are in an array, object, or slice 789 array_push($stk, array('what' => SERVICES_JSON_IN_CMT, 'where' => $c, 'delim' => false)); 790 $c++; 791 //print("Found start of comment at {$c}\n"); 792 793 } elseif (($substr_chrs_c_2 == '*/') && ($top['what'] == SERVICES_JSON_IN_CMT)) { 794 // found a comment end, and we're in one now 795 array_pop($stk); 796 $c++; 797 798 for ($i = $top['where']; $i <= $c; ++$i) 799 $chrs = substr_replace($chrs, ' ', $i, 1); 800 801 //print("Found end of comment at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n"); 802 803 } 804 805 } 806 807 if (reset($stk) == SERVICES_JSON_IN_ARR) { 808 return $arr; 809 810 } elseif (reset($stk) == SERVICES_JSON_IN_OBJ) { 811 return $obj; 812 813 } 814 815 } 816 } 817 } 818 819 /** 820 * @todo Ultimately, this should just call PEAR::isError() 821 */ 822 function isError($data, $code = null) 823 { 824 if (class_exists('pear')) { 825 return PEAR::isError($data, $code); 826 } elseif (is_object($data) && (get_class($data) == 'services_json_error' || 827 is_subclass_of($data, 'services_json_error'))) { 828 return true; 829 } 830 831 return false; 832 } 833 } 834 835 if (class_exists('PEAR_Error')) { 836 837 class Services_JSON_Error extends PEAR_Error 838 { 839 function Services_JSON_Error($message = 'unknown error', $code = null, 840 $mode = null, $options = null, $userinfo = null) 841 { 842 parent::PEAR_Error($message, $code, $mode, $options, $userinfo); 843 } 844 } 845 846 } else { 847 848 /** 849 * @todo Ultimately, this class shall be descended from PEAR_Error 850 */ 851 class Services_JSON_Error 852 { 853 function Services_JSON_Error($message = 'unknown error', $code = null, 854 $mode = null, $options = null, $userinfo = null) 855 { 856 857 } 858 } 859 860 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated: Fri Jan 8 00:19:48 2010 | Cross-referenced by PHPXref 0.7 |