WordPress.org

Make WordPress Core

Ticket #47699: 47699-remove-json-polyfills-and-work-arounds.patch

File 47699-remove-json-polyfills-and-work-arounds.patch, 57.3 KB (added by jrf, 15 months ago)
  • phpcs.xml.dist

    From aa03096878d057304067c558a30cb08f17ee15a8 Mon Sep 17 00:00:00 2001
    From: jrfnl <jrfnl@users.noreply.github.com>
    Date: Fri, 12 Jul 2019 06:13:58 +0200
    Subject: [PATCH] Remove JSON extension related polyfills and work-arounds
    
    ---
     phpcs.xml.dist                                |   1 -
     src/wp-admin/includes/noop.php                |   7 -
     src/wp-includes/class-json.php                | 968 +-----------------
     .../class-wp-customize-manager.php            |  15 +-
     src/wp-includes/compat.php                    | 126 ---
     src/wp-includes/functions.php                 |  71 +-
     .../rest-api/class-wp-rest-request.php        |  13 +-
     .../rest-api/class-wp-rest-server.php         |   7 +-
     tests/phpunit/tests/compat.php                |   8 -
     tests/phpunit/tests/customize/manager.php     |   4 +-
     .../tests/rest-api/rest-schema-setup.php      |  12 +-
     11 files changed, 36 insertions(+), 1196 deletions(-)
     rewrite src/wp-includes/class-json.php (99%)
    
    diff --git a/phpcs.xml.dist b/phpcs.xml.dist
    index 4a37fd55de..ef0c415e2c 100644
    a b  
    8686
    8787        <exclude-pattern>/src/wp-includes/atomlib\.php</exclude-pattern>
    8888        <exclude-pattern>/src/wp-includes/class-IXR\.php</exclude-pattern>
    89         <exclude-pattern>/src/wp-includes/class-json\.php</exclude-pattern>
    9089        <exclude-pattern>/src/wp-includes/class-phpass\.php</exclude-pattern>
    9190        <exclude-pattern>/src/wp-includes/class-phpmailer\.php</exclude-pattern>
    9291        <exclude-pattern>/src/wp-includes/class-pop3\.php</exclude-pattern>
  • src/wp-admin/includes/noop.php

    diff --git a/src/wp-admin/includes/noop.php b/src/wp-admin/includes/noop.php
    index 134bef7efe..6e2397ea2e 100644
    a b function includes_url() {} 
    9393 */
    9494function wp_guess_url() {}
    9595
    96 if ( ! function_exists( 'json_encode' ) ) :
    97         /**
    98          * @ignore
    99          */
    100         function json_encode() {}
    101 endif;
    102 
    10396function get_file( $path ) {
    10497
    10598        if ( function_exists( 'realpath' ) ) {
  • src/wp-includes/class-json.php

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

    diff --git a/src/wp-includes/class-wp-customize-manager.php b/src/wp-includes/class-wp-customize-manager.php
    index 4d7bf3ba44..9bed442067 100644
    a b final class WP_Customize_Manager { 
    11161116                        return new WP_Error( 'wrong_post_type' );
    11171117                }
    11181118                $changeset_data = json_decode( $changeset_post->post_content, true );
    1119                 if ( function_exists( 'json_last_error' ) && json_last_error() ) {
    1120                         return new WP_Error( 'json_parse_error', '', json_last_error() );
     1119                $last_error     = json_last_error();
     1120                if ( $last_error ) {
     1121                        return new WP_Error( 'json_parse_error', '', $last_error );
    11211122                }
    11221123                if ( ! is_array( $changeset_data ) ) {
    11231124                        return new WP_Error( 'expected_array' );
    final class WP_Customize_Manager { 
    28432844                }
    28442845
    28452846                // Gather the data for wp_insert_post()/wp_update_post().
    2846                 $json_options = 0;
    2847                 if ( defined( 'JSON_UNESCAPED_SLASHES' ) ) {
    2848                         $json_options |= JSON_UNESCAPED_SLASHES; // Introduced in PHP 5.4. This is only to improve readability as slashes needn't be escaped in storage.
    2849                 }
    2850                 $json_options |= JSON_PRETTY_PRINT; // Also introduced in PHP 5.4, but WP defines constant for back compat. See WP Trac #30139.
    2851                 $post_array    = array(
    2852                         'post_content' => wp_json_encode( $data, $json_options ),
     2847                $post_array = array(
     2848                        // JSON_UNESCAPED_SLASHES is only to improve readability as slashes needn't be escaped in storage.
     2849                        'post_content' => wp_json_encode( $data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT ),
    28532850                );
    28542851                if ( $args['title'] ) {
    28552852                        $post_array['post_title'] = $args['title'];
  • src/wp-includes/compat.php

    diff --git a/src/wp-includes/compat.php b/src/wp-includes/compat.php
    index 38d51900c4..92eaba7417 100644
    a b function _hash_hmac( $algo, $data, $key, $raw_output = false ) { 
    282282        return $hmac;
    283283}
    284284
    285 if ( ! function_exists( 'json_encode' ) ) {
    286         function json_encode( $string ) {
    287                 global $wp_json;
    288 
    289                 if ( ! ( $wp_json instanceof Services_JSON ) ) {
    290                         require_once( ABSPATH . WPINC . '/class-json.php' );
    291                         $wp_json = new Services_JSON();
    292                 }
    293 
    294                 return $wp_json->encodeUnsafe( $string );
    295         }
    296 }
    297 
    298 if ( ! function_exists( 'json_decode' ) ) {
    299         /**
    300          * @global Services_JSON $wp_json
    301          * @param string $string
    302          * @param bool   $assoc_array
    303          * @return object|array
    304          */
    305         function json_decode( $string, $assoc_array = false ) {
    306                 global $wp_json;
    307 
    308                 if ( ! ( $wp_json instanceof Services_JSON ) ) {
    309                         require_once( ABSPATH . WPINC . '/class-json.php' );
    310                         $wp_json = new Services_JSON();
    311                 }
    312 
    313                 $res = $wp_json->decode( $string );
    314                 if ( $assoc_array ) {
    315                         $res = _json_decode_object_helper( $res );
    316                 }
    317                 return $res;
    318         }
    319 
    320         /**
    321          * @param object $data
    322          * @return array
    323          */
    324         function _json_decode_object_helper( $data ) {
    325                 if ( is_object( $data ) ) {
    326                         $data = get_object_vars( $data );
    327                 }
    328                 return is_array( $data ) ? array_map( __FUNCTION__, $data ) : $data;
    329         }
    330 }
    331 
    332285if ( ! function_exists( 'hash_equals' ) ) :
    333286        /**
    334287         * Timing attack safe string comparison
    if ( ! function_exists( 'hash_equals' ) ) : 
    361314        }
    362315endif;
    363316
    364 // JSON_PRETTY_PRINT was introduced in PHP 5.4
    365 // Defined here to prevent a notice when using it with wp_json_encode()
    366 if ( ! defined( 'JSON_PRETTY_PRINT' ) ) {
    367         define( 'JSON_PRETTY_PRINT', 128 );
    368 }
    369 
    370 if ( ! function_exists( 'json_last_error_msg' ) ) :
    371         /**
    372          * Retrieves the error string of the last json_encode() or json_decode() call.
    373          *
    374          * @since 4.4.0
    375          *
    376          * @internal This is a compatibility function for PHP <5.5
    377          *
    378          * @return bool|string Returns the error message on success, "No Error" if no error has occurred,
    379          *                     or false on failure.
    380          */
    381         function json_last_error_msg() {
    382                 // See https://core.trac.wordpress.org/ticket/27799.
    383                 if ( ! function_exists( 'json_last_error' ) ) {
    384                         return false;
    385                 }
    386 
    387                 $last_error_code = json_last_error();
    388 
    389                 // Just in case JSON_ERROR_NONE is not defined.
    390                 $error_code_none = defined( 'JSON_ERROR_NONE' ) ? JSON_ERROR_NONE : 0;
    391 
    392                 switch ( true ) {
    393                         case $last_error_code === $error_code_none:
    394                                 return 'No error';
    395 
    396                         case defined( 'JSON_ERROR_DEPTH' ) && JSON_ERROR_DEPTH === $last_error_code:
    397                                 return 'Maximum stack depth exceeded';
    398 
    399                         case defined( 'JSON_ERROR_STATE_MISMATCH' ) && JSON_ERROR_STATE_MISMATCH === $last_error_code:
    400                                 return 'State mismatch (invalid or malformed JSON)';
    401 
    402                         case defined( 'JSON_ERROR_CTRL_CHAR' ) && JSON_ERROR_CTRL_CHAR === $last_error_code:
    403                                 return 'Control character error, possibly incorrectly encoded';
    404 
    405                         case defined( 'JSON_ERROR_SYNTAX' ) && JSON_ERROR_SYNTAX === $last_error_code:
    406                                 return 'Syntax error';
    407 
    408                         case defined( 'JSON_ERROR_UTF8' ) && JSON_ERROR_UTF8 === $last_error_code:
    409                                 return 'Malformed UTF-8 characters, possibly incorrectly encoded';
    410 
    411                         case defined( 'JSON_ERROR_RECURSION' ) && JSON_ERROR_RECURSION === $last_error_code:
    412                                 return 'Recursion detected';
    413 
    414                         case defined( 'JSON_ERROR_INF_OR_NAN' ) && JSON_ERROR_INF_OR_NAN === $last_error_code:
    415                                 return 'Inf and NaN cannot be JSON encoded';
    416 
    417                         case defined( 'JSON_ERROR_UNSUPPORTED_TYPE' ) && JSON_ERROR_UNSUPPORTED_TYPE === $last_error_code:
    418                                 return 'Type is not supported';
    419 
    420                         default:
    421                                 return 'An unknown error occurred';
    422                 }
    423         }
    424 endif;
    425 
    426 if ( ! interface_exists( 'JsonSerializable' ) ) {
    427         define( 'WP_JSON_SERIALIZE_COMPATIBLE', true );
    428         /**
    429          * JsonSerializable interface.
    430          *
    431          * Compatibility shim for PHP <5.4
    432          *
    433          * @link https://secure.php.net/jsonserializable
    434          *
    435          * @since 4.4.0
    436          */
    437         interface JsonSerializable {
    438                 // phpcs:ignore WordPress.NamingConventions.ValidFunctionName.MethodNameInvalid
    439                 public function jsonSerialize();
    440         }
    441 }
    442 
    443317// random_int was introduced in PHP 7.0
    444318if ( ! function_exists( 'random_int' ) ) {
    445319        require ABSPATH . WPINC . '/random_compat/random.php';
  • src/wp-includes/functions.php

    diff --git a/src/wp-includes/functions.php b/src/wp-includes/functions.php
    index a66b3fa406..73ed7ef7b5 100644
    a b function _wp_die_process_input( $message, $title = '', $args = array() ) { 
    35713571 * Encode a variable into JSON, with some sanity checks.
    35723572 *
    35733573 * @since 4.1.0
     3574 * @since 5.3.0 No longer handles support for PHP < 5.6.
    35743575 *
    35753576 * @param mixed $data    Variable (usually an array or object) to encode as JSON.
    35763577 * @param int   $options Optional. Options to be passed to json_encode(). Default 0.
    function _wp_die_process_input( $message, $title = '', $args = array() ) { 
    35793580 * @return string|false The JSON encoded string, or false if it cannot be encoded.
    35803581 */
    35813582function wp_json_encode( $data, $options = 0, $depth = 512 ) {
    3582         /*
    3583          * json_encode() has had extra params added over the years.
    3584          * $options was added in 5.3, and $depth in 5.5.
    3585          * We need to make sure we call it with the correct arguments.
    3586          */
    3587         if ( version_compare( PHP_VERSION, '5.5', '>=' ) ) {
    3588                 $args = array( $data, $options, $depth );
    3589         } elseif ( version_compare( PHP_VERSION, '5.3', '>=' ) ) {
    3590                 $args = array( $data, $options );
    3591         } else {
    3592                 $args = array( $data );
    3593         }
    3594 
    3595         // Prepare the data for JSON serialization.
    3596         $args[0] = _wp_json_prepare_data( $data );
    3597 
    3598         // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged -- json_encode() errors are handled after this call
    3599         $json = @call_user_func_array( 'json_encode', $args );
     3583        $json = json_encode( $data, $options, $depth );
    36003584
    36013585        // If json_encode() was successful, no need to do more sanity checking.
    3602         // ... unless we're in an old version of PHP, and json_encode() returned
    3603         // a string containing 'null'. Then we need to do more sanity checking.
    3604         if ( false !== $json && ( version_compare( PHP_VERSION, '5.5', '>=' ) || false === strpos( $json, 'null' ) ) ) {
     3586        if ( false !== $json ) {
    36053587                return $json;
    36063588        }
    36073589
    36083590        try {
    3609                 $args[0] = _wp_json_sanity_check( $data, $depth );
     3591                $data = _wp_json_sanity_check( $data, $depth );
    36103592        } catch ( Exception $e ) {
    36113593                return false;
    36123594        }
    36133595
    3614         return call_user_func_array( 'json_encode', $args );
     3596        return json_encode( $data, $options, $depth );
    36153597}
    36163598
    36173599/**
    function _wp_json_convert_string( $string ) { 
    37153697 * This supports the JsonSerializable interface for PHP 5.2-5.3 as well.
    37163698 *
    37173699 * @ignore
    3718  * @since 4.4.0
    3719  * @access private
     3700 * @since      4.4.0
     3701 * @deprecated 5.3.0 This function is no longer needed as support for PHP 5.2-5.3
     3702 *                   has been dropped.
     3703 * @access     private
    37203704 *
    37213705 * @param mixed $data Native representation.
    37223706 * @return bool|int|float|null|string|array Data ready for `json_encode()`.
    37233707 */
    37243708function _wp_json_prepare_data( $data ) {
    3725         if ( ! defined( 'WP_JSON_SERIALIZE_COMPATIBLE' ) || WP_JSON_SERIALIZE_COMPATIBLE === false ) {
    3726                 return $data;
    3727         }
    3728 
    3729         switch ( gettype( $data ) ) {
    3730                 case 'boolean':
    3731                 case 'integer':
    3732                 case 'double':
    3733                 case 'string':
    3734                 case 'NULL':
    3735                         // These values can be passed through.
    3736                         return $data;
    3737 
    3738                 case 'array':
    3739                         // Arrays must be mapped in case they also return objects.
    3740                         return array_map( '_wp_json_prepare_data', $data );
    3741 
    3742                 case 'object':
    3743                         // If this is an incomplete object (__PHP_Incomplete_Class), bail.
    3744                         if ( ! is_object( $data ) ) {
    3745                                 return null;
    3746                         }
    3747 
    3748                         if ( $data instanceof JsonSerializable ) {
    3749                                 $data = $data->jsonSerialize();
    3750                         } else {
    3751                                 $data = get_object_vars( $data );
    3752                         }
    3753 
    3754                         // Now, pass the array (or whatever was returned from jsonSerialize through).
    3755                         return _wp_json_prepare_data( $data );
    3756 
    3757                 default:
    3758                         return null;
    3759         }
     3709        _deprecated_function( __FUNCTION__, '5.3.0' );
     3710        return $data;
    37603711}
    37613712
    37623713/**
  • src/wp-includes/rest-api/class-wp-rest-request.php

    diff --git a/src/wp-includes/rest-api/class-wp-rest-request.php b/src/wp-includes/rest-api/class-wp-rest-request.php
    index ee106b5c5a..c66410d315 100644
    a b class WP_REST_Request implements ArrayAccess { 
    637637
    638638                /*
    639639                 * Check for a parsing error.
    640                  *
    641                  * Note that due to WP's JSON compatibility functions, json_last_error
    642                  * might not be defined: https://core.trac.wordpress.org/ticket/27799
    643640                 */
    644                 if ( null === $params && ( ! function_exists( 'json_last_error' ) || JSON_ERROR_NONE !== json_last_error() ) ) {
     641                if ( null === $params && JSON_ERROR_NONE !== json_last_error() ) {
    645642                        // Ensure subsequent calls receive error instance.
    646643                        $this->parsed_json = false;
    647644
    648645                        $error_data = array(
    649                                 'status' => WP_Http::BAD_REQUEST,
     646                                'status'             => WP_Http::BAD_REQUEST,
     647                                'json_error_code'    => json_last_error(),
     648                                'json_error_message' => json_last_error_msg(),
    650649                        );
    651                         if ( function_exists( 'json_last_error' ) ) {
    652                                 $error_data['json_error_code']    = json_last_error();
    653                                 $error_data['json_error_message'] = json_last_error_msg();
    654                         }
    655650
    656651                        return new WP_Error( 'rest_invalid_json', __( 'Invalid JSON body passed.' ), $error_data );
    657652                }
  • src/wp-includes/rest-api/class-wp-rest-server.php

    diff --git a/src/wp-includes/rest-api/class-wp-rest-server.php b/src/wp-includes/rest-api/class-wp-rest-server.php
    index f34a6106e3..9bd7190169 100644
    a b class WP_REST_Server { 
    996996         * @return bool|string Boolean false or string error message.
    997997         */
    998998        protected function get_json_last_error() {
    999                 // See https://core.trac.wordpress.org/ticket/27799.
    1000                 if ( ! function_exists( 'json_last_error' ) ) {
    1001                         return false;
    1002                 }
    1003 
    1004999                $last_error_code = json_last_error();
    10051000
    1006                 if ( ( defined( 'JSON_ERROR_NONE' ) && JSON_ERROR_NONE === $last_error_code ) || empty( $last_error_code ) ) {
     1001                if ( JSON_ERROR_NONE === $last_error_code || empty( $last_error_code ) ) {
    10071002                        return false;
    10081003                }
    10091004
  • tests/phpunit/tests/compat.php

    diff --git a/tests/phpunit/tests/compat.php b/tests/phpunit/tests/compat.php
    index d70d0271b1..fe8f1acbae 100644
    a b EOT; 
    179179                $this->assertEquals( array( 1 => '993003b95758e0ac2eba451a4c5877eb1bb7b92a' ), unpack( 'H40', _hash_hmac( 'sha1', 'simple', 'key', true ) ) );
    180180        }
    181181
    182         function test_json_encode_decode() {
    183                 require_once( ABSPATH . WPINC . '/class-json.php' );
    184                 $json = new Services_JSON();
    185                 // Super basic test to verify Services_JSON is intact and working.
    186                 $this->assertEquals( '["foo"]', $json->encodeUnsafe( array( 'foo' ) ) );
    187                 $this->assertEquals( array( 'foo' ), $json->decode( '["foo"]' ) );
    188         }
    189 
    190182        /**
    191183         * Test that is_countable() is always available (either from PHP or WP).
    192184         *
  • tests/phpunit/tests/customize/manager.php

    diff --git a/tests/phpunit/tests/customize/manager.php b/tests/phpunit/tests/customize/manager.php
    index f081245c6b..5656fd9ebe 100644
    a b class Tests_WP_Customize_Manager extends WP_UnitTestCase { 
    18561856
    18571857                $r = $manager->save_changeset_post( $args );
    18581858                $this->assertInstanceOf( 'WP_Error', $r );
    1859                 if ( function_exists( 'json_last_error' ) ) {
    1860                         $this->assertEquals( 'json_parse_error', $r->get_error_code() );
    1861                 }
     1859                $this->assertEquals( 'json_parse_error', $r->get_error_code() );
    18621860
    18631861                wp_update_post(
    18641862                        array(
  • tests/phpunit/tests/rest-api/rest-schema-setup.php

    diff --git a/tests/phpunit/tests/rest-api/rest-schema-setup.php b/tests/phpunit/tests/rest-api/rest-schema-setup.php
    index 92219b31da..f4e330e827 100644
    a b class WP_Test_REST_Schema_Initialization extends WP_Test_REST_TestCase { 
    449449                        );
    450450                        $this->assertTrue( ! empty( $data ), $route['name'] . ' route should return data.' );
    451451
    452                         if ( version_compare( PHP_VERSION, '5.4', '>=' ) ) {
    453                                 $fixture           = $this->normalize_fixture( $data, $route['name'] );
    454                                 $mocked_responses .= "\nmockedApiResponse." . $route['name'] . ' = '
    455                                         . json_encode( $fixture, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES )
    456                                         . ";\n";
    457                         }
     452                        $fixture           = $this->normalize_fixture( $data, $route['name'] );
     453                        $mocked_responses .= "\nmockedApiResponse." . $route['name'] . ' = '
     454                                . json_encode( $fixture, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES )
     455                                . ";\n";
    458456                }
    459457
    460458                // Only generate API client fixtures in single site and when required JSON_* constants are supported.
    461                 if ( ! is_multisite() && version_compare( PHP_VERSION, '5.4', '>=' ) ) {
     459                if ( ! is_multisite() ) {
    462460                        // Save the route object for QUnit tests.
    463461                        $file = dirname( DIR_TESTROOT ) . '/qunit/fixtures/wp-api-generated.js';
    464462                        file_put_contents( $file, $mocked_responses );