CoolFace
Apppublic

kenken999/php

sourceHugging Faceupdated 2y agoView on Hugging Face
0likes
json.php816 linesDownload Raw Back to classes
1<?php2/* 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-interchange8* format. It is easy for humans to read and write. It is easy for machines9* to parse and generate. It is based on a subset of the JavaScript10* Programming Language, Standard ECMA-262 3rd Edition - December 1999.11* This feature can also be found in  Python. JSON is a text format that is12* completely language independent but uses conventions that are familiar13* to programmers of the C-family of languages, including C, C++, C#, Java,14* JavaScript, Perl, TCL, and many others. These properties make JSON an15* ideal data-interchange language.16*17* This package provides a simple encoder and decoder for JSON notation. It18* is intended for use with client-side Javascript applications that make19* use of HTTPRequest to perform server communication functions - data can20* be encoded into JSON notation for use in a client-side javascript, or21* decoded from incoming Javascript requests. JSON format is native to22* Javascript, and can be directly eval()'ed with no further parsing23* overhead24*25* All strings should be in ASCII or UTF-8 format!26*27* LICENSE: Redistribution and use in source and binary forms, with or28* without modification, are permitted provided that the following29* conditions are met: Redistributions of source code must retain the30* above copyright notice, this list of conditions and the following31* disclaimer. Redistributions in binary form must reproduce the above32* copyright notice, this list of conditions and the following disclaimer33* in the documentation and/or other materials provided with the34* distribution.35*36* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED37* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF38* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN39* 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; LOSS42* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND43* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR44* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE45* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH46* DAMAGE.47*48* @category49* @package     Services_JSON50* @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 Migurski54* @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.php56* @link        http://pear.php.net/pepr/pepr-proposal-show.php?id=19857*/58 59/**60* Marker constant for Services_JSON::decode(), used to flag stack state61*/62define('SERVICES_JSON_SLICE',   1);63 64/**65* Marker constant for Services_JSON::decode(), used to flag stack state66*/67define('SERVICES_JSON_IN_STR',  2);68 69/**70* Marker constant for Services_JSON::decode(), used to flag stack state71*/72define('SERVICES_JSON_IN_ARR',  3);73 74/**75* Marker constant for Services_JSON::decode(), used to flag stack state76*/77define('SERVICES_JSON_IN_OBJ',  4);78 79/**80* Marker constant for Services_JSON::decode(), used to flag stack state81*/82define('SERVICES_JSON_IN_CMT', 5);83 84/**85* Behavior switch for Services_JSON::decode()86*/87define('SERVICES_JSON_LOOSE_TYPE', 16);88 89/**90* Behavior switch for Services_JSON::decode()91*/92define('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_JSON101* $json = new Services_JSON();102*103* // convert a complexe value to JSON notation, and send it to the browser104* $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 notation111* $input = file_get_contents('php://input', 1000000);112* $value = $json->decode($input);113* </code>114*/115class Services_JSON116{117	/**118	 * We suppose that data in project and in database are in the same encoding, 119	 * so we can encode data in different encoding, and we use this property to prevent convertion from 120	 * utf-8 when data not in utf-8 encoding 121	 *122	 * @var bool123	 */124	var $isUtf8 = true;125	126   /**127    * constructs a new JSON instance128    *129    * @param    int     $use    object behavior flags; combine with boolean-OR130    *131    *                           possible values:132    *                           - SERVICES_JSON_LOOSE_TYPE:  loose typing.133    *                                   "{...}" syntax creates associative arrays134    *                                   instead of objects in decode().135    *                           - SERVICES_JSON_SUPPRESS_ERRORS:  error suppression.136    *                                   Values which can't be encoded (e.g. resources)137    *                                   appear as NULL instead of throwing errors.138    *                                   By default, a deeply-nested resource will139    *                                   bubble up with an error, so all return values140    *                                   from encode() should be checked with isError()141    */142    function __construct($use = 0, $isUtf8 = true)143    {144        $this->use = $use;145        $this->isUtf8 = $isUtf8;146    }147 148   /**149    * convert a string from one UTF-16 char to one UTF-8 char150    *151    * Normally should be handled by mb_convert_encoding, but152    * provides a slower PHP-only method for installations153    * that lack the multibye string extension.154    *155    * @param    string  $utf16  UTF-16 character156    * @return   string  UTF-8 character157    * @access   private158    */159    function utf162utf8($utf16)160    {161        // oh please oh please oh please oh please oh please162        if(function_exists('mb_convert_encoding')) {163            return mb_convert_encoding($utf16, 'UTF-8', 'UTF-16');164        }165 166        $bytes = (ord($utf16[0]) << 8) | ord($utf16[1]);167 168        switch(true) {169            case ((0x7F & $bytes) == $bytes):170                // this case should never be reached, because we are in ASCII range171                // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8172                return chr(0x7F & $bytes);173 174            case (0x07FF & $bytes) == $bytes:175                // return a 2-byte UTF-8 character176                // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8177                return chr(0xC0 | (($bytes >> 6) & 0x1F))178                     . chr(0x80 | ($bytes & 0x3F));179 180            case (0xFFFF & $bytes) == $bytes:181                // return a 3-byte UTF-8 character182                // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8183                return chr(0xE0 | (($bytes >> 12) & 0x0F))184                     . chr(0x80 | (($bytes >> 6) & 0x3F))185                     . chr(0x80 | ($bytes & 0x3F));186        }187 188        // ignoring UTF-32 for now, sorry189        return '';190    }191 192   /**193    * convert a string from one UTF-8 char to one UTF-16 char194    *195    * Normally should be handled by mb_convert_encoding, but196    * provides a slower PHP-only method for installations197    * that lack the multibye string extension.198    *199    * @param    string  $utf8   UTF-8 character200    * @return   string  UTF-16 character201    * @access   private202    */203    function utf82utf16($utf8)204    {205        // oh please oh please oh please oh please oh please206        if(function_exists('mb_convert_encoding')) {207            return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8');208        }209 210        switch(strlen($utf8)) {211            case 1:212                // this case should never be reached, because we are in ASCII range213                // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8214                return $utf8;215 216            case 2:217                // return a UTF-16 character from a 2-byte UTF-8 char218                // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8219                return chr(0x07 & (ord($utf8[0]) >> 2))220                     . chr((0xC0 & (ord($utf8[0]) << 6))221                         | (0x3F & ord($utf8[1])));222 223            case 3:224                // return a UTF-16 character from a 3-byte UTF-8 char225                // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8226                return chr((0xF0 & (ord($utf8[0]) << 4))227                         | (0x0F & (ord($utf8[1]) >> 2)))228                     . chr((0xC0 & (ord($utf8[1]) << 6))229                         | (0x7F & ord($utf8[2])));230        }231 232        // ignoring UTF-32 for now, sorry233        return '';234    }235 236   /**237    * encodes an arbitrary variable into JSON format238    *239    * @param    mixed   $var    any number, boolean, string, array, or object to be encoded.240    *                           see argument 1 to Services_JSON() above for array-parsing behavior.241    *                           if var is a strng, note that encode() always expects it242    *                           to be in ASCII or UTF-8 format!243    *244    * @return   mixed   JSON string representation of input var or an error if a problem occurs245    * @access   public246    */247    function encode($var)248    {249        switch (gettype($var)) {250            case 'boolean':251                return $var ? 'true' : 'false';252 253            case 'NULL':254                return 'null';255 256            case 'integer':257                return (int) $var;258 259            case 'double':260            case 'float':261                return (float) $var;262 263            case 'string':264                // STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT265                $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 necessary271                */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, slosh297                            $ascii .= '\\'.$var[$c];298                            break;299 300                        case !$this->isUtf8 ||  (($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 110XXXXX307                            // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8308                            $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 1110XXXX316                            // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8317                            $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 11110XXX327                            // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8328                            $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 111110XX339                            // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8340                            $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 1111110X352                            // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8353                            $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 integer371                * we must treat the the whole array as an object. We372                * also try to catch a sparsely populated associative373                * array with numeric keys here because some JS engines374                * will create an array with empty indexes up to375                * max_index which can cause memory issues and because376                * the keys, which may be relevant, will be remapped377                * otherwise.378                *379                * As per the ECMA and JSON specification an object may380                * have any string as a property. Unfortunately due to381                * a hole in the ECMA specification if the key is a382                * ECMA reserved word or starts with a digit the383                * parameter is only accessible using ECMAScript's384                * bracket notation.385                */386 387                // treat as a JSON object388                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(Services_JSON::isError($property)) {395                            return $property;396                        }397                    }398 399                    return '{' . join(',', $properties) . '}';400                }401 402                // treat it like a regular array403                $elements = array_map(array($this, 'encode'), $var);404 405                foreach($elements as $element) {406                    if(Services_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(Services_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 Services_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 pairs437    *438    * @param    string  $name   name of key to use439    * @param    mixed   $value  reference to an array element to be encoded440    *441    * @return   string  JSON-formatted name-value pair, like '"name":value'442    * @access   private443    */444    function name_value($name, $value)445    {446        $encoded_value = $this->encode($value);447 448        if(Services_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 whitespace457    *458    * @param    $str    string      string value to strip of comments and whitespace459    *460    * @return   string  string value stripped of comments and whitespace461    * @access   private462    */463    function reduce_string($str)464    {465        $str = preg_replace(array(466 467                // eliminate single line comments in '// ...' form468                '#^\s*//(.+)$#m',469 470                // eliminate multi-line comments in '/* ... */' form, at start of string471                '#^\s*/\*(.+)\*/#Us',472 473                // eliminate multi-line comments in '/* ... */' form, at end of string474                '#/\*(.+)\*/\s*$#Us'475 476            ), '', $str);477 478        // eliminate extraneous space479        return trim($str);480    }481 482   /**483    * decodes a JSON string into appropriate variable484    *485    * @param    string  $str    JSON-formatted string486    *487    * @return   mixed   number, boolean, string, array, or object488    *                   corresponding to given JSON input string.489    *                   See argument 1 to Services_JSON() above for object-output behavior.490    *                   Note that decode() always returns strings491    *                   in ASCII or UTF-8 format!492    * @access   public493    */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                $m = array();510 511                if (is_numeric($str)) {512                    // Lookie-loo, it's a number513 514                    // This would work on its own, but I'm trying to be515                    // good about returning integers where appropriate:516                    // return (float)$str;517 518                    // Return float or int, as appropriate519                    return ((float)$str == (integer)$str)520                        ? (integer)$str521                        : (float)$str;522 523                } elseif (preg_match('/^("|\').*(\1)$/s', $str, $m) && $m[1] == $m[2]) {524                    // STRINGS RETURNED IN UTF-8 FORMAT525                    $delim = substr($str, 0, 1);526                    $chrs = substr($str, 1, -1);527                    $utf8 = '';528                    $strlen_chrs = strlen($chrs);529 530                    for ($c = 0; $c < $strlen_chrs; ++$c) {531 532                        $substr_chrs_c_2 = substr($chrs, $c, 2);533                        $ord_chrs_c = ord($chrs[$c]);534 535                        switch (true) {536                            case $substr_chrs_c_2 == '\b':537                                $utf8 .= chr(0x08);538                                ++$c;539                                break;540                            case $substr_chrs_c_2 == '\t':541                                $utf8 .= chr(0x09);542                                ++$c;543                                break;544                            case $substr_chrs_c_2 == '\n':545                                $utf8 .= chr(0x0A);546                                ++$c;547                                break;548                            case $substr_chrs_c_2 == '\f':549                                $utf8 .= chr(0x0C);550                                ++$c;551                                break;552                            case $substr_chrs_c_2 == '\r':553                                $utf8 .= chr(0x0D);554                                ++$c;555                                break;556 557                            case $substr_chrs_c_2 == '\\"':558                            case $substr_chrs_c_2 == '\\\'':559                            case $substr_chrs_c_2 == '\\\\':560                            case $substr_chrs_c_2 == '\\/':561                                if (($delim == '"' && $substr_chrs_c_2 != '\\\'') ||562                                   ($delim == "'" && $substr_chrs_c_2 != '\\"')) {563                                    $utf8 .= $chrs[++$c];564                                }565                                break;566 567                            case $this->isUtf8 && preg_match('/\\\u[0-9A-F]{4}/i', substr($chrs, $c, 6)):568                                // single, escaped unicode character569                                $utf16 = chr(hexdec(substr($chrs, ($c + 2), 2)))570                                       . chr(hexdec(substr($chrs, ($c + 4), 2)));571                                $utf8 .= $this->utf162utf8($utf16);572                                $c += 5;573                                break;574 575                            case !$this->isUtf8 || ($ord_chrs_c >= 0x20) && ($ord_chrs_c <= 0x7F):576                                $utf8 .= $chrs[$c];577                                break;578 579                            case ($ord_chrs_c & 0xE0) == 0xC0:580                                // characters U-00000080 - U-000007FF, mask 110XXXXX581                                //see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8582                                $utf8 .= substr($chrs, $c, 2);583                                ++$c;584                                break;585 586                            case ($ord_chrs_c & 0xF0) == 0xE0:587                                // characters U-00000800 - U-0000FFFF, mask 1110XXXX588                                // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8589                                $utf8 .= substr($chrs, $c, 3);590                                $c += 2;591                                break;592 593                            case ($ord_chrs_c & 0xF8) == 0xF0:594                                // characters U-00010000 - U-001FFFFF, mask 11110XXX595                                // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8596                                $utf8 .= substr($chrs, $c, 4);597                                $c += 3;598                                break;599 600                            case ($ord_chrs_c & 0xFC) == 0xF8:601                                // characters U-00200000 - U-03FFFFFF, mask 111110XX602                                // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8603                                $utf8 .= substr($chrs, $c, 5);604                                $c += 4;605                                break;606 607                            case ($ord_chrs_c & 0xFE) == 0xFC:608                                // characters U-04000000 - U-7FFFFFFF, mask 1111110X609                                // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8610                                $utf8 .= substr($chrs, $c, 6);611                                $c += 5;612                                break;613 614                        }615 616                    }617 618                    return $utf8;619 620                } elseif (preg_match('/^\[.*\]$/s', $str) || preg_match('/^\{.*\}$/s', $str)) {621                    // array, or object notation622 623                    if ($str[0] == '[') {624                        $stk = array(SERVICES_JSON_IN_ARR);625                        $arr = array();626                    } else {627                        if ($this->use & SERVICES_JSON_LOOSE_TYPE) {628                            $stk = array(SERVICES_JSON_IN_OBJ);629                            $obj = array();630                        } else {631                            $stk = array(SERVICES_JSON_IN_OBJ);632                            $obj = new stdClass();633                        }634                    }635 636                    array_push($stk, array('what'  => SERVICES_JSON_SLICE,637                                           'where' => 0,638                                           'delim' => false));639 640                    $chrs = substr($str, 1, -1);641                    $chrs = $this->reduce_string($chrs);642 643                    if ($chrs == '') {644                        if (reset($stk) == SERVICES_JSON_IN_ARR) {645                            return $arr;646 647                        } else {648                            return $obj;649 650                        }651                    }652 653                    //print("\nparsing [$chrs]\n");654 655                    $strlen_chrs = strlen($chrs);656 657                    for ($c = 0; $c <= $strlen_chrs; ++$c) {658 659                        $top = end($stk);660                        $substr_chrs_c_2 = substr($chrs, $c, 2);661 662                        if (($c == $strlen_chrs) || (($chrs[$c] == ',') && ($top['what'] == SERVICES_JSON_SLICE))) {663                            // found a comma that is not inside a string, array, etc.,664                            // OR we've reached the end of the character list665                            $slice = substr($chrs, $top['where'], ($c - $top['where']));666                            array_push($stk, array('what' => SERVICES_JSON_SLICE, 'where' => ($c + 1), 'delim' => false));667                            //print("Found split at [$c]: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");668 669                            if (reset($stk) == SERVICES_JSON_IN_ARR) {670                                // we are in an array, so just push an element onto the stack671                                array_push($arr, $this->decode($slice));672 673                            } elseif (reset($stk) == SERVICES_JSON_IN_OBJ) {674                                // we are in an object, so figure675                                // out the property name and set an676                                // element in an associative array,677                                // for now678                                $parts = array();679                                680                                if (preg_match('/^\s*(["\'].*[^\\\]["\'])\s*:\s*(\S.*),?$/Uis', $slice, $parts)) {681                                    // "name":value pair682                                    $key = $this->decode($parts[1]);683                                    $val = $this->decode($parts[2]);684 685                                    if ($this->use & SERVICES_JSON_LOOSE_TYPE) {686                                        $obj[$key] = $val;687                                    } else {688                                        $obj->$key = $val;689                                    }690                                } elseif (preg_match('/^\s*(\w+)\s*:\s*(\S.*),?$/Uis', $slice, $parts)) {691                                    // name:value pair, where name is unquoted692                                    $key = $parts[1];693                                    $val = $this->decode($parts[2]);694 695                                    if ($this->use & SERVICES_JSON_LOOSE_TYPE) {696                                        $obj[$key] = $val;697                                    } else {698                                        $obj->$key = $val;699                                    }700                                }701 702                            }703 704                        } elseif ((($chrs[$c] == '"') || ($chrs[$c] == "'")) && ($top['what'] != SERVICES_JSON_IN_STR)) {705                            // found a quote, and we are not inside a string706                            array_push($stk, array('what' => SERVICES_JSON_IN_STR, 'where' => $c, 'delim' => $chrs[$c]));707                            //print("Found start of string at [$c]\n");708 709                        } elseif (($chrs[$c] == $top['delim']) &&710                                 ($top['what'] == SERVICES_JSON_IN_STR) &&711                                 ((strlen(substr($chrs, 0, $c)) - strlen(rtrim(substr($chrs, 0, $c), '\\'))) % 2 != 1)) {712                            // found a quote, we're in a string, and it's not escaped713                            // we know that it's not escaped becase there is _not_ an714                            // odd number of backslashes at the end of the string so far715                            array_pop($stk);716                            //print("Found end of string at [$c]: ".substr($chrs, $top['where'], (1 + 1 + $c - $top['where']))."\n");717 718                        } elseif (($chrs[$c] == '[') &&719                                 in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {720                            // found a left-bracket, and we are in an array, object, or slice721                            array_push($stk, array('what' => SERVICES_JSON_IN_ARR, 'where' => $c, 'delim' => false));722                            //print("Found start of array at [$c]\n");723 724                        } elseif (($chrs[$c] == ']') && ($top['what'] == SERVICES_JSON_IN_ARR)) {725                            // found a right-bracket, and we're in an array726                            array_pop($stk);727                            //print("Found end of array at [$c]: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");728 729                        } elseif (($chrs[$c] == '{') &&730                                 in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {731                            // found a left-brace, and we are in an array, object, or slice732                            array_push($stk, array('what' => SERVICES_JSON_IN_OBJ, 'where' => $c, 'delim' => false));733                            //print("Found start of object at [$c]\n");734 735                        } elseif (($chrs[$c] == '}') && ($top['what'] == SERVICES_JSON_IN_OBJ)) {736                            // found a right-brace, and we're in an object737                            array_pop($stk);738                            //print("Found end of object at [$c]: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");739 740                        } elseif (($substr_chrs_c_2 == '/*') &&741                                 in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {742                            // found a comment start, and we are in an array, object, or slice743                            array_push($stk, array('what' => SERVICES_JSON_IN_CMT, 'where' => $c, 'delim' => false));744                            $c++;745                            //print("Found start of comment at [$c]\n");746 747                        } elseif (($substr_chrs_c_2 == '*/') && ($top['what'] == SERVICES_JSON_IN_CMT)) {748                            // found a comment end, and we're in one now749                            array_pop($stk);750                            $c++;751 752                            for ($i = $top['where']; $i <= $c; ++$i)753                                $chrs = substr_replace($chrs, ' ', $i, 1);754 755                            //print("Found end of comment at [$c]: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");756 757                        }758 759                    }760 761                    if (reset($stk) == SERVICES_JSON_IN_ARR) {762                        return $arr;763 764                    } elseif (reset($stk) == SERVICES_JSON_IN_OBJ) {765                        return $obj;766 767                    }768 769                }770        }771    }772 773    /**774     * @todo Ultimately, this should just call PEAR::isError()775     */776    function isError($data, $code = null)777    {778        if (class_exists('pear')) {779            return PEAR::isError($data, $code);780        } elseif (is_object($data) && (get_class($data) == 'services_json_error' ||781                                 is_subclass_of($data, 'services_json_error'))) {782            return true;783        }784 785        return false;786    }787}788 789if (class_exists('PEAR_Error')) {790 791    class Services_JSON_Error extends PEAR_Error792    {793        function __construct($message = 'unknown error', $code = null,794                                     $mode = null, $options = null, $userinfo = null)795        {796            parent::__construct($message, $code, $mode, $options, $userinfo);797        }798    }799 800} else {801 802    /**803     * @todo Ultimately, this class shall be descended from PEAR_Error804     */805    class Services_JSON_Error806    {807        function __construct($message = 'unknown error', $code = null,808                                     $mode = null, $options = null, $userinfo = null)809        {810 811        }812    }813 814}815    816?>