Changeset 23126 for branches/version-2_13_0/data/module/Services/JSON.php
- Timestamp:
- 2013/08/26 15:52:37 (11 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
branches/version-2_13_0/data/module/Services/JSON.php
r20116 r23126 1 1 <?php 2 2 /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ 3 4 3 /** 5 4 * Converts to and from JSON format. … … 93 92 94 93 /** 94 * Behavior switch for Services_JSON::decode() 95 */ 96 define('SERVICES_JSON_USE_TO_JSON', 64); 97 98 /** 95 99 * Converts to and from JSON format. 96 100 * … … 130 134 * bubble up with an error, so all return values 131 135 * from encode() should be checked with isError() 136 * - SERVICES_JSON_USE_TO_JSON: call toJSON when serializing objects 137 * It serializes the return value from the toJSON call rather 138 * than the object it'self, toJSON can return associative arrays, 139 * strings or numbers, if you return an object, make sure it does 140 * not have a toJSON method, otherwise an error will occur. 132 141 */ 133 142 function Services_JSON($use = 0) 134 143 { 135 144 $this->use = $use; 136 } 137 145 $this->_mb_strlen = function_exists('mb_strlen'); 146 $this->_mb_convert_encoding = function_exists('mb_convert_encoding'); 147 $this->_mb_substr = function_exists('mb_substr'); 148 } 149 // private - cache the mbstring lookup results.. 150 var $_mb_strlen = false; 151 var $_mb_substr = false; 152 var $_mb_convert_encoding = false; 153 138 154 /** 139 155 * convert a string from one UTF-16 char to one UTF-8 char … … 150 166 { 151 167 // oh please oh please oh please oh please oh please 152 if( function_exists('mb_convert_encoding')) {168 if($this->_mb_convert_encoding) { 153 169 return mb_convert_encoding($utf16, 'UTF-8', 'UTF-16'); 154 170 } … … 194 210 { 195 211 // oh please oh please oh please oh please oh please 196 if( function_exists('mb_convert_encoding')) {212 if($this->_mb_convert_encoding) { 197 213 return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8'); 198 214 } 199 215 200 switch( strlen($utf8)) {216 switch($this->strlen8($utf8)) { 201 217 case 1: 202 218 // this case should never be reached, because we are in ASCII range … … 225 241 226 242 /** 227 * encodes an arbitrary variable into JSON format 243 * encodes an arbitrary variable into JSON format (and sends JSON Header) 228 244 * 229 245 * @param mixed $var any number, boolean, string, array, or object to be encoded. … … 237 253 function encode($var) 238 254 { 255 header('Content-type: application/json'); 256 return $this->encodeUnsafe($var); 257 } 258 /** 259 * encodes an arbitrary variable into JSON format without JSON Header - warning - may allow XSS!!!!) 260 * 261 * @param mixed $var any number, boolean, string, array, or object to be encoded. 262 * see argument 1 to Services_JSON() above for array-parsing behavior. 263 * if var is a strng, note that encode() always expects it 264 * to be in ASCII or UTF-8 format! 265 * 266 * @return mixed JSON string representation of input var or an error if a problem occurs 267 * @access public 268 */ 269 function encodeUnsafe($var) 270 { 271 // see bug #16908 - regarding numeric locale printing 272 $lc = setlocale(LC_NUMERIC, 0); 273 setlocale(LC_NUMERIC, 'C'); 274 $ret = $this->_encode($var); 275 setlocale(LC_NUMERIC, $lc); 276 return $ret; 277 278 } 279 /** 280 * PRIVATE CODE that does the work of encodes an arbitrary variable into JSON format 281 * 282 * @param mixed $var any number, boolean, string, array, or object to be encoded. 283 * see argument 1 to Services_JSON() above for array-parsing behavior. 284 * if var is a strng, note that encode() always expects it 285 * to be in ASCII or UTF-8 format! 286 * 287 * @return mixed JSON string representation of input var or an error if a problem occurs 288 * @access public 289 */ 290 function _encode($var) 291 { 292 239 293 switch (gettype($var)) { 240 294 case 'boolean': … … 249 303 case 'double': 250 304 case 'float': 251 return (float) $var;305 return (float) $var; 252 306 253 307 case 'string': 254 308 // STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT 255 309 $ascii = ''; 256 $strlen_var = strlen($var);310 $strlen_var = $this->strlen8($var); 257 311 258 312 /* … … 296 350 // characters U-00000080 - U-000007FF, mask 110XXXXX 297 351 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 352 if ($c+1 >= $strlen_var) { 353 $c += 1; 354 $ascii .= '?'; 355 break; 356 } 357 298 358 $char = pack('C*', $ord_var_c, ord($var{$c + 1})); 299 359 $c += 1; … … 303 363 304 364 case (($ord_var_c & 0xF0) == 0xE0): 365 if ($c+2 >= $strlen_var) { 366 $c += 2; 367 $ascii .= '?'; 368 break; 369 } 305 370 // characters U-00000800 - U-0000FFFF, mask 1110XXXX 306 371 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 307 372 $char = pack('C*', $ord_var_c, 308 ord($var{$c + 1}),309 ord($var{$c + 2}));373 @ord($var{$c + 1}), 374 @ord($var{$c + 2})); 310 375 $c += 2; 311 376 $utf16 = $this->utf82utf16($char); … … 314 379 315 380 case (($ord_var_c & 0xF8) == 0xF0): 381 if ($c+3 >= $strlen_var) { 382 $c += 3; 383 $ascii .= '?'; 384 break; 385 } 316 386 // characters U-00010000 - U-001FFFFF, mask 11110XXX 317 387 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 … … 328 398 // characters U-00200000 - U-03FFFFFF, mask 111110XX 329 399 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 400 if ($c+4 >= $strlen_var) { 401 $c += 4; 402 $ascii .= '?'; 403 break; 404 } 330 405 $char = pack('C*', $ord_var_c, 331 406 ord($var{$c + 1}), … … 339 414 340 415 case (($ord_var_c & 0xFE) == 0xFC): 416 if ($c+5 >= $strlen_var) { 417 $c += 5; 418 $ascii .= '?'; 419 break; 420 } 341 421 // characters U-04000000 - U-7FFFFFFF, mask 1111110X 342 422 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 … … 353 433 } 354 434 } 355 356 return '"'.$ascii.'"'; 435 return '"'.$ascii.'"'; 357 436 358 437 case 'array': … … 391 470 392 471 // treat it like a regular array 393 $elements = array_map(array($this, ' encode'), $var);472 $elements = array_map(array($this, '_encode'), $var); 394 473 395 474 foreach($elements as $element) { … … 402 481 403 482 case 'object': 483 484 // support toJSON methods. 485 if (($this->use & SERVICES_JSON_USE_TO_JSON) && method_exists($var, 'toJSON')) { 486 // this may end up allowing unlimited recursion 487 // so we check the return value to make sure it's not got the same method. 488 $recode = $var->toJSON(); 489 490 if (method_exists($recode, 'toJSON')) { 491 492 return ($this->use & SERVICES_JSON_SUPPRESS_ERRORS) 493 ? 'null' 494 : new Services_JSON_Error(class_name($var). 495 " toJSON returned an object with a toJSON method."); 496 497 } 498 499 return $this->_encode( $recode ); 500 } 501 404 502 $vars = get_object_vars($var); 405 503 406 504 $properties = array_map(array($this, 'name_value'), 407 505 array_keys($vars), … … 434 532 function name_value($name, $value) 435 533 { 436 $encoded_value = $this-> encode($value);534 $encoded_value = $this->_encode($value); 437 535 438 536 if(Services_JSON::isError($encoded_value)) { … … 440 538 } 441 539 442 return $this-> encode(strval($name)) . ':' . $encoded_value;540 return $this->_encode(strval($name)) . ':' . $encoded_value; 443 541 } 444 542 … … 513 611 } elseif (preg_match('/^("|\').*(\1)$/s', $str, $m) && $m[1] == $m[2]) { 514 612 // STRINGS RETURNED IN UTF-8 FORMAT 515 $delim = substr($str, 0, 1);516 $chrs = substr($str, 1, -1);613 $delim = $this->substr8($str, 0, 1); 614 $chrs = $this->substr8($str, 1, -1); 517 615 $utf8 = ''; 518 $strlen_chrs = strlen($chrs);616 $strlen_chrs = $this->strlen8($chrs); 519 617 520 618 for ($c = 0; $c < $strlen_chrs; ++$c) { 521 619 522 $substr_chrs_c_2 = substr($chrs, $c, 2);620 $substr_chrs_c_2 = $this->substr8($chrs, $c, 2); 523 621 $ord_chrs_c = ord($chrs{$c}); 524 622 … … 555 653 break; 556 654 557 case preg_match('/\\\u[0-9A-F]{4}/i', substr($chrs, $c, 6)):655 case preg_match('/\\\u[0-9A-F]{4}/i', $this->substr8($chrs, $c, 6)): 558 656 // single, escaped unicode character 559 $utf16 = chr(hexdec( substr($chrs, ($c + 2), 2)))560 . chr(hexdec( substr($chrs, ($c + 4), 2)));657 $utf16 = chr(hexdec($this->substr8($chrs, ($c + 2), 2))) 658 . chr(hexdec($this->substr8($chrs, ($c + 4), 2))); 561 659 $utf8 .= $this->utf162utf8($utf16); 562 660 $c += 5; … … 570 668 // characters U-00000080 - U-000007FF, mask 110XXXXX 571 669 //see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 572 $utf8 .= substr($chrs, $c, 2);670 $utf8 .= $this->substr8($chrs, $c, 2); 573 671 ++$c; 574 672 break; … … 577 675 // characters U-00000800 - U-0000FFFF, mask 1110XXXX 578 676 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 579 $utf8 .= substr($chrs, $c, 3);677 $utf8 .= $this->substr8($chrs, $c, 3); 580 678 $c += 2; 581 679 break; … … 584 682 // characters U-00010000 - U-001FFFFF, mask 11110XXX 585 683 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 586 $utf8 .= substr($chrs, $c, 4);684 $utf8 .= $this->substr8($chrs, $c, 4); 587 685 $c += 3; 588 686 break; … … 591 689 // characters U-00200000 - U-03FFFFFF, mask 111110XX 592 690 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 593 $utf8 .= substr($chrs, $c, 5);691 $utf8 .= $this->substr8($chrs, $c, 5); 594 692 $c += 4; 595 693 break; … … 598 696 // characters U-04000000 - U-7FFFFFFF, mask 1111110X 599 697 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 600 $utf8 .= substr($chrs, $c, 6);698 $utf8 .= $this->substr8($chrs, $c, 6); 601 699 $c += 5; 602 700 break; … … 628 726 'delim' => false)); 629 727 630 $chrs = substr($str, 1, -1);728 $chrs = $this->substr8($str, 1, -1); 631 729 $chrs = $this->reduce_string($chrs); 632 730 … … 643 741 //print("\nparsing {$chrs}\n"); 644 742 645 $strlen_chrs = strlen($chrs);743 $strlen_chrs = $this->strlen8($chrs); 646 744 647 745 for ($c = 0; $c <= $strlen_chrs; ++$c) { 648 746 649 747 $top = end($stk); 650 $substr_chrs_c_2 = substr($chrs, $c, 2);748 $substr_chrs_c_2 = $this->substr8($chrs, $c, 2); 651 749 652 750 if (($c == $strlen_chrs) || (($chrs{$c} == ',') && ($top['what'] == SERVICES_JSON_SLICE))) { 653 751 // found a comma that is not inside a string, array, etc., 654 752 // OR we've reached the end of the character list 655 $slice = substr($chrs, $top['where'], ($c - $top['where']));753 $slice = $this->substr8($chrs, $top['where'], ($c - $top['where'])); 656 754 array_push($stk, array('what' => SERVICES_JSON_SLICE, 'where' => ($c + 1), 'delim' => false)); 657 //print("Found split at {$c}: ". substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");755 //print("Found split at {$c}: ".$this->substr8($chrs, $top['where'], (1 + $c - $top['where']))."\n"); 658 756 659 757 if (reset($stk) == SERVICES_JSON_IN_ARR) { … … 668 766 $parts = array(); 669 767 670 if (preg_match('/^\s*(["\'].*[^\\\]["\'])\s*:\s*(\S.*),?$/Uis', $slice, $parts)) {671 768 if (preg_match('/^\s*(["\'].*[^\\\]["\'])\s*:/Uis', $slice, $parts)) { 769 // "name":value pair 672 770 $key = $this->decode($parts[1]); 673 $val = $this->decode($parts[2]); 674 771 $val = $this->decode(trim(substr($slice, strlen($parts[0])), ", \t\n\r\0\x0B")); 675 772 if ($this->use & SERVICES_JSON_LOOSE_TYPE) { 676 773 $obj[$key] = $val; … … 678 775 $obj->$key = $val; 679 776 } 680 } elseif (preg_match('/^\s*(\w+)\s*: \s*(\S.*),?$/Uis', $slice, $parts)) {777 } elseif (preg_match('/^\s*(\w+)\s*:/Uis', $slice, $parts)) { 681 778 // name:value pair, where name is unquoted 682 779 $key = $parts[1]; 683 $val = $this->decode( $parts[2]);780 $val = $this->decode(trim(substr($slice, strlen($parts[0])), ", \t\n\r\0\x0B")); 684 781 685 782 if ($this->use & SERVICES_JSON_LOOSE_TYPE) { … … 699 796 } elseif (($chrs{$c} == $top['delim']) && 700 797 ($top['what'] == SERVICES_JSON_IN_STR) && 701 (( strlen(substr($chrs, 0, $c)) - strlen(rtrim(substr($chrs, 0, $c), '\\'))) % 2 != 1)) {798 (($this->strlen8($this->substr8($chrs, 0, $c)) - $this->strlen8(rtrim($this->substr8($chrs, 0, $c), '\\'))) % 2 != 1)) { 702 799 // found a quote, we're in a string, and it's not escaped 703 800 // we know that it's not escaped becase there is _not_ an 704 801 // odd number of backslashes at the end of the string so far 705 802 array_pop($stk); 706 //print("Found end of string at {$c}: ". substr($chrs, $top['where'], (1 + 1 + $c - $top['where']))."\n");803 //print("Found end of string at {$c}: ".$this->substr8($chrs, $top['where'], (1 + 1 + $c - $top['where']))."\n"); 707 804 708 805 } elseif (($chrs{$c} == '[') && … … 715 812 // found a right-bracket, and we're in an array 716 813 array_pop($stk); 717 //print("Found end of array at {$c}: ". substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");814 //print("Found end of array at {$c}: ".$this->substr8($chrs, $top['where'], (1 + $c - $top['where']))."\n"); 718 815 719 816 } elseif (($chrs{$c} == '{') && … … 726 823 // found a right-brace, and we're in an object 727 824 array_pop($stk); 728 //print("Found end of object at {$c}: ". substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");825 //print("Found end of object at {$c}: ".$this->substr8($chrs, $top['where'], (1 + $c - $top['where']))."\n"); 729 826 730 827 } elseif (($substr_chrs_c_2 == '/*') && … … 743 840 $chrs = substr_replace($chrs, ' ', $i, 1); 744 841 745 //print("Found end of comment at {$c}: ". substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");842 //print("Found end of comment at {$c}: ".$this->substr8($chrs, $top['where'], (1 + $c - $top['where']))."\n"); 746 843 747 844 } … … 775 872 return false; 776 873 } 874 875 /** 876 * Calculates length of string in bytes 877 * @param string 878 * @return integer length 879 */ 880 function strlen8( $str ) 881 { 882 if ( $this->_mb_strlen ) { 883 return mb_strlen( $str, "8bit" ); 884 } 885 return strlen( $str ); 886 } 887 888 /** 889 * Returns part of a string, interpreting $start and $length as number of bytes. 890 * @param string 891 * @param integer start 892 * @param integer length 893 * @return integer length 894 */ 895 function substr8( $string, $start, $length=false ) 896 { 897 if ( $length === false ) { 898 $length = $this->strlen8( $string ) - $start; 899 } 900 if ( $this->_mb_substr ) { 901 return mb_substr( $string, $start, $length, "8bit" ); 902 } 903 return substr( $string, $start, $length ); 904 } 905 777 906 } 778 907 … … 801 930 } 802 931 } 803 932 804 933 } 805 806 ?>
Note: See TracChangeset
for help on using the changeset viewer.