| [ Index ] |
|
Code source de Flux CMS 1.5 |
1 <?php 2 /* 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-interchange 8 * format. It is easy for humans to read and write. It is easy for machines 9 * to parse and generate. It is based on a subset of the JavaScript 10 * Programming Language, Standard ECMA-262 3rd Edition - December 1999. 11 * This feature can also be found in Python. JSON is a text format that is 12 * completely language independent but uses conventions that are familiar 13 * to programmers of the C-family of languages, including C, C++, C#, Java, 14 * JavaScript, Perl, TCL, and many others. These properties make JSON an 15 * ideal data-interchange language. 16 * 17 * This package provides a simple encoder and decoder for JSON notation. It 18 * is intended for use with client-side Javascript applications that make 19 * use of HTTPRequest to perform server communication functions - data can 20 * be encoded into JSON notation for use in a client-side javascript, or 21 * decoded from incoming Javascript requests. JSON format is native to 22 * Javascript, and can be directly eval()'ed with no further parsing 23 * overhead 24 * 25 * All strings should be in ASCII or UTF-8 format! 26 * 27 * LICENSE: Redistribution and use in source and binary forms, with or 28 * without modification, are permitted provided that the following 29 * conditions are met: Redistributions of source code must retain the 30 * above copyright notice, this list of conditions and the following 31 * disclaimer. Redistributions in binary form must reproduce the above 32 * copyright notice, this list of conditions and the following disclaimer 33 * in the documentation and/or other materials provided with the 34 * distribution. 35 * 36 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED 37 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 38 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN 39 * 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; LOSS 42 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 43 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 44 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE 45 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 46 * DAMAGE. 47 * 48 * @category 49 * @package Services_JSON 50 * @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 Migurski 54 * @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.php 56 * @link http://pear.php.net/pepr/pepr-proposal-show.php?id=198 57 */ 58 59 /** 60 * Marker constant for Services_JSON::decode(), used to flag stack state 61 */ 62 define('SERVICES_JSON_SLICE', 1); 63 64 /** 65 * Marker constant for Services_JSON::decode(), used to flag stack state 66 */ 67 define('SERVICES_JSON_IN_STR', 2); 68 69 /** 70 * Marker constant for Services_JSON::decode(), used to flag stack state 71 */ 72 define('SERVICES_JSON_IN_ARR', 3); 73 74 /** 75 * Marker constant for Services_JSON::decode(), used to flag stack state 76 */ 77 define('SERVICES_JSON_IN_OBJ', 4); 78 79 /** 80 * Marker constant for Services_JSON::decode(), used to flag stack state 81 */ 82 define('SERVICES_JSON_IN_CMT', 5); 83 84 /** 85 * Behavior switch for Services_JSON::decode() 86 */ 87 define('SERVICES_JSON_LOOSE_TYPE', 16); 88 89 /** 90 * Behavior switch for Services_JSON::decode() 91 */ 92 define('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_JSON 101 * $json = new Services_JSON(); 102 * 103 * // convert a complexe value to JSON notation, and send it to the browser 104 * $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 notation 111 * $input = file_get_contents('php://input', 1000000); 112 * $value = $json->decode($input); 113 * </code> 114 */ 115 class JSON 116 { 117 /** 118 * constructs a new JSON instance 119 * 120 * @param int $use object behavior flags; combine with boolean-OR 121 * 122 * possible values: 123 * - SERVICES_JSON_LOOSE_TYPE: loose typing. 124 * "{...}" syntax creates associative arrays 125 * instead of objects in decode(). 126 * - SERVICES_JSON_SUPPRESS_ERRORS: error suppression. 127 * Values which can't be encoded (e.g. resources) 128 * appear as NULL instead of throwing errors. 129 * By default, a deeply-nested resource will 130 * bubble up with an error, so all return values 131 * from encode() should be checked with isError() 132 */ 133 function JSON($use = 0) 134 { 135 $this->use = $use; 136 } 137 138 /** 139 * convert a string from one UTF-16 char to one UTF-8 char 140 * 141 * Normally should be handled by mb_convert_encoding, but 142 * provides a slower PHP-only method for installations 143 * that lack the multibye string extension. 144 * 145 * @param string $utf16 UTF-16 character 146 * @return string UTF-8 character 147 * @access private 148 */ 149 function utf162utf8($utf16) 150 { 151 // oh please oh please oh please oh please oh please 152 if (function_exists("iconv")) { 153 return iconv('UTF-16BE','UTF-8',$utf16); 154 } 155 if(function_exists('mb_convert_encoding')) { 156 return mb_convert_encoding($utf16, 'UTF-8', 'UTF-16'); 157 } 158 159 $bytes = (ord($utf16{0}) << 8) | ord($utf16{1}); 160 161 switch(true) { 162 case ((0x7F & $bytes) == $bytes): 163 // this case should never be reached, because we are in ASCII range 164 // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 165 return chr(0x7F & $bytes); 166 167 case (0x07FF & $bytes) == $bytes: 168 // return a 2-byte UTF-8 character 169 // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 170 return chr(0xC0 | (($bytes >> 6) & 0x1F)) 171 . chr(0x80 | ($bytes & 0x3F)); 172 173 case (0xFFFF & $bytes) == $bytes: 174 // return a 3-byte UTF-8 character 175 // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 176 return chr(0xE0 | (($bytes >> 12) & 0x0F)) 177 . chr(0x80 | (($bytes >> 6) & 0x3F)) 178 . chr(0x80 | ($bytes & 0x3F)); 179 } 180 181 // ignoring UTF-32 for now, sorry 182 return ''; 183 } 184 185 /** 186 * convert a string from one UTF-8 char to one UTF-16 char 187 * 188 * Normally should be handled by mb_convert_encoding, but 189 * provides a slower PHP-only method for installations 190 * that lack the multibye string extension. 191 * 192 * @param string $utf8 UTF-8 character 193 * @return string UTF-16 character 194 * @access private 195 */ 196 function utf82utf16($utf8) 197 { 198 // oh please oh please oh please oh please oh please 199 200 if (function_exists("iconv")) { 201 return iconv('UTF-8','UTF-16BE',$utf8); 202 } 203 if(function_exists('mb_convert_encoding')) { 204 return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8'); 205 } 206 207 switch(strlen($utf8)) { 208 case 1: 209 // this case should never be reached, because we are in ASCII range 210 // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 211 return $utf8; 212 213 case 2: 214 // return a UTF-16 character from a 2-byte UTF-8 char 215 // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 216 return chr(0x07 & (ord($utf8{0}) >> 2)) 217 . chr((0xC0 & (ord($utf8{0}) << 6)) 218 | (0x3F & ord($utf8{1}))); 219 220 case 3: 221 // return a UTF-16 character from a 3-byte UTF-8 char 222 // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 223 return chr((0xF0 & (ord($utf8{0}) << 4)) 224 | (0x0F & (ord($utf8{1}) >> 2))) 225 . chr((0xC0 & (ord($utf8{1}) << 6)) 226 | (0x7F & ord($utf8{2}))); 227 } 228 229 // ignoring UTF-32 for now, sorry 230 return ''; 231 } 232 233 /** 234 * encodes an arbitrary variable into JSON format 235 * 236 * @param mixed $var any number, boolean, string, array, or object to be encoded. 237 * see argument 1 to Services_JSON() above for array-parsing behavior. 238 * if var is a strng, note that encode() always expects it 239 * to be in ASCII or UTF-8 format! 240 * 241 * @return mixed JSON string representation of input var or an error if a problem occurs 242 * @access public 243 */ 244 function encode($var) 245 { 246 switch (gettype($var)) { 247 case 'boolean': 248 return $var ? 'true' : 'false'; 249 250 case 'NULL': 251 return 'null'; 252 253 case 'integer': 254 return (int) $var; 255 256 case 'double': 257 case 'float': 258 return (float) $var; 259 260 case 'string': 261 if ($c = iconv("UTF-8","JAVA",$var)) { 262 return '"'.$c.'"'; 263 } 264 // STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT 265 $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 necessary 271 */ 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, slosh 297 $ascii .= '\\'.$var{$c}; 298 break; 299 300 case (($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 110XXXXX 307 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 308 $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 1110XXXX 316 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 317 $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 11110XXX 327 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 328 $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 111110XX 339 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 340 $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 1111110X 352 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 353 $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 integer 371 * we must treat the the whole array as an object. We 372 * also try to catch a sparsely populated associative 373 * array with numeric keys here because some JS engines 374 * will create an array with empty indexes up to 375 * max_index which can cause memory issues and because 376 * the keys, which may be relevant, will be remapped 377 * otherwise. 378 * 379 * As per the ECMA and JSON specification an object may 380 * have any string as a property. Unfortunately due to 381 * a hole in the ECMA specification if the key is a 382 * ECMA reserved word or starts with a digit the 383 * parameter is only accessible using ECMAScript's 384 * bracket notation. 385 */ 386 387 // treat as a JSON object 388 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(JSON::isError($property)) { 395 return $property; 396 } 397 } 398 399 return '{' . join(',', $properties) . '}'; 400 } 401 402 // treat it like a regular array 403 $elements = array_map(array($this, 'encode'), $var); 404 405 foreach($elements as $element) { 406 if(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(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 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 pairs 437 * 438 * @param string $name name of key to use 439 * @param mixed $value reference to an array element to be encoded 440 * 441 * @return string JSON-formatted name-value pair, like '"name":value' 442 * @access private 443 */ 444 function name_value($name, $value) 445 { 446 $encoded_value = $this->encode($value); 447 448 if(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 whitespace 457 * 458 * @param $str string string value to strip of comments and whitespace 459 * 460 * @return string string value stripped of comments and whitespace 461 * @access private 462 */ 463 function reduce_string($str) 464 { 465 $str = preg_replace(array( 466 467 // eliminate single line comments in '// ...' form 468 '#^\s*//(.+)$#m', 469 470 // eliminate multi-line comments in '/* ... */' form, at start of string 471 '#^\s*/\*(.+)\*/#Us', 472 473 // eliminate multi-line comments in '/* ... */' form, at end of string 474 '#/\*(.+)\*/\s*$#Us' 475 476 ), '', $str); 477 478 // eliminate extraneous space 479 return trim($str); 480 } 481 482 /** 483 * decodes a JSON string into appropriate variable 484 * 485 * @param string $str JSON-formatted string 486 * 487 * @return mixed number, boolean, string, array, or object 488 * corresponding to given JSON input string. 489 * See argument 1 to Services_JSON() above for object-output behavior. 490 * Note that decode() always returns strings 491 * in ASCII or UTF-8 format! 492 * @access public 493 */ 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 510 $m = array(); 511 512 if (is_numeric($str)) { 513 // Lookie-loo, it's a number 514 515 // This would work on its own, but I'm trying to be 516 // good about returning integers where appropriate: 517 // return (float)$str; 518 519 // Return float or int, as appropriate 520 return ((float)$str == (integer)$str) 521 ? (integer)$str 522 : (float)$str; 523 524 } elseif (preg_match('/^("|\').*(\1)$/s', $str, $m) && $m[1] == $m[2]) { 525 // STRINGS RETURNED IN UTF-8 FORMAT 526 $chrs = substr($str, 1, -1); 527 528 if ($c = iconv("JAVA","UTF-8",$chrs)) { 529 return $c; 530 } 531 $delim = substr($str, 0, 1); 532 $utf8 = ''; 533 $strlen_chrs = strlen($chrs); 534 535 for ($c = 0; $c < $strlen_chrs; ++$c) { 536 537 $substr_chrs_c_2 = substr($chrs, $c, 2); 538 $ord_chrs_c = ord($chrs{$c}); 539 540 switch (true) { 541 case $substr_chrs_c_2 == '\b': 542 $utf8 .= chr(0x08); 543 ++$c; 544 break; 545 case $substr_chrs_c_2 == '\t': 546 $utf8 .= chr(0x09); 547 ++$c; 548 break; 549 case $substr_chrs_c_2 == '\n': 550 $utf8 .= chr(0x0A); 551 ++$c; 552 break; 553 case $substr_chrs_c_2 == '\f': 554 $utf8 .= chr(0x0C); 555 ++$c; 556 break; 557 case $substr_chrs_c_2 == '\r': 558 $utf8 .= chr(0x0D); 559 ++$c; 560 break; 561 562 case $substr_chrs_c_2 == '\\"': 563 case $substr_chrs_c_2 == '\\\'': 564 case $substr_chrs_c_2 == '\\\\': 565 case $substr_chrs_c_2 == '\\/': 566 if (($delim == '"' && $substr_chrs_c_2 != '\\\'') || 567 ($delim == "'" && $substr_chrs_c_2 != '\\"')) { 568 $utf8 .= $chrs{++$c}; 569 } 570 break; 571 572 case preg_match('/\\\u[0-9A-F]{4}/i', substr($chrs, $c, 6)): 573 // single, escaped unicode character 574 $utf16 = chr(hexdec(substr($chrs, ($c + 2), 2))) 575 . chr(hexdec(substr($chrs, ($c + 4), 2))); 576 $utf8 .= $this->utf162utf8($utf16); 577 $c += 5; 578 break; 579 580 case ($ord_chrs_c >= 0x20) && ($ord_chrs_c <= 0x7F): 581 $utf8 .= $chrs{$c}; 582 break; 583 584 case ($ord_chrs_c & 0xE0) == 0xC0: 585 // characters U-00000080 - U-000007FF, mask 110XXXXX 586 //see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 587 $utf8 .= substr($chrs, $c, 2); 588 ++$c; 589 break; 590 591 case ($ord_chrs_c & 0xF0) == 0xE0: 592 // characters U-00000800 - U-0000FFFF, mask 1110XXXX 593 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 594 $utf8 .= substr($chrs, $c, 3); 595 $c += 2; 596 break; 597 598 case ($ord_chrs_c & 0xF8) == 0xF0: 599 // characters U-00010000 - U-001FFFFF, mask 11110XXX 600 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 601 $utf8 .= substr($chrs, $c, 4); 602 $c += 3; 603 break; 604 605 case ($ord_chrs_c & 0xFC) == 0xF8: 606 // characters U-00200000 - U-03FFFFFF, mask 111110XX 607 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 608 $utf8 .= substr($chrs, $c, 5); 609 $c += 4; 610 break; 611 612 case ($ord_chrs_c & 0xFE) == 0xFC: 613 // characters U-04000000 - U-7FFFFFFF, mask 1111110X 614 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 615 $utf8 .= substr($chrs, $c, 6); 616 $c += 5; 617 break; 618 619 } 620 621 } 622 623 return $utf8; 624 625 } elseif (preg_match('/^\[.*\]$/s', $str) || preg_match('/^\{.*\}$/s', $str)) { 626 // array, or object notation 627 628 if ($str{0} == '[') { 629 $stk = array(SERVICES_JSON_IN_ARR); 630 $arr = array(); 631 } else { 632 if ($this->use & SERVICES_JSON_LOOSE_TYPE) { 633 $stk = array(SERVICES_JSON_IN_OBJ); 634 $obj = array(); 635 } else { 636 $stk = array(SERVICES_JSON_IN_OBJ); 637 $obj = new stdClass(); 638 } 639 } 640 641 array_push($stk, array('what' => SERVICES_JSON_SLICE, 642 'where' => 0, 643 'delim' => false)); 644 645 $chrs = substr($str, 1, -1); 646 $chrs = $this->reduce_string($chrs); 647 648 if ($chrs == '') { 649 if (reset($stk) == SERVICES_JSON_IN_ARR) { 650 return $arr; 651 652 } else { 653 return $obj; 654 655 } 656 } 657 658 //print("\nparsing {$chrs}\n"); 659 660 $strlen_chrs = strlen($chrs); 661 662 for ($c = 0; $c <= $strlen_chrs; ++$c) { 663 664 $top = end($stk); 665 $substr_chrs_c_2 = substr($chrs, $c, 2); 666 667 if (($c == $strlen_chrs) || (($chrs{$c} == ',') && ($top['what'] == SERVICES_JSON_SLICE))) { 668 // found a comma that is not inside a string, array, etc., 669 // OR we've reached the end of the character list 670 $slice = substr($chrs, $top['where'], ($c - $top['where'])); 671 array_push($stk, array('what' => SERVICES_JSON_SLICE, 'where' => ($c + 1), 'delim' => false)); 672 //print("Found split at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n"); 673 674 if (reset($stk) == SERVICES_JSON_IN_ARR) { 675 // we are in an array, so just push an element onto the stack 676 array_push($arr, $this->decode($slice)); 677 678 } elseif (reset($stk) == SERVICES_JSON_IN_OBJ) { 679 // we are in an object, so figure 680 // out the property name and set an 681 // element in an associative array, 682 // for now 683 $parts = array(); 684 685 if (preg_match('/^\s*(["\'].*[^\\\]["\'])\s*:\s*(\S.*),?$/Uis', $slice, $parts)) { 686 // "name":value pair 687 $key = $this->decode($parts[1]); 688 $val = $this->decode($parts[2]); 689 690 if ($this->use & SERVICES_JSON_LOOSE_TYPE) { 691 $obj[$key] = $val; 692 } else { 693 $obj->$key = $val; 694 } 695 } elseif (preg_match('/^\s*(\w+)\s*:\s*(\S.*),?$/Uis', $slice, $parts)) { 696 // name:value pair, where name is unquoted 697 $key = $parts[1]; 698 $val = $this->decode($parts[2]); 699 700 if ($this->use & SERVICES_JSON_LOOSE_TYPE) { 701 $obj[$key] = $val; 702 } else { 703 $obj->$key = $val; 704 } 705 } 706 707 } 708 709 } elseif ((($chrs{$c} == '"') || ($chrs{$c} == "'")) && ($top['what'] != SERVICES_JSON_IN_STR)) { 710 // found a quote, and we are not inside a string 711 array_push($stk, array('what' => SERVICES_JSON_IN_STR, 'where' => $c, 'delim' => $chrs{$c})); 712 //print("Found start of string at {$c}\n"); 713 714 } elseif (($chrs{$c} == $top['delim']) && 715 ($top['what'] == SERVICES_JSON_IN_STR) && 716 ((strlen(substr($chrs, 0, $c)) - strlen(rtrim(substr($chrs, 0, $c), '\\'))) % 2 != 1)) { 717 // found a quote, we're in a string, and it's not escaped 718 // we know that it's not escaped becase there is _not_ an 719 // odd number of backslashes at the end of the string so far 720 array_pop($stk); 721 //print("Found end of string at {$c}: ".substr($chrs, $top['where'], (1 + 1 + $c - $top['where']))."\n"); 722 723 } elseif (($chrs{$c} == '[') && 724 in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) { 725 // found a left-bracket, and we are in an array, object, or slice 726 array_push($stk, array('what' => SERVICES_JSON_IN_ARR, 'where' => $c, 'delim' => false)); 727 //print("Found start of array at {$c}\n"); 728 729 } elseif (($chrs{$c} == ']') && ($top['what'] == SERVICES_JSON_IN_ARR)) { 730 // found a right-bracket, and we're in an array 731 array_pop($stk); 732 //print("Found end of array at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n"); 733 734 } elseif (($chrs{$c} == '{') && 735 in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) { 736 // found a left-brace, and we are in an array, object, or slice 737 array_push($stk, array('what' => SERVICES_JSON_IN_OBJ, 'where' => $c, 'delim' => false)); 738 //print("Found start of object at {$c}\n"); 739 740 } elseif (($chrs{$c} == '}') && ($top['what'] == SERVICES_JSON_IN_OBJ)) { 741 // found a right-brace, and we're in an object 742 array_pop($stk); 743 //print("Found end of object at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n"); 744 745 } elseif (($substr_chrs_c_2 == '/*') && 746 in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) { 747 // found a comment start, and we are in an array, object, or slice 748 array_push($stk, array('what' => SERVICES_JSON_IN_CMT, 'where' => $c, 'delim' => false)); 749 $c++; 750 //print("Found start of comment at {$c}\n"); 751 752 } elseif (($substr_chrs_c_2 == '*/') && ($top['what'] == SERVICES_JSON_IN_CMT)) { 753 // found a comment end, and we're in one now 754 array_pop($stk); 755 $c++; 756 757 for ($i = $top['where']; $i <= $c; ++$i) 758 $chrs = substr_replace($chrs, ' ', $i, 1); 759 760 //print("Found end of comment at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n"); 761 762 } 763 764 } 765 766 if (reset($stk) == SERVICES_JSON_IN_ARR) { 767 return $arr; 768 769 } elseif (reset($stk) == SERVICES_JSON_IN_OBJ) { 770 return $obj; 771 772 } 773 774 } 775 } 776 } 777 778 /** 779 * @todo Ultimately, this should just call PEAR::isError() 780 */ 781 function isError($data, $code = null) 782 { 783 if (class_exists('pear')) { 784 return PEAR::isError($data, $code); 785 } elseif (is_object($data) && (get_class($data) == 'services_json_error' || 786 is_subclass_of($data, 'services_json_error'))) { 787 return true; 788 } 789 790 return false; 791 } 792 } 793 794 if (class_exists('PEAR_Error')) { 795 796 class JSON_Error extends PEAR_Error 797 { 798 function Services_JSON_Error($message = 'unknown error', $code = null, 799 $mode = null, $options = null, $userinfo = null) 800 { 801 parent::PEAR_Error($message, $code, $mode, $options, $userinfo); 802 } 803 } 804 805 } else { 806 807 /** 808 * @todo Ultimately, this class shall be descended from PEAR_Error 809 */ 810 class JSON_Error 811 { 812 function JSON_Error($message = 'unknown error', $code = null, 813 $mode = null, $options = null, $userinfo = null) 814 { 815 816 } 817 } 818 819 } 820 821 ?>
titre
Description
Corps
titre
Description
Corps
titre
Description
Corps
titre
Corps
| Généré le : Wed Nov 21 13:08:55 2007 | par Balluche grâce à PHPXref 0.7 |
|