angelovcom.net

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

class-json.php (43441B)


      1 <?php
      2 _deprecated_file( basename( __FILE__ ), '5.3.0', '', 'The PHP native JSON extension is now a requirement.' );
      3 
      4 if ( ! class_exists( 'Services_JSON' ) ) :
      5 /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
      6 /**
      7  * Converts to and from JSON format.
      8  *
      9  * JSON (JavaScript Object Notation) is a lightweight data-interchange
     10  * format. It is easy for humans to read and write. It is easy for machines
     11  * to parse and generate. It is based on a subset of the JavaScript
     12  * Programming Language, Standard ECMA-262 3rd Edition - December 1999.
     13  * This feature can also be found in  Python. JSON is a text format that is
     14  * completely language independent but uses conventions that are familiar
     15  * to programmers of the C-family of languages, including C, C++, C#, Java,
     16  * JavaScript, Perl, TCL, and many others. These properties make JSON an
     17  * ideal data-interchange language.
     18  *
     19  * This package provides a simple encoder and decoder for JSON notation. It
     20  * is intended for use with client-side JavaScript applications that make
     21  * use of HTTPRequest to perform server communication functions - data can
     22  * be encoded into JSON notation for use in a client-side javaScript, or
     23  * decoded from incoming JavaScript requests. JSON format is native to
     24  * JavaScript, and can be directly eval()'ed with no further parsing
     25  * overhead
     26  *
     27  * All strings should be in ASCII or UTF-8 format!
     28  *
     29  * LICENSE: Redistribution and use in source and binary forms, with or
     30  * without modification, are permitted provided that the following
     31  * conditions are met: Redistributions of source code must retain the
     32  * above copyright notice, this list of conditions and the following
     33  * disclaimer. Redistributions in binary form must reproduce the above
     34  * copyright notice, this list of conditions and the following disclaimer
     35  * in the documentation and/or other materials provided with the
     36  * distribution.
     37  *
     38  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
     39  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
     40  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
     41  * NO EVENT SHALL CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
     42  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
     43  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
     44  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
     45  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
     46  * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
     47  * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
     48  * DAMAGE.
     49  *
     50  * @category
     51  * @package     Services_JSON
     52  * @author      Michal Migurski <mike-json@teczno.com>
     53  * @author      Matt Knapp <mdknapp[at]gmail[dot]com>
     54  * @author      Brett Stimmerman <brettstimmerman[at]gmail[dot]com>
     55  * @copyright   2005 Michal Migurski
     56  * @version     CVS: $Id: JSON.php 305040 2010-11-02 23:19:03Z alan_k $
     57  * @license     http://www.opensource.org/licenses/bsd-license.php
     58  * @link        http://pear.php.net/pepr/pepr-proposal-show.php?id=198
     59  */
     60 
     61 /**
     62  * Marker constant for Services_JSON::decode(), used to flag stack state
     63  */
     64 define('SERVICES_JSON_SLICE',   1);
     65 
     66 /**
     67  * Marker constant for Services_JSON::decode(), used to flag stack state
     68  */
     69 define('SERVICES_JSON_IN_STR',  2);
     70 
     71 /**
     72  * Marker constant for Services_JSON::decode(), used to flag stack state
     73  */
     74 define('SERVICES_JSON_IN_ARR',  3);
     75 
     76 /**
     77  * Marker constant for Services_JSON::decode(), used to flag stack state
     78  */
     79 define('SERVICES_JSON_IN_OBJ',  4);
     80 
     81 /**
     82  * Marker constant for Services_JSON::decode(), used to flag stack state
     83  */
     84 define('SERVICES_JSON_IN_CMT', 5);
     85 
     86 /**
     87  * Behavior switch for Services_JSON::decode()
     88  */
     89 define('SERVICES_JSON_LOOSE_TYPE', 16);
     90 
     91 /**
     92  * Behavior switch for Services_JSON::decode()
     93  */
     94 define('SERVICES_JSON_SUPPRESS_ERRORS', 32);
     95 
     96 /**
     97  * Behavior switch for Services_JSON::decode()
     98  */
     99 define('SERVICES_JSON_USE_TO_JSON', 64);
    100 
    101 /**
    102  * Converts to and from JSON format.
    103  *
    104  * Brief example of use:
    105  *
    106  * <code>
    107  * // create a new instance of Services_JSON
    108  * $json = new Services_JSON();
    109  *
    110  * // convert a complex value to JSON notation, and send it to the browser
    111  * $value = array('foo', 'bar', array(1, 2, 'baz'), array(3, array(4)));
    112  * $output = $json->encode($value);
    113  *
    114  * print($output);
    115  * // prints: ["foo","bar",[1,2,"baz"],[3,[4]]]
    116  *
    117  * // accept incoming POST data, assumed to be in JSON notation
    118  * $input = file_get_contents('php://input', 1000000);
    119  * $value = $json->decode($input);
    120  * </code>
    121  */
    122 class Services_JSON
    123 {
    124    /**
    125     * constructs a new JSON instance
    126     *
    127     * @deprecated 5.3.0 Use the PHP native JSON extension instead.
    128     *
    129     * @param    int     $use    object behavior flags; combine with boolean-OR
    130     *
    131     *                           possible values:
    132     *                           - SERVICES_JSON_LOOSE_TYPE:  loose typing.
    133     *                                   "{...}" syntax creates associative arrays
    134     *                                   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 will
    139     *                                   bubble up with an error, so all return values
    140     *                                   from encode() should be checked with isError()
    141     *                           - SERVICES_JSON_USE_TO_JSON:  call toJSON when serializing objects
    142     *                                   It serializes the return value from the toJSON call rather 
    143     *                                   than the object itself, toJSON can return associative arrays, 
    144     *                                   strings or numbers, if you return an object, make sure it does
    145     *                                   not have a toJSON method, otherwise an error will occur.
    146     */
    147     function __construct( $use = 0 )
    148     {
    149         _deprecated_function( __METHOD__, '5.3.0', 'The PHP native JSON extension' );
    150 
    151         $this->use = $use;
    152         $this->_mb_strlen            = function_exists('mb_strlen');
    153         $this->_mb_convert_encoding  = function_exists('mb_convert_encoding');
    154         $this->_mb_substr            = function_exists('mb_substr');
    155     }
    156 
    157 	/**
    158 	 * PHP4 constructor.
    159      *
    160      * @deprecated 5.3.0 Use __construct() instead.
    161      *
    162      * @see Services_JSON::__construct()
    163 	 */
    164 	public function Services_JSON( $use = 0 ) {
    165 		_deprecated_constructor( 'Services_JSON', '5.3.0', get_class( $this ) );
    166 		self::__construct( $use );
    167 	}
    168     // private - cache the mbstring lookup results..
    169     var $_mb_strlen = false;
    170     var $_mb_substr = false;
    171     var $_mb_convert_encoding = false;
    172     
    173    /**
    174     * convert a string from one UTF-16 char to one UTF-8 char
    175     *
    176     * Normally should be handled by mb_convert_encoding, but
    177     * provides a slower PHP-only method for installations
    178     * that lack the multibye string extension.
    179     *
    180     * @deprecated 5.3.0 Use the PHP native JSON extension instead.
    181     *
    182     * @param    string  $utf16  UTF-16 character
    183     * @return   string  UTF-8 character
    184     * @access   private
    185     */
    186     function utf162utf8($utf16)
    187     {
    188         _deprecated_function( __METHOD__, '5.3.0', 'The PHP native JSON extension' );
    189 
    190         // oh please oh please oh please oh please oh please
    191         if($this->_mb_convert_encoding) {
    192             return mb_convert_encoding($utf16, 'UTF-8', 'UTF-16');
    193         }
    194 
    195         $bytes = (ord($utf16[0]) << 8) | ord($utf16[1]);
    196 
    197         switch(true) {
    198             case ((0x7F & $bytes) == $bytes):
    199                 // this case should never be reached, because we are in ASCII range
    200                 // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
    201                 return chr(0x7F & $bytes);
    202 
    203             case (0x07FF & $bytes) == $bytes:
    204                 // return a 2-byte UTF-8 character
    205                 // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
    206                 return chr(0xC0 | (($bytes >> 6) & 0x1F))
    207                      . chr(0x80 | ($bytes & 0x3F));
    208 
    209             case (0xFFFF & $bytes) == $bytes:
    210                 // return a 3-byte UTF-8 character
    211                 // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
    212                 return chr(0xE0 | (($bytes >> 12) & 0x0F))
    213                      . chr(0x80 | (($bytes >> 6) & 0x3F))
    214                      . chr(0x80 | ($bytes & 0x3F));
    215         }
    216 
    217         // ignoring UTF-32 for now, sorry
    218         return '';
    219     }
    220 
    221    /**
    222     * convert a string from one UTF-8 char to one UTF-16 char
    223     *
    224     * Normally should be handled by mb_convert_encoding, but
    225     * provides a slower PHP-only method for installations
    226     * that lack the multibyte string extension.
    227     *
    228     * @deprecated 5.3.0 Use the PHP native JSON extension instead.
    229     *
    230     * @param    string  $utf8   UTF-8 character
    231     * @return   string  UTF-16 character
    232     * @access   private
    233     */
    234     function utf82utf16($utf8)
    235     {
    236         _deprecated_function( __METHOD__, '5.3.0', 'The PHP native JSON extension' );
    237 
    238         // oh please oh please oh please oh please oh please
    239         if($this->_mb_convert_encoding) {
    240             return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8');
    241         }
    242 
    243         switch($this->strlen8($utf8)) {
    244             case 1:
    245                 // this case should never be reached, because we are in ASCII range
    246                 // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
    247                 return $utf8;
    248 
    249             case 2:
    250                 // return a UTF-16 character from a 2-byte UTF-8 char
    251                 // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
    252                 return chr(0x07 & (ord($utf8[0]) >> 2))
    253                      . chr((0xC0 & (ord($utf8[0]) << 6))
    254                          | (0x3F & ord($utf8[1])));
    255 
    256             case 3:
    257                 // return a UTF-16 character from a 3-byte UTF-8 char
    258                 // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
    259                 return chr((0xF0 & (ord($utf8[0]) << 4))
    260                          | (0x0F & (ord($utf8[1]) >> 2)))
    261                      . chr((0xC0 & (ord($utf8[1]) << 6))
    262                          | (0x7F & ord($utf8[2])));
    263         }
    264 
    265         // ignoring UTF-32 for now, sorry
    266         return '';
    267     }
    268 
    269    /**
    270     * encodes an arbitrary variable into JSON format (and sends JSON Header)
    271     *
    272     * @deprecated 5.3.0 Use the PHP native JSON extension instead.
    273     *
    274     * @param    mixed   $var    any number, boolean, string, array, or object to be encoded.
    275     *                           see argument 1 to Services_JSON() above for array-parsing behavior.
    276     *                           if var is a string, note that encode() always expects it
    277     *                           to be in ASCII or UTF-8 format!
    278     *
    279     * @return   mixed   JSON string representation of input var or an error if a problem occurs
    280     * @access   public
    281     */
    282     function encode($var)
    283     {
    284         _deprecated_function( __METHOD__, '5.3.0', 'The PHP native JSON extension' );
    285 
    286         header('Content-type: application/json');
    287         return $this->encodeUnsafe($var);
    288     }
    289     /**
    290     * encodes an arbitrary variable into JSON format without JSON Header - warning - may allow XSS!!!!)
    291     *
    292     * @deprecated 5.3.0 Use the PHP native JSON extension instead.
    293     *
    294     * @param    mixed   $var    any number, boolean, string, array, or object to be encoded.
    295     *                           see argument 1 to Services_JSON() above for array-parsing behavior.
    296     *                           if var is a string, note that encode() always expects it
    297     *                           to be in ASCII or UTF-8 format!
    298     *
    299     * @return   mixed   JSON string representation of input var or an error if a problem occurs
    300     * @access   public
    301     */
    302     function encodeUnsafe($var)
    303     {
    304         _deprecated_function( __METHOD__, '5.3.0', 'The PHP native JSON extension' );
    305 
    306         // see bug #16908 - regarding numeric locale printing
    307         $lc = setlocale(LC_NUMERIC, 0);
    308         setlocale(LC_NUMERIC, 'C');
    309         $ret = $this->_encode($var);
    310         setlocale(LC_NUMERIC, $lc);
    311         return $ret;
    312         
    313     }
    314     /**
    315     * PRIVATE CODE that does the work of encodes an arbitrary variable into JSON format 
    316     *
    317     * @deprecated 5.3.0 Use the PHP native JSON extension instead.
    318     *
    319     * @param    mixed   $var    any number, boolean, string, array, or object to be encoded.
    320     *                           see argument 1 to Services_JSON() above for array-parsing behavior.
    321     *                           if var is a string, note that encode() always expects it
    322     *                           to be in ASCII or UTF-8 format!
    323     *
    324     * @return   mixed   JSON string representation of input var or an error if a problem occurs
    325     * @access   public
    326     */
    327     function _encode($var) 
    328     {
    329         _deprecated_function( __METHOD__, '5.3.0', 'The PHP native JSON extension' );
    330 
    331         switch (gettype($var)) {
    332             case 'boolean':
    333                 return $var ? 'true' : 'false';
    334 
    335             case 'NULL':
    336                 return 'null';
    337 
    338             case 'integer':
    339                 return (int) $var;
    340 
    341             case 'double':
    342             case 'float':
    343                 return  (float) $var;
    344 
    345             case 'string':
    346                 // STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT
    347                 $ascii = '';
    348                 $strlen_var = $this->strlen8($var);
    349 
    350                /*
    351                 * Iterate over every character in the string,
    352                 * escaping with a slash or encoding to UTF-8 where necessary
    353                 */
    354                 for ($c = 0; $c < $strlen_var; ++$c) {
    355 
    356                     $ord_var_c = ord($var[$c]);
    357 
    358                     switch (true) {
    359                         case $ord_var_c == 0x08:
    360                             $ascii .= '\b';
    361                             break;
    362                         case $ord_var_c == 0x09:
    363                             $ascii .= '\t';
    364                             break;
    365                         case $ord_var_c == 0x0A:
    366                             $ascii .= '\n';
    367                             break;
    368                         case $ord_var_c == 0x0C:
    369                             $ascii .= '\f';
    370                             break;
    371                         case $ord_var_c == 0x0D:
    372                             $ascii .= '\r';
    373                             break;
    374 
    375                         case $ord_var_c == 0x22:
    376                         case $ord_var_c == 0x2F:
    377                         case $ord_var_c == 0x5C:
    378                             // double quote, slash, slosh
    379                             $ascii .= '\\'.$var[$c];
    380                             break;
    381 
    382                         case (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)):
    383                             // characters U-00000000 - U-0000007F (same as ASCII)
    384                             $ascii .= $var[$c];
    385                             break;
    386 
    387                         case (($ord_var_c & 0xE0) == 0xC0):
    388                             // characters U-00000080 - U-000007FF, mask 110XXXXX
    389                             // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
    390                             if ($c+1 >= $strlen_var) {
    391                                 $c += 1;
    392                                 $ascii .= '?';
    393                                 break;
    394                             }
    395                             
    396                             $char = pack('C*', $ord_var_c, ord($var[$c + 1]));
    397                             $c += 1;
    398                             $utf16 = $this->utf82utf16($char);
    399                             $ascii .= sprintf('\u%04s', bin2hex($utf16));
    400                             break;
    401 
    402                         case (($ord_var_c & 0xF0) == 0xE0):
    403                             if ($c+2 >= $strlen_var) {
    404                                 $c += 2;
    405                                 $ascii .= '?';
    406                                 break;
    407                             }
    408                             // characters U-00000800 - U-0000FFFF, mask 1110XXXX
    409                             // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
    410                             $char = pack('C*', $ord_var_c,
    411                                          @ord($var[$c + 1]),
    412                                          @ord($var[$c + 2]));
    413                             $c += 2;
    414                             $utf16 = $this->utf82utf16($char);
    415                             $ascii .= sprintf('\u%04s', bin2hex($utf16));
    416                             break;
    417 
    418                         case (($ord_var_c & 0xF8) == 0xF0):
    419                             if ($c+3 >= $strlen_var) {
    420                                 $c += 3;
    421                                 $ascii .= '?';
    422                                 break;
    423                             }
    424                             // characters U-00010000 - U-001FFFFF, mask 11110XXX
    425                             // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
    426                             $char = pack('C*', $ord_var_c,
    427                                          ord($var[$c + 1]),
    428                                          ord($var[$c + 2]),
    429                                          ord($var[$c + 3]));
    430                             $c += 3;
    431                             $utf16 = $this->utf82utf16($char);
    432                             $ascii .= sprintf('\u%04s', bin2hex($utf16));
    433                             break;
    434 
    435                         case (($ord_var_c & 0xFC) == 0xF8):
    436                             // characters U-00200000 - U-03FFFFFF, mask 111110XX
    437                             // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
    438                             if ($c+4 >= $strlen_var) {
    439                                 $c += 4;
    440                                 $ascii .= '?';
    441                                 break;
    442                             }
    443                             $char = pack('C*', $ord_var_c,
    444                                          ord($var[$c + 1]),
    445                                          ord($var[$c + 2]),
    446                                          ord($var[$c + 3]),
    447                                          ord($var[$c + 4]));
    448                             $c += 4;
    449                             $utf16 = $this->utf82utf16($char);
    450                             $ascii .= sprintf('\u%04s', bin2hex($utf16));
    451                             break;
    452 
    453                         case (($ord_var_c & 0xFE) == 0xFC):
    454                         if ($c+5 >= $strlen_var) {
    455                                 $c += 5;
    456                                 $ascii .= '?';
    457                                 break;
    458                             }
    459                             // characters U-04000000 - U-7FFFFFFF, mask 1111110X
    460                             // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
    461                             $char = pack('C*', $ord_var_c,
    462                                          ord($var[$c + 1]),
    463                                          ord($var[$c + 2]),
    464                                          ord($var[$c + 3]),
    465                                          ord($var[$c + 4]),
    466                                          ord($var[$c + 5]));
    467                             $c += 5;
    468                             $utf16 = $this->utf82utf16($char);
    469                             $ascii .= sprintf('\u%04s', bin2hex($utf16));
    470                             break;
    471                     }
    472                 }
    473                 return  '"'.$ascii.'"';
    474 
    475             case 'array':
    476                /*
    477                 * As per JSON spec if any array key is not an integer
    478                 * we must treat the whole array as an object. We
    479                 * also try to catch a sparsely populated associative
    480                 * array with numeric keys here because some JS engines
    481                 * will create an array with empty indexes up to
    482                 * max_index which can cause memory issues and because
    483                 * the keys, which may be relevant, will be remapped
    484                 * otherwise.
    485                 *
    486                 * As per the ECMA and JSON specification an object may
    487                 * have any string as a property. Unfortunately due to
    488                 * a hole in the ECMA specification if the key is a
    489                 * ECMA reserved word or starts with a digit the
    490                 * parameter is only accessible using ECMAScript's
    491                 * bracket notation.
    492                 */
    493 
    494                 // treat as a JSON object
    495                 if (is_array($var) && count($var) && (array_keys($var) !== range(0, sizeof($var) - 1))) {
    496                     $properties = array_map(array($this, 'name_value'),
    497                                             array_keys($var),
    498                                             array_values($var));
    499 
    500                     foreach($properties as $property) {
    501                         if(Services_JSON::isError($property)) {
    502                             return $property;
    503                         }
    504                     }
    505 
    506                     return '{' . join(',', $properties) . '}';
    507                 }
    508 
    509                 // treat it like a regular array
    510                 $elements = array_map(array($this, '_encode'), $var);
    511 
    512                 foreach($elements as $element) {
    513                     if(Services_JSON::isError($element)) {
    514                         return $element;
    515                     }
    516                 }
    517 
    518                 return '[' . join(',', $elements) . ']';
    519 
    520             case 'object':
    521             
    522                 // support toJSON methods.
    523                 if (($this->use & SERVICES_JSON_USE_TO_JSON) && method_exists($var, 'toJSON')) {
    524                     // this may end up allowing unlimited recursion
    525                     // so we check the return value to make sure it's not got the same method.
    526                     $recode = $var->toJSON();
    527                     
    528                     if (method_exists($recode, 'toJSON')) {
    529                         
    530                         return ($this->use & SERVICES_JSON_SUPPRESS_ERRORS)
    531                         ? 'null'
    532                         : new Services_JSON_Error(get_class($var).
    533                             " toJSON returned an object with a toJSON method.");
    534                             
    535                     }
    536                     
    537                     return $this->_encode( $recode );
    538                 } 
    539                 
    540                 $vars = get_object_vars($var);
    541                 
    542                 $properties = array_map(array($this, 'name_value'),
    543                                         array_keys($vars),
    544                                         array_values($vars));
    545 
    546                 foreach($properties as $property) {
    547                     if(Services_JSON::isError($property)) {
    548                         return $property;
    549                     }
    550                 }
    551 
    552                 return '{' . join(',', $properties) . '}';
    553 
    554             default:
    555                 return ($this->use & SERVICES_JSON_SUPPRESS_ERRORS)
    556                     ? 'null'
    557                     : new Services_JSON_Error(gettype($var)." can not be encoded as JSON string");
    558         }
    559     }
    560 
    561    /**
    562     * array-walking function for use in generating JSON-formatted name-value pairs
    563     *
    564     * @deprecated 5.3.0 Use the PHP native JSON extension instead.
    565     *
    566     * @param    string  $name   name of key to use
    567     * @param    mixed   $value  reference to an array element to be encoded
    568     *
    569     * @return   string  JSON-formatted name-value pair, like '"name":value'
    570     * @access   private
    571     */
    572     function name_value($name, $value)
    573     {
    574         _deprecated_function( __METHOD__, '5.3.0', 'The PHP native JSON extension' );
    575 
    576         $encoded_value = $this->_encode($value);
    577 
    578         if(Services_JSON::isError($encoded_value)) {
    579             return $encoded_value;
    580         }
    581 
    582         return $this->_encode((string) $name) . ':' . $encoded_value;
    583     }
    584 
    585    /**
    586     * reduce a string by removing leading and trailing comments and whitespace
    587     *
    588     * @deprecated 5.3.0 Use the PHP native JSON extension instead.
    589     *
    590     * @param    $str    string      string value to strip of comments and whitespace
    591     *
    592     * @return   string  string value stripped of comments and whitespace
    593     * @access   private
    594     */
    595     function reduce_string($str)
    596     {
    597         _deprecated_function( __METHOD__, '5.3.0', 'The PHP native JSON extension' );
    598 
    599         $str = preg_replace(array(
    600 
    601                 // eliminate single line comments in '// ...' form
    602                 '#^\s*//(.+)$#m',
    603 
    604                 // eliminate multi-line comments in '/* ... */' form, at start of string
    605                 '#^\s*/\*(.+)\*/#Us',
    606 
    607                 // eliminate multi-line comments in '/* ... */' form, at end of string
    608                 '#/\*(.+)\*/\s*$#Us'
    609 
    610             ), '', $str);
    611 
    612         // eliminate extraneous space
    613         return trim($str);
    614     }
    615 
    616    /**
    617     * decodes a JSON string into appropriate variable
    618     *
    619     * @deprecated 5.3.0 Use the PHP native JSON extension instead.
    620     *
    621     * @param    string  $str    JSON-formatted string
    622     *
    623     * @return   mixed   number, boolean, string, array, or object
    624     *                   corresponding to given JSON input string.
    625     *                   See argument 1 to Services_JSON() above for object-output behavior.
    626     *                   Note that decode() always returns strings
    627     *                   in ASCII or UTF-8 format!
    628     * @access   public
    629     */
    630     function decode($str)
    631     {
    632         _deprecated_function( __METHOD__, '5.3.0', 'The PHP native JSON extension' );
    633 
    634         $str = $this->reduce_string($str);
    635 
    636         switch (strtolower($str)) {
    637             case 'true':
    638                 return true;
    639 
    640             case 'false':
    641                 return false;
    642 
    643             case 'null':
    644                 return null;
    645 
    646             default:
    647                 $m = array();
    648 
    649                 if (is_numeric($str)) {
    650                     // Lookie-loo, it's a number
    651 
    652                     // This would work on its own, but I'm trying to be
    653                     // good about returning integers where appropriate:
    654                     // return (float)$str;
    655 
    656                     // Return float or int, as appropriate
    657                     return ((float)$str == (integer)$str)
    658                         ? (integer)$str
    659                         : (float)$str;
    660 
    661                 } elseif (preg_match('/^("|\').*(\1)$/s', $str, $m) && $m[1] == $m[2]) {
    662                     // STRINGS RETURNED IN UTF-8 FORMAT
    663                     $delim = $this->substr8($str, 0, 1);
    664                     $chrs = $this->substr8($str, 1, -1);
    665                     $utf8 = '';
    666                     $strlen_chrs = $this->strlen8($chrs);
    667 
    668                     for ($c = 0; $c < $strlen_chrs; ++$c) {
    669 
    670                         $substr_chrs_c_2 = $this->substr8($chrs, $c, 2);
    671                         $ord_chrs_c = ord($chrs[$c]);
    672 
    673                         switch (true) {
    674                             case $substr_chrs_c_2 == '\b':
    675                                 $utf8 .= chr(0x08);
    676                                 ++$c;
    677                                 break;
    678                             case $substr_chrs_c_2 == '\t':
    679                                 $utf8 .= chr(0x09);
    680                                 ++$c;
    681                                 break;
    682                             case $substr_chrs_c_2 == '\n':
    683                                 $utf8 .= chr(0x0A);
    684                                 ++$c;
    685                                 break;
    686                             case $substr_chrs_c_2 == '\f':
    687                                 $utf8 .= chr(0x0C);
    688                                 ++$c;
    689                                 break;
    690                             case $substr_chrs_c_2 == '\r':
    691                                 $utf8 .= chr(0x0D);
    692                                 ++$c;
    693                                 break;
    694 
    695                             case $substr_chrs_c_2 == '\\"':
    696                             case $substr_chrs_c_2 == '\\\'':
    697                             case $substr_chrs_c_2 == '\\\\':
    698                             case $substr_chrs_c_2 == '\\/':
    699                                 if (($delim == '"' && $substr_chrs_c_2 != '\\\'') ||
    700                                    ($delim == "'" && $substr_chrs_c_2 != '\\"')) {
    701                                     $utf8 .= $chrs[++$c];
    702                                 }
    703                                 break;
    704 
    705                             case preg_match('/\\\u[0-9A-F]{4}/i', $this->substr8($chrs, $c, 6)):
    706                                 // single, escaped unicode character
    707                                 $utf16 = chr(hexdec($this->substr8($chrs, ($c + 2), 2)))
    708                                        . chr(hexdec($this->substr8($chrs, ($c + 4), 2)));
    709                                 $utf8 .= $this->utf162utf8($utf16);
    710                                 $c += 5;
    711                                 break;
    712 
    713                             case ($ord_chrs_c >= 0x20) && ($ord_chrs_c <= 0x7F):
    714                                 $utf8 .= $chrs[$c];
    715                                 break;
    716 
    717                             case ($ord_chrs_c & 0xE0) == 0xC0:
    718                                 // characters U-00000080 - U-000007FF, mask 110XXXXX
    719                                 //see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
    720                                 $utf8 .= $this->substr8($chrs, $c, 2);
    721                                 ++$c;
    722                                 break;
    723 
    724                             case ($ord_chrs_c & 0xF0) == 0xE0:
    725                                 // characters U-00000800 - U-0000FFFF, mask 1110XXXX
    726                                 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
    727                                 $utf8 .= $this->substr8($chrs, $c, 3);
    728                                 $c += 2;
    729                                 break;
    730 
    731                             case ($ord_chrs_c & 0xF8) == 0xF0:
    732                                 // characters U-00010000 - U-001FFFFF, mask 11110XXX
    733                                 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
    734                                 $utf8 .= $this->substr8($chrs, $c, 4);
    735                                 $c += 3;
    736                                 break;
    737 
    738                             case ($ord_chrs_c & 0xFC) == 0xF8:
    739                                 // characters U-00200000 - U-03FFFFFF, mask 111110XX
    740                                 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
    741                                 $utf8 .= $this->substr8($chrs, $c, 5);
    742                                 $c += 4;
    743                                 break;
    744 
    745                             case ($ord_chrs_c & 0xFE) == 0xFC:
    746                                 // characters U-04000000 - U-7FFFFFFF, mask 1111110X
    747                                 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
    748                                 $utf8 .= $this->substr8($chrs, $c, 6);
    749                                 $c += 5;
    750                                 break;
    751 
    752                         }
    753 
    754                     }
    755 
    756                     return $utf8;
    757 
    758                 } elseif (preg_match('/^\[.*\]$/s', $str) || preg_match('/^\{.*\}$/s', $str)) {
    759                     // array, or object notation
    760 
    761                     if ($str[0] == '[') {
    762                         $stk = array(SERVICES_JSON_IN_ARR);
    763                         $arr = array();
    764                     } else {
    765                         if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
    766                             $stk = array(SERVICES_JSON_IN_OBJ);
    767                             $obj = array();
    768                         } else {
    769                             $stk = array(SERVICES_JSON_IN_OBJ);
    770                             $obj = new stdClass();
    771                         }
    772                     }
    773 
    774                     array_push($stk, array('what'  => SERVICES_JSON_SLICE,
    775                                            'where' => 0,
    776                                            'delim' => false));
    777 
    778                     $chrs = $this->substr8($str, 1, -1);
    779                     $chrs = $this->reduce_string($chrs);
    780 
    781                     if ($chrs == '') {
    782                         if (reset($stk) == SERVICES_JSON_IN_ARR) {
    783                             return $arr;
    784 
    785                         } else {
    786                             return $obj;
    787 
    788                         }
    789                     }
    790 
    791                     //print("\nparsing {$chrs}\n");
    792 
    793                     $strlen_chrs = $this->strlen8($chrs);
    794 
    795                     for ($c = 0; $c <= $strlen_chrs; ++$c) {
    796 
    797                         $top = end($stk);
    798                         $substr_chrs_c_2 = $this->substr8($chrs, $c, 2);
    799 
    800                         if (($c == $strlen_chrs) || (($chrs[$c] == ',') && ($top['what'] == SERVICES_JSON_SLICE))) {
    801                             // found a comma that is not inside a string, array, etc.,
    802                             // OR we've reached the end of the character list
    803                             $slice = $this->substr8($chrs, $top['where'], ($c - $top['where']));
    804                             array_push($stk, array('what' => SERVICES_JSON_SLICE, 'where' => ($c + 1), 'delim' => false));
    805                             //print("Found split at {$c}: ".$this->substr8($chrs, $top['where'], (1 + $c - $top['where']))."\n");
    806 
    807                             if (reset($stk) == SERVICES_JSON_IN_ARR) {
    808                                 // we are in an array, so just push an element onto the stack
    809                                 array_push($arr, $this->decode($slice));
    810 
    811                             } elseif (reset($stk) == SERVICES_JSON_IN_OBJ) {
    812                                 // we are in an object, so figure
    813                                 // out the property name and set an
    814                                 // element in an associative array,
    815                                 // for now
    816                                 $parts = array();
    817                                 
    818                                if (preg_match('/^\s*(["\'].*[^\\\]["\'])\s*:/Uis', $slice, $parts)) {
    819                                     // "name":value pair
    820                                     $key = $this->decode($parts[1]);
    821                                     $val = $this->decode(trim(substr($slice, strlen($parts[0])), ", \t\n\r\0\x0B"));
    822                                     if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
    823                                         $obj[$key] = $val;
    824                                     } else {
    825                                         $obj->$key = $val;
    826                                     }
    827                                 } elseif (preg_match('/^\s*(\w+)\s*:/Uis', $slice, $parts)) {
    828                                     // name:value pair, where name is unquoted
    829                                     $key = $parts[1];
    830                                     $val = $this->decode(trim(substr($slice, strlen($parts[0])), ", \t\n\r\0\x0B"));
    831 
    832                                     if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
    833                                         $obj[$key] = $val;
    834                                     } else {
    835                                         $obj->$key = $val;
    836                                     }
    837                                 }
    838 
    839                             }
    840 
    841                         } elseif ((($chrs[$c] == '"') || ($chrs[$c] == "'")) && ($top['what'] != SERVICES_JSON_IN_STR)) {
    842                             // found a quote, and we are not inside a string
    843                             array_push($stk, array('what' => SERVICES_JSON_IN_STR, 'where' => $c, 'delim' => $chrs[$c]));
    844                             //print("Found start of string at {$c}\n");
    845 
    846                         } elseif (($chrs[$c] == $top['delim']) &&
    847                                  ($top['what'] == SERVICES_JSON_IN_STR) &&
    848                                  (($this->strlen8($this->substr8($chrs, 0, $c)) - $this->strlen8(rtrim($this->substr8($chrs, 0, $c), '\\'))) % 2 != 1)) {
    849                             // found a quote, we're in a string, and it's not escaped
    850                             // we know that it's not escaped because there is _not_ an
    851                             // odd number of backslashes at the end of the string so far
    852                             array_pop($stk);
    853                             //print("Found end of string at {$c}: ".$this->substr8($chrs, $top['where'], (1 + 1 + $c - $top['where']))."\n");
    854 
    855                         } elseif (($chrs[$c] == '[') &&
    856                                  in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
    857                             // found a left-bracket, and we are in an array, object, or slice
    858                             array_push($stk, array('what' => SERVICES_JSON_IN_ARR, 'where' => $c, 'delim' => false));
    859                             //print("Found start of array at {$c}\n");
    860 
    861                         } elseif (($chrs[$c] == ']') && ($top['what'] == SERVICES_JSON_IN_ARR)) {
    862                             // found a right-bracket, and we're in an array
    863                             array_pop($stk);
    864                             //print("Found end of array at {$c}: ".$this->substr8($chrs, $top['where'], (1 + $c - $top['where']))."\n");
    865 
    866                         } elseif (($chrs[$c] == '{') &&
    867                                  in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
    868                             // found a left-brace, and we are in an array, object, or slice
    869                             array_push($stk, array('what' => SERVICES_JSON_IN_OBJ, 'where' => $c, 'delim' => false));
    870                             //print("Found start of object at {$c}\n");
    871 
    872                         } elseif (($chrs[$c] == '}') && ($top['what'] == SERVICES_JSON_IN_OBJ)) {
    873                             // found a right-brace, and we're in an object
    874                             array_pop($stk);
    875                             //print("Found end of object at {$c}: ".$this->substr8($chrs, $top['where'], (1 + $c - $top['where']))."\n");
    876 
    877                         } elseif (($substr_chrs_c_2 == '/*') &&
    878                                  in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
    879                             // found a comment start, and we are in an array, object, or slice
    880                             array_push($stk, array('what' => SERVICES_JSON_IN_CMT, 'where' => $c, 'delim' => false));
    881                             $c++;
    882                             //print("Found start of comment at {$c}\n");
    883 
    884                         } elseif (($substr_chrs_c_2 == '*/') && ($top['what'] == SERVICES_JSON_IN_CMT)) {
    885                             // found a comment end, and we're in one now
    886                             array_pop($stk);
    887                             $c++;
    888 
    889                             for ($i = $top['where']; $i <= $c; ++$i)
    890                                 $chrs = substr_replace($chrs, ' ', $i, 1);
    891 
    892                             //print("Found end of comment at {$c}: ".$this->substr8($chrs, $top['where'], (1 + $c - $top['where']))."\n");
    893 
    894                         }
    895 
    896                     }
    897 
    898                     if (reset($stk) == SERVICES_JSON_IN_ARR) {
    899                         return $arr;
    900 
    901                     } elseif (reset($stk) == SERVICES_JSON_IN_OBJ) {
    902                         return $obj;
    903 
    904                     }
    905 
    906                 }
    907         }
    908     }
    909 
    910     /**
    911      * @deprecated 5.3.0 Use the PHP native JSON extension instead.
    912      *
    913      * @todo Ultimately, this should just call PEAR::isError()
    914      */
    915     function isError($data, $code = null)
    916     {
    917         _deprecated_function( __METHOD__, '5.3.0', 'The PHP native JSON extension' );
    918 
    919         if (class_exists('pear')) {
    920             return PEAR::isError($data, $code);
    921         } elseif (is_object($data) && ($data instanceof services_json_error ||
    922                                  is_subclass_of($data, 'services_json_error'))) {
    923             return true;
    924         }
    925 
    926         return false;
    927     }
    928     
    929     /**
    930      * Calculates length of string in bytes
    931      *
    932      * @deprecated 5.3.0 Use the PHP native JSON extension instead.
    933      *
    934      * @param string
    935      * @return integer length
    936      */
    937     function strlen8( $str ) 
    938     {
    939         _deprecated_function( __METHOD__, '5.3.0', 'The PHP native JSON extension' );
    940 
    941         if ( $this->_mb_strlen ) {
    942             return mb_strlen( $str, "8bit" );
    943         }
    944         return strlen( $str );
    945     }
    946     
    947     /**
    948      * Returns part of a string, interpreting $start and $length as number of bytes.
    949      *
    950      * @deprecated 5.3.0 Use the PHP native JSON extension instead.
    951      *
    952      * @param string
    953      * @param integer start
    954      * @param integer length
    955      * @return integer length
    956      */
    957     function substr8( $string, $start, $length=false ) 
    958     {
    959         _deprecated_function( __METHOD__, '5.3.0', 'The PHP native JSON extension' );
    960 
    961         if ( $length === false ) {
    962             $length = $this->strlen8( $string ) - $start;
    963         }
    964         if ( $this->_mb_substr ) {
    965             return mb_substr( $string, $start, $length, "8bit" );
    966         }
    967         return substr( $string, $start, $length );
    968     }
    969 
    970 }
    971 
    972 if (class_exists('PEAR_Error')) {
    973 
    974     class Services_JSON_Error extends PEAR_Error
    975     {
    976 	    /**
    977 	     * PHP5 constructor.
    978 	     *
    979 	     * @deprecated 5.3.0 Use the PHP native JSON extension instead.
    980 	     */
    981         function __construct($message = 'unknown error', $code = null,
    982                                      $mode = null, $options = null, $userinfo = null)
    983         {
    984             _deprecated_function( __METHOD__, '5.3.0', 'The PHP native JSON extension' );
    985 
    986             parent::PEAR_Error($message, $code, $mode, $options, $userinfo);
    987         }
    988 
    989 	    /**
    990 	     * PHP4 constructor.
    991 	     *
    992 	     * @deprecated 5.3.0 Use __construct() instead.
    993 	     *
    994 	     * @see Services_JSON_Error::__construct()
    995 	     */
    996 		public function Services_JSON_Error($message = 'unknown error', $code = null,
    997                                      $mode = null, $options = null, $userinfo = null) {
    998 			_deprecated_constructor( 'Services_JSON_Error', '5.3.0', get_class( $this ) );
    999 			self::__construct($message, $code, $mode, $options, $userinfo);
   1000 		}
   1001     }
   1002 
   1003 } else {
   1004 
   1005     /**
   1006      * @todo Ultimately, this class shall be descended from PEAR_Error
   1007      */
   1008     class Services_JSON_Error
   1009     {
   1010 	    /**
   1011 	     * PHP5 constructor.
   1012 	     *
   1013 	     * @deprecated 5.3.0 Use the PHP native JSON extension instead.
   1014 	     */
   1015         function __construct( $message = 'unknown error', $code = null,
   1016                                      $mode = null, $options = null, $userinfo = null )
   1017         {
   1018             _deprecated_function( __METHOD__, '5.3.0', 'The PHP native JSON extension' );
   1019         }
   1020 
   1021 	    /**
   1022 	     * PHP4 constructor.
   1023 	     *
   1024 	     * @deprecated 5.3.0 Use __construct() instead.
   1025 	     *
   1026 	     * @see Services_JSON_Error::__construct()
   1027 	     */
   1028 		public function Services_JSON_Error( $message = 'unknown error', $code = null,
   1029 	                                     $mode = null, $options = null, $userinfo = null ) {
   1030 			_deprecated_constructor( 'Services_JSON_Error', '5.3.0', get_class( $this ) );
   1031 			self::__construct( $message, $code, $mode, $options, $userinfo );
   1032 		}
   1033     }
   1034 
   1035 }
   1036 
   1037 endif;