Update to Services_JSON 1.0.3. props bpetty. fixes #21568.

git-svn-id: http://core.svn.wordpress.org/trunk@23376 1a063a9b-81f0-0310-95a4-ce76da25c4cd
This commit is contained in:
Andrew Nacin 2013-02-02 02:09:01 +00:00
parent 60a4d36571
commit cbabdafbed

View File

@ -1,5 +1,5 @@
<?php <?php
if ( !class_exists( 'Services_JSON' ) ) : if ( ! class_exists( 'Services_JSON' ) ) :
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
/** /**
* Converts to and from JSON format. * Converts to and from JSON format.
@ -51,7 +51,7 @@ if ( !class_exists( 'Services_JSON' ) ) :
* @author Matt Knapp <mdknapp[at]gmail[dot]com> * @author Matt Knapp <mdknapp[at]gmail[dot]com>
* @author Brett Stimmerman <brettstimmerman[at]gmail[dot]com> * @author Brett Stimmerman <brettstimmerman[at]gmail[dot]com>
* @copyright 2005 Michal Migurski * @copyright 2005 Michal Migurski
* @version CVS: $Id: JSON.php 288200 2009-09-09 15:41:29Z alan_k $ * @version CVS: $Id: JSON.php 305040 2010-11-02 23:19:03Z alan_k $
* @license http://www.opensource.org/licenses/bsd-license.php * @license http://www.opensource.org/licenses/bsd-license.php
* @link http://pear.php.net/pepr/pepr-proposal-show.php?id=198 * @link http://pear.php.net/pepr/pepr-proposal-show.php?id=198
*/ */
@ -91,6 +91,11 @@ define('SERVICES_JSON_LOOSE_TYPE', 16);
*/ */
define('SERVICES_JSON_SUPPRESS_ERRORS', 32); define('SERVICES_JSON_SUPPRESS_ERRORS', 32);
/**
* Behavior switch for Services_JSON::decode()
*/
define('SERVICES_JSON_USE_TO_JSON', 64);
/** /**
* Converts to and from JSON format. * Converts to and from JSON format.
* *
@ -129,11 +134,23 @@ class Services_JSON
* By default, a deeply-nested resource will * By default, a deeply-nested resource will
* bubble up with an error, so all return values * bubble up with an error, so all return values
* from encode() should be checked with isError() * from encode() should be checked with isError()
* - SERVICES_JSON_USE_TO_JSON: call toJSON when serializing objects
* It serializes the return value from the toJSON call rather
* than the object it'self, toJSON can return associative arrays,
* strings or numbers, if you return an object, make sure it does
* not have a toJSON method, otherwise an error will occur.
*/ */
function Services_JSON($use = 0) function Services_JSON($use = 0)
{ {
$this->use = $use; $this->use = $use;
$this->_mb_strlen = function_exists('mb_strlen');
$this->_mb_convert_encoding = function_exists('mb_convert_encoding');
$this->_mb_substr = function_exists('mb_substr');
} }
// private - cache the mbstring lookup results..
var $_mb_strlen = false;
var $_mb_substr = false;
var $_mb_convert_encoding = false;
/** /**
* convert a string from one UTF-16 char to one UTF-8 char * convert a string from one UTF-16 char to one UTF-8 char
@ -149,11 +166,11 @@ class Services_JSON
function utf162utf8($utf16) function utf162utf8($utf16)
{ {
// oh please oh please oh please oh please oh please // oh please oh please oh please oh please oh please
if(function_exists('mb_convert_encoding')) { if($this->_mb_convert_encoding) {
return mb_convert_encoding($utf16, 'UTF-8', 'UTF-16'); return mb_convert_encoding($utf16, 'UTF-8', 'UTF-16');
} }
$bytes = (ord($utf16[0]) << 8) | ord($utf16[1]); $bytes = (ord($utf16{0}) << 8) | ord($utf16{1});
switch(true) { switch(true) {
case ((0x7F & $bytes) == $bytes): case ((0x7F & $bytes) == $bytes):
@ -193,11 +210,11 @@ class Services_JSON
function utf82utf16($utf8) function utf82utf16($utf8)
{ {
// oh please oh please oh please oh please oh please // oh please oh please oh please oh please oh please
if(function_exists('mb_convert_encoding')) { if($this->_mb_convert_encoding) {
return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8'); return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8');
} }
switch(strlen($utf8)) { switch($this->strlen8($utf8)) {
case 1: case 1:
// this case should never be reached, because we are in ASCII range // this case should never be reached, because we are in ASCII range
// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
@ -206,17 +223,17 @@ class Services_JSON
case 2: case 2:
// return a UTF-16 character from a 2-byte UTF-8 char // return a UTF-16 character from a 2-byte UTF-8 char
// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
return chr(0x07 & (ord($utf8[0]) >> 2)) return chr(0x07 & (ord($utf8{0}) >> 2))
. chr((0xC0 & (ord($utf8[0]) << 6)) . chr((0xC0 & (ord($utf8{0}) << 6))
| (0x3F & ord($utf8[1]))); | (0x3F & ord($utf8{1})));
case 3: case 3:
// return a UTF-16 character from a 3-byte UTF-8 char // return a UTF-16 character from a 3-byte UTF-8 char
// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
return chr((0xF0 & (ord($utf8[0]) << 4)) return chr((0xF0 & (ord($utf8{0}) << 4))
| (0x0F & (ord($utf8[1]) >> 2))) | (0x0F & (ord($utf8{1}) >> 2)))
. chr((0xC0 & (ord($utf8[1]) << 6)) . chr((0xC0 & (ord($utf8{1}) << 6))
| (0x7F & ord($utf8[2]))); | (0x7F & ord($utf8{2})));
} }
// ignoring UTF-32 for now, sorry // ignoring UTF-32 for now, sorry
@ -237,10 +254,10 @@ class Services_JSON
function encode($var) function encode($var)
{ {
header('Content-type: application/json'); header('Content-type: application/json');
return $this->_encode($var); return $this->encodeUnsafe($var);
} }
/** /**
* encodes an arbitrary variable into JSON format without JSON Header - warning - may allow CSS!!!!) * encodes an arbitrary variable into JSON format without JSON Header - warning - may allow XSS!!!!)
* *
* @param mixed $var any number, boolean, string, array, or object to be encoded. * @param mixed $var any number, boolean, string, array, or object to be encoded.
* see argument 1 to Services_JSON() above for array-parsing behavior. * see argument 1 to Services_JSON() above for array-parsing behavior.
@ -252,7 +269,13 @@ class Services_JSON
*/ */
function encodeUnsafe($var) function encodeUnsafe($var)
{ {
return $this->_encode($var); // see bug #16908 - regarding numeric locale printing
$lc = setlocale(LC_NUMERIC, 0);
setlocale(LC_NUMERIC, 'C');
$ret = $this->_encode($var);
setlocale(LC_NUMERIC, $lc);
return $ret;
} }
/** /**
* PRIVATE CODE that does the work of encodes an arbitrary variable into JSON format * PRIVATE CODE that does the work of encodes an arbitrary variable into JSON format
@ -285,7 +308,7 @@ class Services_JSON
case 'string': case 'string':
// STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT // STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT
$ascii = ''; $ascii = '';
$strlen_var = strlen($var); $strlen_var = $this->strlen8($var);
/* /*
* Iterate over every character in the string, * Iterate over every character in the string,
@ -293,7 +316,7 @@ class Services_JSON
*/ */
for ($c = 0; $c < $strlen_var; ++$c) { for ($c = 0; $c < $strlen_var; ++$c) {
$ord_var_c = ord($var[$c]); $ord_var_c = ord($var{$c});
switch (true) { switch (true) {
case $ord_var_c == 0x08: case $ord_var_c == 0x08:
@ -316,12 +339,12 @@ class Services_JSON
case $ord_var_c == 0x2F: case $ord_var_c == 0x2F:
case $ord_var_c == 0x5C: case $ord_var_c == 0x5C:
// double quote, slash, slosh // double quote, slash, slosh
$ascii .= '\\'.$var[$c]; $ascii .= '\\'.$var{$c};
break; break;
case (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)): case (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)):
// characters U-00000000 - U-0000007F (same as ASCII) // characters U-00000000 - U-0000007F (same as ASCII)
$ascii .= $var[$c]; $ascii .= $var{$c};
break; break;
case (($ord_var_c & 0xE0) == 0xC0): case (($ord_var_c & 0xE0) == 0xC0):
@ -333,7 +356,7 @@ class Services_JSON
break; break;
} }
$char = pack('C*', $ord_var_c, ord($var[$c + 1])); $char = pack('C*', $ord_var_c, ord($var{$c + 1}));
$c += 1; $c += 1;
$utf16 = $this->utf82utf16($char); $utf16 = $this->utf82utf16($char);
$ascii .= sprintf('\u%04s', bin2hex($utf16)); $ascii .= sprintf('\u%04s', bin2hex($utf16));
@ -348,8 +371,8 @@ class Services_JSON
// characters U-00000800 - U-0000FFFF, mask 1110XXXX // characters U-00000800 - U-0000FFFF, mask 1110XXXX
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
$char = pack('C*', $ord_var_c, $char = pack('C*', $ord_var_c,
@ord($var[$c + 1]), @ord($var{$c + 1}),
@ord($var[$c + 2])); @ord($var{$c + 2}));
$c += 2; $c += 2;
$utf16 = $this->utf82utf16($char); $utf16 = $this->utf82utf16($char);
$ascii .= sprintf('\u%04s', bin2hex($utf16)); $ascii .= sprintf('\u%04s', bin2hex($utf16));
@ -364,9 +387,9 @@ class Services_JSON
// characters U-00010000 - U-001FFFFF, mask 11110XXX // characters U-00010000 - U-001FFFFF, mask 11110XXX
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
$char = pack('C*', $ord_var_c, $char = pack('C*', $ord_var_c,
ord($var[$c + 1]), ord($var{$c + 1}),
ord($var[$c + 2]), ord($var{$c + 2}),
ord($var[$c + 3])); ord($var{$c + 3}));
$c += 3; $c += 3;
$utf16 = $this->utf82utf16($char); $utf16 = $this->utf82utf16($char);
$ascii .= sprintf('\u%04s', bin2hex($utf16)); $ascii .= sprintf('\u%04s', bin2hex($utf16));
@ -381,10 +404,10 @@ class Services_JSON
break; break;
} }
$char = pack('C*', $ord_var_c, $char = pack('C*', $ord_var_c,
ord($var[$c + 1]), ord($var{$c + 1}),
ord($var[$c + 2]), ord($var{$c + 2}),
ord($var[$c + 3]), ord($var{$c + 3}),
ord($var[$c + 4])); ord($var{$c + 4}));
$c += 4; $c += 4;
$utf16 = $this->utf82utf16($char); $utf16 = $this->utf82utf16($char);
$ascii .= sprintf('\u%04s', bin2hex($utf16)); $ascii .= sprintf('\u%04s', bin2hex($utf16));
@ -399,11 +422,11 @@ class Services_JSON
// characters U-04000000 - U-7FFFFFFF, mask 1111110X // characters U-04000000 - U-7FFFFFFF, mask 1111110X
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
$char = pack('C*', $ord_var_c, $char = pack('C*', $ord_var_c,
ord($var[$c + 1]), ord($var{$c + 1}),
ord($var[$c + 2]), ord($var{$c + 2}),
ord($var[$c + 3]), ord($var{$c + 3}),
ord($var[$c + 4]), ord($var{$c + 4}),
ord($var[$c + 5])); ord($var{$c + 5}));
$c += 5; $c += 5;
$utf16 = $this->utf82utf16($char); $utf16 = $this->utf82utf16($char);
$ascii .= sprintf('\u%04s', bin2hex($utf16)); $ascii .= sprintf('\u%04s', bin2hex($utf16));
@ -458,6 +481,25 @@ class Services_JSON
return '[' . join(',', $elements) . ']'; return '[' . join(',', $elements) . ']';
case 'object': case 'object':
// support toJSON methods.
if (($this->use & SERVICES_JSON_USE_TO_JSON) && method_exists($var, 'toJSON')) {
// this may end up allowing unlimited recursion
// so we check the return value to make sure it's not got the same method.
$recode = $var->toJSON();
if (method_exists($recode, 'toJSON')) {
return ($this->use & SERVICES_JSON_SUPPRESS_ERRORS)
? 'null'
: new Services_JSON_Error(class_name($var).
" toJSON returned an object with a toJSON method.");
}
return $this->_encode( $recode );
}
$vars = get_object_vars($var); $vars = get_object_vars($var);
$properties = array_map(array($this, 'name_value'), $properties = array_map(array($this, 'name_value'),
@ -569,15 +611,15 @@ class Services_JSON
} elseif (preg_match('/^("|\').*(\1)$/s', $str, $m) && $m[1] == $m[2]) { } elseif (preg_match('/^("|\').*(\1)$/s', $str, $m) && $m[1] == $m[2]) {
// STRINGS RETURNED IN UTF-8 FORMAT // STRINGS RETURNED IN UTF-8 FORMAT
$delim = substr($str, 0, 1); $delim = $this->substr8($str, 0, 1);
$chrs = substr($str, 1, -1); $chrs = $this->substr8($str, 1, -1);
$utf8 = ''; $utf8 = '';
$strlen_chrs = strlen($chrs); $strlen_chrs = $this->strlen8($chrs);
for ($c = 0; $c < $strlen_chrs; ++$c) { for ($c = 0; $c < $strlen_chrs; ++$c) {
$substr_chrs_c_2 = substr($chrs, $c, 2); $substr_chrs_c_2 = $this->substr8($chrs, $c, 2);
$ord_chrs_c = ord($chrs[$c]); $ord_chrs_c = ord($chrs{$c});
switch (true) { switch (true) {
case $substr_chrs_c_2 == '\b': case $substr_chrs_c_2 == '\b':
@ -607,54 +649,54 @@ class Services_JSON
case $substr_chrs_c_2 == '\\/': case $substr_chrs_c_2 == '\\/':
if (($delim == '"' && $substr_chrs_c_2 != '\\\'') || if (($delim == '"' && $substr_chrs_c_2 != '\\\'') ||
($delim == "'" && $substr_chrs_c_2 != '\\"')) { ($delim == "'" && $substr_chrs_c_2 != '\\"')) {
$utf8 .= $chrs[++$c]; $utf8 .= $chrs{++$c};
} }
break; break;
case preg_match('/\\\u[0-9A-F]{4}/i', substr($chrs, $c, 6)): case preg_match('/\\\u[0-9A-F]{4}/i', $this->substr8($chrs, $c, 6)):
// single, escaped unicode character // single, escaped unicode character
$utf16 = chr(hexdec(substr($chrs, ($c + 2), 2))) $utf16 = chr(hexdec($this->substr8($chrs, ($c + 2), 2)))
. chr(hexdec(substr($chrs, ($c + 4), 2))); . chr(hexdec($this->substr8($chrs, ($c + 4), 2)));
$utf8 .= $this->utf162utf8($utf16); $utf8 .= $this->utf162utf8($utf16);
$c += 5; $c += 5;
break; break;
case ($ord_chrs_c >= 0x20) && ($ord_chrs_c <= 0x7F): case ($ord_chrs_c >= 0x20) && ($ord_chrs_c <= 0x7F):
$utf8 .= $chrs[$c]; $utf8 .= $chrs{$c};
break; break;
case ($ord_chrs_c & 0xE0) == 0xC0: case ($ord_chrs_c & 0xE0) == 0xC0:
// characters U-00000080 - U-000007FF, mask 110XXXXX // characters U-00000080 - U-000007FF, mask 110XXXXX
//see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 //see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
$utf8 .= substr($chrs, $c, 2); $utf8 .= $this->substr8($chrs, $c, 2);
++$c; ++$c;
break; break;
case ($ord_chrs_c & 0xF0) == 0xE0: case ($ord_chrs_c & 0xF0) == 0xE0:
// characters U-00000800 - U-0000FFFF, mask 1110XXXX // characters U-00000800 - U-0000FFFF, mask 1110XXXX
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
$utf8 .= substr($chrs, $c, 3); $utf8 .= $this->substr8($chrs, $c, 3);
$c += 2; $c += 2;
break; break;
case ($ord_chrs_c & 0xF8) == 0xF0: case ($ord_chrs_c & 0xF8) == 0xF0:
// characters U-00010000 - U-001FFFFF, mask 11110XXX // characters U-00010000 - U-001FFFFF, mask 11110XXX
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
$utf8 .= substr($chrs, $c, 4); $utf8 .= $this->substr8($chrs, $c, 4);
$c += 3; $c += 3;
break; break;
case ($ord_chrs_c & 0xFC) == 0xF8: case ($ord_chrs_c & 0xFC) == 0xF8:
// characters U-00200000 - U-03FFFFFF, mask 111110XX // characters U-00200000 - U-03FFFFFF, mask 111110XX
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
$utf8 .= substr($chrs, $c, 5); $utf8 .= $this->substr8($chrs, $c, 5);
$c += 4; $c += 4;
break; break;
case ($ord_chrs_c & 0xFE) == 0xFC: case ($ord_chrs_c & 0xFE) == 0xFC:
// characters U-04000000 - U-7FFFFFFF, mask 1111110X // characters U-04000000 - U-7FFFFFFF, mask 1111110X
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
$utf8 .= substr($chrs, $c, 6); $utf8 .= $this->substr8($chrs, $c, 6);
$c += 5; $c += 5;
break; break;
@ -667,7 +709,7 @@ class Services_JSON
} elseif (preg_match('/^\[.*\]$/s', $str) || preg_match('/^\{.*\}$/s', $str)) { } elseif (preg_match('/^\[.*\]$/s', $str) || preg_match('/^\{.*\}$/s', $str)) {
// array, or object notation // array, or object notation
if ($str[0] == '[') { if ($str{0} == '[') {
$stk = array(SERVICES_JSON_IN_ARR); $stk = array(SERVICES_JSON_IN_ARR);
$arr = array(); $arr = array();
} else { } else {
@ -684,7 +726,7 @@ class Services_JSON
'where' => 0, 'where' => 0,
'delim' => false)); 'delim' => false));
$chrs = substr($str, 1, -1); $chrs = $this->substr8($str, 1, -1);
$chrs = $this->reduce_string($chrs); $chrs = $this->reduce_string($chrs);
if ($chrs == '') { if ($chrs == '') {
@ -699,19 +741,19 @@ class Services_JSON
//print("\nparsing {$chrs}\n"); //print("\nparsing {$chrs}\n");
$strlen_chrs = strlen($chrs); $strlen_chrs = $this->strlen8($chrs);
for ($c = 0; $c <= $strlen_chrs; ++$c) { for ($c = 0; $c <= $strlen_chrs; ++$c) {
$top = end($stk); $top = end($stk);
$substr_chrs_c_2 = substr($chrs, $c, 2); $substr_chrs_c_2 = $this->substr8($chrs, $c, 2);
if (($c == $strlen_chrs) || (($chrs[$c] == ',') && ($top['what'] == SERVICES_JSON_SLICE))) { if (($c == $strlen_chrs) || (($chrs{$c} == ',') && ($top['what'] == SERVICES_JSON_SLICE))) {
// found a comma that is not inside a string, array, etc., // found a comma that is not inside a string, array, etc.,
// OR we've reached the end of the character list // OR we've reached the end of the character list
$slice = substr($chrs, $top['where'], ($c - $top['where'])); $slice = $this->substr8($chrs, $top['where'], ($c - $top['where']));
array_push($stk, array('what' => SERVICES_JSON_SLICE, 'where' => ($c + 1), 'delim' => false)); array_push($stk, array('what' => SERVICES_JSON_SLICE, 'where' => ($c + 1), 'delim' => false));
//print("Found split at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n"); //print("Found split at {$c}: ".$this->substr8($chrs, $top['where'], (1 + $c - $top['where']))."\n");
if (reset($stk) == SERVICES_JSON_IN_ARR) { if (reset($stk) == SERVICES_JSON_IN_ARR) {
// we are in an array, so just push an element onto the stack // we are in an array, so just push an element onto the stack
@ -724,20 +766,19 @@ class Services_JSON
// for now // for now
$parts = array(); $parts = array();
if (preg_match('/^\s*(["\'].*[^\\\]["\'])\s*:\s*(\S.*),?$/Uis', $slice, $parts)) { if (preg_match('/^\s*(["\'].*[^\\\]["\'])\s*:/Uis', $slice, $parts)) {
// "name":value pair // "name":value pair
$key = $this->decode($parts[1]); $key = $this->decode($parts[1]);
$val = $this->decode($parts[2]); $val = $this->decode(trim(substr($slice, strlen($parts[0])), ", \t\n\r\0\x0B"));
if ($this->use & SERVICES_JSON_LOOSE_TYPE) { if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
$obj[$key] = $val; $obj[$key] = $val;
} else { } else {
$obj->$key = $val; $obj->$key = $val;
} }
} elseif (preg_match('/^\s*(\w+)\s*:\s*(\S.*),?$/Uis', $slice, $parts)) { } elseif (preg_match('/^\s*(\w+)\s*:/Uis', $slice, $parts)) {
// name:value pair, where name is unquoted // name:value pair, where name is unquoted
$key = $parts[1]; $key = $parts[1];
$val = $this->decode($parts[2]); $val = $this->decode(trim(substr($slice, strlen($parts[0])), ", \t\n\r\0\x0B"));
if ($this->use & SERVICES_JSON_LOOSE_TYPE) { if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
$obj[$key] = $val; $obj[$key] = $val;
@ -748,41 +789,41 @@ class Services_JSON
} }
} elseif ((($chrs[$c] == '"') || ($chrs[$c] == "'")) && ($top['what'] != SERVICES_JSON_IN_STR)) { } elseif ((($chrs{$c} == '"') || ($chrs{$c} == "'")) && ($top['what'] != SERVICES_JSON_IN_STR)) {
// found a quote, and we are not inside a string // found a quote, and we are not inside a string
array_push($stk, array('what' => SERVICES_JSON_IN_STR, 'where' => $c, 'delim' => $chrs[$c])); array_push($stk, array('what' => SERVICES_JSON_IN_STR, 'where' => $c, 'delim' => $chrs{$c}));
//print("Found start of string at {$c}\n"); //print("Found start of string at {$c}\n");
} elseif (($chrs[$c] == $top['delim']) && } elseif (($chrs{$c} == $top['delim']) &&
($top['what'] == SERVICES_JSON_IN_STR) && ($top['what'] == SERVICES_JSON_IN_STR) &&
((strlen(substr($chrs, 0, $c)) - strlen(rtrim(substr($chrs, 0, $c), '\\'))) % 2 != 1)) { (($this->strlen8($this->substr8($chrs, 0, $c)) - $this->strlen8(rtrim($this->substr8($chrs, 0, $c), '\\'))) % 2 != 1)) {
// found a quote, we're in a string, and it's not escaped // found a quote, we're in a string, and it's not escaped
// we know that it's not escaped becase there is _not_ an // we know that it's not escaped becase there is _not_ an
// odd number of backslashes at the end of the string so far // odd number of backslashes at the end of the string so far
array_pop($stk); array_pop($stk);
//print("Found end of string at {$c}: ".substr($chrs, $top['where'], (1 + 1 + $c - $top['where']))."\n"); //print("Found end of string at {$c}: ".$this->substr8($chrs, $top['where'], (1 + 1 + $c - $top['where']))."\n");
} elseif (($chrs[$c] == '[') && } elseif (($chrs{$c} == '[') &&
in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) { in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
// found a left-bracket, and we are in an array, object, or slice // found a left-bracket, and we are in an array, object, or slice
array_push($stk, array('what' => SERVICES_JSON_IN_ARR, 'where' => $c, 'delim' => false)); array_push($stk, array('what' => SERVICES_JSON_IN_ARR, 'where' => $c, 'delim' => false));
//print("Found start of array at {$c}\n"); //print("Found start of array at {$c}\n");
} elseif (($chrs[$c] == ']') && ($top['what'] == SERVICES_JSON_IN_ARR)) { } elseif (($chrs{$c} == ']') && ($top['what'] == SERVICES_JSON_IN_ARR)) {
// found a right-bracket, and we're in an array // found a right-bracket, and we're in an array
array_pop($stk); array_pop($stk);
//print("Found end of array at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n"); //print("Found end of array at {$c}: ".$this->substr8($chrs, $top['where'], (1 + $c - $top['where']))."\n");
} elseif (($chrs[$c] == '{') && } elseif (($chrs{$c} == '{') &&
in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) { in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
// found a left-brace, and we are in an array, object, or slice // found a left-brace, and we are in an array, object, or slice
array_push($stk, array('what' => SERVICES_JSON_IN_OBJ, 'where' => $c, 'delim' => false)); array_push($stk, array('what' => SERVICES_JSON_IN_OBJ, 'where' => $c, 'delim' => false));
//print("Found start of object at {$c}\n"); //print("Found start of object at {$c}\n");
} elseif (($chrs[$c] == '}') && ($top['what'] == SERVICES_JSON_IN_OBJ)) { } elseif (($chrs{$c} == '}') && ($top['what'] == SERVICES_JSON_IN_OBJ)) {
// found a right-brace, and we're in an object // found a right-brace, and we're in an object
array_pop($stk); array_pop($stk);
//print("Found end of object at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n"); //print("Found end of object at {$c}: ".$this->substr8($chrs, $top['where'], (1 + $c - $top['where']))."\n");
} elseif (($substr_chrs_c_2 == '/*') && } elseif (($substr_chrs_c_2 == '/*') &&
in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) { in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
@ -799,7 +840,7 @@ class Services_JSON
for ($i = $top['where']; $i <= $c; ++$i) for ($i = $top['where']; $i <= $c; ++$i)
$chrs = substr_replace($chrs, ' ', $i, 1); $chrs = substr_replace($chrs, ' ', $i, 1);
//print("Found end of comment at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n"); //print("Found end of comment at {$c}: ".$this->substr8($chrs, $top['where'], (1 + $c - $top['where']))."\n");
} }
@ -831,6 +872,38 @@ class Services_JSON
return false; return false;
} }
/**
* Calculates length of string in bytes
* @param string
* @return integer length
*/
function strlen8( $str )
{
if ( $this->_mb_strlen ) {
return mb_strlen( $str, "8bit" );
}
return strlen( $str );
}
/**
* Returns part of a string, interpreting $start and $length as number of bytes.
* @param string
* @param integer start
* @param integer length
* @return integer length
*/
function substr8( $string, $start, $length=false )
{
if ( $length === false ) {
$length = $this->strlen8( $string ) - $start;
}
if ( $this->_mb_substr ) {
return mb_substr( $string, $start, $length, "8bit" );
}
return substr( $string, $start, $length );
}
} }
if (class_exists('PEAR_Error')) { if (class_exists('PEAR_Error')) {
@ -859,4 +932,5 @@ if (class_exists('PEAR_Error')) {
} }
} }
endif; endif;