web/wp-includes/class-json.php
changeset 194 32102edaa81b
parent 136 bde1974c263b
equal deleted inserted replaced
193:2f6f6f7551ca 194:32102edaa81b
     1 <?php
     1 <?php
       
     2 if ( !class_exists( 'Services_JSON' ) ) :
     2 /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
     3 /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
     3 /**
     4 /**
     4  * Converts to and from JSON format.
     5  * Converts to and from JSON format.
     5  *
     6  *
     6  * JSON (JavaScript Object Notation) is a lightweight data-interchange
     7  * JSON (JavaScript Object Notation) is a lightweight data-interchange
   150 		// oh please oh please oh please oh please oh please
   151 		// oh please oh please oh please oh please oh please
   151 		if(function_exists('mb_convert_encoding')) {
   152 		if(function_exists('mb_convert_encoding')) {
   152 			return mb_convert_encoding($utf16, 'UTF-8', 'UTF-16');
   153 			return mb_convert_encoding($utf16, 'UTF-8', 'UTF-16');
   153 		}
   154 		}
   154 
   155 
   155 		$bytes = (ord($utf16{0}) << 8) | ord($utf16{1});
   156 		$bytes = (ord($utf16[0]) << 8) | ord($utf16[1]);
   156 
   157 
   157 		switch(true) {
   158 		switch(true) {
   158 			case ((0x7F & $bytes) == $bytes):
   159 			case ((0x7F & $bytes) == $bytes):
   159 				// this case should never be reached, because we are in ASCII range
   160 				// 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 				// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
   203 				return $utf8;
   204 				return $utf8;
   204 
   205 
   205 			case 2:
   206 			case 2:
   206 				// return a UTF-16 character from a 2-byte UTF-8 char
   207 				// 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 				// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
   208 				return chr(0x07 & (ord($utf8{0}) >> 2))
   209 				return chr(0x07 & (ord($utf8[0]) >> 2))
   209 					. chr((0xC0 & (ord($utf8{0}) << 6))
   210 					. chr((0xC0 & (ord($utf8[0]) << 6))
   210 						| (0x3F & ord($utf8{1})));
   211 						| (0x3F & ord($utf8[1])));
   211 
   212 
   212 			case 3:
   213 			case 3:
   213 				// return a UTF-16 character from a 3-byte UTF-8 char
   214 				// 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 				// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
   215 				return chr((0xF0 & (ord($utf8{0}) << 4))
   216 				return chr((0xF0 & (ord($utf8[0]) << 4))
   216 						| (0x0F & (ord($utf8{1}) >> 2)))
   217 						| (0x0F & (ord($utf8[1]) >> 2)))
   217 					. chr((0xC0 & (ord($utf8{1}) << 6))
   218 					. chr((0xC0 & (ord($utf8[1]) << 6))
   218 						| (0x7F & ord($utf8{2})));
   219 						| (0x7F & ord($utf8[2])));
   219 		}
   220 		}
   220 
   221 
   221 		// ignoring UTF-32 for now, sorry
   222 		// ignoring UTF-32 for now, sorry
   222 		return '';
   223 		return '';
   223 	}
   224 	}
   290 				* Iterate over every character in the string,
   291 				* Iterate over every character in the string,
   291 				* escaping with a slash or encoding to UTF-8 where necessary
   292 				* escaping with a slash or encoding to UTF-8 where necessary
   292 				*/
   293 				*/
   293 				for ($c = 0; $c < $strlen_var; ++$c) {
   294 				for ($c = 0; $c < $strlen_var; ++$c) {
   294 
   295 
   295 					$ord_var_c = ord($var{$c});
   296 					$ord_var_c = ord($var[$c]);
   296 
   297 
   297 					switch (true) {
   298 					switch (true) {
   298 						case $ord_var_c == 0x08:
   299 						case $ord_var_c == 0x08:
   299 							$ascii .= '\b';
   300 							$ascii .= '\b';
   300 							break;
   301 							break;
   313 
   314 
   314 						case $ord_var_c == 0x22:
   315 						case $ord_var_c == 0x22:
   315 						case $ord_var_c == 0x2F:
   316 						case $ord_var_c == 0x2F:
   316 						case $ord_var_c == 0x5C:
   317 						case $ord_var_c == 0x5C:
   317 							// double quote, slash, slosh
   318 							// double quote, slash, slosh
   318 							$ascii .= '\\'.$var{$c};
   319 							$ascii .= '\\'.$var[$c];
   319 							break;
   320 							break;
   320 
   321 
   321 						case (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)):
   322 						case (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)):
   322 							// characters U-00000000 - U-0000007F (same as ASCII)
   323 							// characters U-00000000 - U-0000007F (same as ASCII)
   323 							$ascii .= $var{$c};
   324 							$ascii .= $var[$c];
   324 							break;
   325 							break;
   325 
   326 
   326 						case (($ord_var_c & 0xE0) == 0xC0):
   327 						case (($ord_var_c & 0xE0) == 0xC0):
   327 							// characters U-00000080 - U-000007FF, mask 110XXXXX
   328 							// characters U-00000080 - U-000007FF, mask 110XXXXX
   328 							// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
   329 							// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
   330 								$c += 1;
   331 								$c += 1;
   331 								$ascii .= '?';
   332 								$ascii .= '?';
   332 								break;
   333 								break;
   333 							}
   334 							}
   334 
   335 
   335 							$char = pack('C*', $ord_var_c, ord($var{$c + 1}));
   336 							$char = pack('C*', $ord_var_c, ord($var[$c + 1]));
   336 							$c += 1;
   337 							$c += 1;
   337 							$utf16 = $this->utf82utf16($char);
   338 							$utf16 = $this->utf82utf16($char);
   338 							$ascii .= sprintf('\u%04s', bin2hex($utf16));
   339 							$ascii .= sprintf('\u%04s', bin2hex($utf16));
   339 							break;
   340 							break;
   340 
   341 
   345 								break;
   346 								break;
   346 							}
   347 							}
   347 							// characters U-00000800 - U-0000FFFF, mask 1110XXXX
   348 							// characters U-00000800 - U-0000FFFF, mask 1110XXXX
   348 							// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
   349 							// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
   349 							$char = pack('C*', $ord_var_c,
   350 							$char = pack('C*', $ord_var_c,
   350 										@ord($var{$c + 1}),
   351 										@ord($var[$c + 1]),
   351 										@ord($var{$c + 2}));
   352 										@ord($var[$c + 2]));
   352 							$c += 2;
   353 							$c += 2;
   353 							$utf16 = $this->utf82utf16($char);
   354 							$utf16 = $this->utf82utf16($char);
   354 							$ascii .= sprintf('\u%04s', bin2hex($utf16));
   355 							$ascii .= sprintf('\u%04s', bin2hex($utf16));
   355 							break;
   356 							break;
   356 
   357 
   361 								break;
   362 								break;
   362 							}
   363 							}
   363 							// characters U-00010000 - U-001FFFFF, mask 11110XXX
   364 							// characters U-00010000 - U-001FFFFF, mask 11110XXX
   364 							// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
   365 							// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
   365 							$char = pack('C*', $ord_var_c,
   366 							$char = pack('C*', $ord_var_c,
   366 										ord($var{$c + 1}),
   367 										ord($var[$c + 1]),
   367 										ord($var{$c + 2}),
   368 										ord($var[$c + 2]),
   368 										ord($var{$c + 3}));
   369 										ord($var[$c + 3]));
   369 							$c += 3;
   370 							$c += 3;
   370 							$utf16 = $this->utf82utf16($char);
   371 							$utf16 = $this->utf82utf16($char);
   371 							$ascii .= sprintf('\u%04s', bin2hex($utf16));
   372 							$ascii .= sprintf('\u%04s', bin2hex($utf16));
   372 							break;
   373 							break;
   373 
   374 
   378 								$c += 4;
   379 								$c += 4;
   379 								$ascii .= '?';
   380 								$ascii .= '?';
   380 								break;
   381 								break;
   381 							}
   382 							}
   382 							$char = pack('C*', $ord_var_c,
   383 							$char = pack('C*', $ord_var_c,
   383 										ord($var{$c + 1}),
   384 										ord($var[$c + 1]),
   384 										ord($var{$c + 2}),
   385 										ord($var[$c + 2]),
   385 										ord($var{$c + 3}),
   386 										ord($var[$c + 3]),
   386 										ord($var{$c + 4}));
   387 										ord($var[$c + 4]));
   387 							$c += 4;
   388 							$c += 4;
   388 							$utf16 = $this->utf82utf16($char);
   389 							$utf16 = $this->utf82utf16($char);
   389 							$ascii .= sprintf('\u%04s', bin2hex($utf16));
   390 							$ascii .= sprintf('\u%04s', bin2hex($utf16));
   390 							break;
   391 							break;
   391 
   392 
   396 								break;
   397 								break;
   397 							}
   398 							}
   398 							// characters U-04000000 - U-7FFFFFFF, mask 1111110X
   399 							// characters U-04000000 - U-7FFFFFFF, mask 1111110X
   399 							// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
   400 							// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
   400 							$char = pack('C*', $ord_var_c,
   401 							$char = pack('C*', $ord_var_c,
   401 										ord($var{$c + 1}),
   402 										ord($var[$c + 1]),
   402 										ord($var{$c + 2}),
   403 										ord($var[$c + 2]),
   403 										ord($var{$c + 3}),
   404 										ord($var[$c + 3]),
   404 										ord($var{$c + 4}),
   405 										ord($var[$c + 4]),
   405 										ord($var{$c + 5}));
   406 										ord($var[$c + 5]));
   406 							$c += 5;
   407 							$c += 5;
   407 							$utf16 = $this->utf82utf16($char);
   408 							$utf16 = $this->utf82utf16($char);
   408 							$ascii .= sprintf('\u%04s', bin2hex($utf16));
   409 							$ascii .= sprintf('\u%04s', bin2hex($utf16));
   409 							break;
   410 							break;
   410 					}
   411 					}
   574 					$strlen_chrs = strlen($chrs);
   575 					$strlen_chrs = strlen($chrs);
   575 
   576 
   576 					for ($c = 0; $c < $strlen_chrs; ++$c) {
   577 					for ($c = 0; $c < $strlen_chrs; ++$c) {
   577 
   578 
   578 						$substr_chrs_c_2 = substr($chrs, $c, 2);
   579 						$substr_chrs_c_2 = substr($chrs, $c, 2);
   579 						$ord_chrs_c = ord($chrs{$c});
   580 						$ord_chrs_c = ord($chrs[$c]);
   580 
   581 
   581 						switch (true) {
   582 						switch (true) {
   582 							case $substr_chrs_c_2 == '\b':
   583 							case $substr_chrs_c_2 == '\b':
   583 								$utf8 .= chr(0x08);
   584 								$utf8 .= chr(0x08);
   584 								++$c;
   585 								++$c;
   604 							case $substr_chrs_c_2 == '\\\'':
   605 							case $substr_chrs_c_2 == '\\\'':
   605 							case $substr_chrs_c_2 == '\\\\':
   606 							case $substr_chrs_c_2 == '\\\\':
   606 							case $substr_chrs_c_2 == '\\/':
   607 							case $substr_chrs_c_2 == '\\/':
   607 								if (($delim == '"' && $substr_chrs_c_2 != '\\\'') ||
   608 								if (($delim == '"' && $substr_chrs_c_2 != '\\\'') ||
   608 								($delim == "'" && $substr_chrs_c_2 != '\\"')) {
   609 								($delim == "'" && $substr_chrs_c_2 != '\\"')) {
   609 									$utf8 .= $chrs{++$c};
   610 									$utf8 .= $chrs[++$c];
   610 								}
   611 								}
   611 								break;
   612 								break;
   612 
   613 
   613 							case preg_match('/\\\u[0-9A-F]{4}/i', substr($chrs, $c, 6)):
   614 							case preg_match('/\\\u[0-9A-F]{4}/i', substr($chrs, $c, 6)):
   614 								// single, escaped unicode character
   615 								// single, escaped unicode character
   617 								$utf8 .= $this->utf162utf8($utf16);
   618 								$utf8 .= $this->utf162utf8($utf16);
   618 								$c += 5;
   619 								$c += 5;
   619 								break;
   620 								break;
   620 
   621 
   621 							case ($ord_chrs_c >= 0x20) && ($ord_chrs_c <= 0x7F):
   622 							case ($ord_chrs_c >= 0x20) && ($ord_chrs_c <= 0x7F):
   622 								$utf8 .= $chrs{$c};
   623 								$utf8 .= $chrs[$c];
   623 								break;
   624 								break;
   624 
   625 
   625 							case ($ord_chrs_c & 0xE0) == 0xC0:
   626 							case ($ord_chrs_c & 0xE0) == 0xC0:
   626 								// characters U-00000080 - U-000007FF, mask 110XXXXX
   627 								// characters U-00000080 - U-000007FF, mask 110XXXXX
   627 								//see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
   628 								//see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
   664 					return $utf8;
   665 					return $utf8;
   665 
   666 
   666 				} elseif (preg_match('/^\[.*\]$/s', $str) || preg_match('/^\{.*\}$/s', $str)) {
   667 				} elseif (preg_match('/^\[.*\]$/s', $str) || preg_match('/^\{.*\}$/s', $str)) {
   667 					// array, or object notation
   668 					// array, or object notation
   668 
   669 
   669 					if ($str{0} == '[') {
   670 					if ($str[0] == '[') {
   670 						$stk = array(SERVICES_JSON_IN_ARR);
   671 						$stk = array(SERVICES_JSON_IN_ARR);
   671 						$arr = array();
   672 						$arr = array();
   672 					} else {
   673 					} else {
   673 						if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
   674 						if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
   674 							$stk = array(SERVICES_JSON_IN_OBJ);
   675 							$stk = array(SERVICES_JSON_IN_OBJ);
   703 					for ($c = 0; $c <= $strlen_chrs; ++$c) {
   704 					for ($c = 0; $c <= $strlen_chrs; ++$c) {
   704 
   705 
   705 						$top = end($stk);
   706 						$top = end($stk);
   706 						$substr_chrs_c_2 = substr($chrs, $c, 2);
   707 						$substr_chrs_c_2 = substr($chrs, $c, 2);
   707 
   708 
   708 						if (($c == $strlen_chrs) || (($chrs{$c} == ',') && ($top['what'] == SERVICES_JSON_SLICE))) {
   709 						if (($c == $strlen_chrs) || (($chrs[$c] == ',') && ($top['what'] == SERVICES_JSON_SLICE))) {
   709 							// found a comma that is not inside a string, array, etc.,
   710 							// found a comma that is not inside a string, array, etc.,
   710 							// OR we've reached the end of the character list
   711 							// OR we've reached the end of the character list
   711 							$slice = substr($chrs, $top['where'], ($c - $top['where']));
   712 							$slice = substr($chrs, $top['where'], ($c - $top['where']));
   712 							array_push($stk, array('what' => SERVICES_JSON_SLICE, 'where' => ($c + 1), 'delim' => false));
   713 							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 							//print("Found split at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
   745 									}
   746 									}
   746 								}
   747 								}
   747 
   748 
   748 							}
   749 							}
   749 
   750 
   750 						} elseif ((($chrs{$c} == '"') || ($chrs{$c} == "'")) && ($top['what'] != SERVICES_JSON_IN_STR)) {
   751 						} elseif ((($chrs[$c] == '"') || ($chrs[$c] == "'")) && ($top['what'] != SERVICES_JSON_IN_STR)) {
   751 							// found a quote, and we are not inside a string
   752 							// 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 							array_push($stk, array('what' => SERVICES_JSON_IN_STR, 'where' => $c, 'delim' => $chrs[$c]));
   753 							//print("Found start of string at {$c}\n");
   754 							//print("Found start of string at {$c}\n");
   754 
   755 
   755 						} elseif (($chrs{$c} == $top['delim']) &&
   756 						} elseif (($chrs[$c] == $top['delim']) &&
   756 								($top['what'] == SERVICES_JSON_IN_STR) &&
   757 								($top['what'] == SERVICES_JSON_IN_STR) &&
   757 								((strlen(substr($chrs, 0, $c)) - strlen(rtrim(substr($chrs, 0, $c), '\\'))) % 2 != 1)) {
   758 								((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 							// 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 							// 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 							// odd number of backslashes at the end of the string so far
   761 							array_pop($stk);
   762 							array_pop($stk);
   762 							//print("Found end of string at {$c}: ".substr($chrs, $top['where'], (1 + 1 + $c - $top['where']))."\n");
   763 							//print("Found end of string at {$c}: ".substr($chrs, $top['where'], (1 + 1 + $c - $top['where']))."\n");
   763 
   764 
   764 						} elseif (($chrs{$c} == '[') &&
   765 						} elseif (($chrs[$c] == '[') &&
   765 								in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
   766 								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 							// 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 							array_push($stk, array('what' => SERVICES_JSON_IN_ARR, 'where' => $c, 'delim' => false));
   768 							//print("Found start of array at {$c}\n");
   769 							//print("Found start of array at {$c}\n");
   769 
   770 
   770 						} elseif (($chrs{$c} == ']') && ($top['what'] == SERVICES_JSON_IN_ARR)) {
   771 						} elseif (($chrs[$c] == ']') && ($top['what'] == SERVICES_JSON_IN_ARR)) {
   771 							// found a right-bracket, and we're in an array
   772 							// found a right-bracket, and we're in an array
   772 							array_pop($stk);
   773 							array_pop($stk);
   773 							//print("Found end of array at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
   774 							//print("Found end of array at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
   774 
   775 
   775 						} elseif (($chrs{$c} == '{') &&
   776 						} elseif (($chrs[$c] == '{') &&
   776 								in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
   777 								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 							// 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 							array_push($stk, array('what' => SERVICES_JSON_IN_OBJ, 'where' => $c, 'delim' => false));
   779 							//print("Found start of object at {$c}\n");
   780 							//print("Found start of object at {$c}\n");
   780 
   781 
   781 						} elseif (($chrs{$c} == '}') && ($top['what'] == SERVICES_JSON_IN_OBJ)) {
   782 						} elseif (($chrs[$c] == '}') && ($top['what'] == SERVICES_JSON_IN_OBJ)) {
   782 							// found a right-brace, and we're in an object
   783 							// found a right-brace, and we're in an object
   783 							array_pop($stk);
   784 							array_pop($stk);
   784 							//print("Found end of object at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
   785 							//print("Found end of object at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
   785 
   786 
   786 						} elseif (($substr_chrs_c_2 == '/*') &&
   787 						} elseif (($substr_chrs_c_2 == '/*') &&
   856 
   857 
   857 		}
   858 		}
   858 	}
   859 	}
   859 
   860 
   860 }
   861 }
       
   862 endif;