WordPress.org

Make WordPress Core

Ticket #10337: json.php4.patch

File json.php4.patch, 37.8 KB (added by Viper007Bond, 12 years ago)

This patch is required for all future patches if you're running PHP4 (future patches won't have this JSON stuff included)

  • wp-includes/class-json.php

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

     
    5656        }
    5757}
    5858
    59 if ( ! function_exists('hash_hmac') ):
     59if ( !function_exists('hash_hmac') ):
    6060function hash_hmac($algo, $data, $key, $raw_output = false) {
    6161        $packs = array('md5' => 'H32', 'sha1' => 'H40');
    6262
     
    7777}
    7878endif;
    7979
    80 if ( ! function_exists('mb_substr') ):
     80if ( !function_exists('mb_substr') ):
    8181        function mb_substr( $str, $start, $length=null, $encoding=null ) {
    8282                return _mb_substr($str, $start, $length, $encoding);
    8383        }
     
    115115        }
    116116}
    117117
    118 ?>
     118if ( !function_exists('json_encode') ) {
     119        function json_encode( $string ) {
     120                require_once( 'class-json.php' );
     121
     122                $json = new Services_JSON();
     123
     124                return $json->encode( $string );
     125        }
     126}
     127
     128if ( !function_exists('json_decode') ) {
     129        function json_decode( $string ) {
     130                require_once( 'class-json.php' );
     131
     132                $json = new Services_JSON();
     133
     134                return $json->decode( $string );
     135        }
     136}