_deprecated_file( basename( __FILE__ ), '5.3.0', '', 'The PHP native JSON extension is now a requirement.' );
if ( ! class_exists( 'Services_JSON' ) ) :
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
* Converts to and from JSON format.
* JSON (JavaScript Object Notation) is a lightweight data-interchange
* format. It is easy for humans to read and write. It is easy for machines
* to parse and generate. It is based on a subset of the JavaScript
* Programming Language, Standard ECMA-262 3rd Edition - December 1999.
* This feature can also be found in Python. JSON is a text format that is
* completely language independent but uses conventions that are familiar
* to programmers of the C-family of languages, including C, C++, C#, Java,
* JavaScript, Perl, TCL, and many others. These properties make JSON an
* ideal data-interchange language.
* This package provides a simple encoder and decoder for JSON notation. It
* is intended for use with client-side JavaScript applications that make
* use of HTTPRequest to perform server communication functions - data can
* be encoded into JSON notation for use in a client-side javaScript, or
* decoded from incoming JavaScript requests. JSON format is native to
* JavaScript, and can be directly eval()'ed with no further parsing
* All strings should be in ASCII or UTF-8 format!
* LICENSE: Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met: Redistributions of source code must retain the
* above copyright notice, this list of conditions and the following
* disclaimer. Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
* NO EVENT SHALL CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* @author Michal Migurski <mike-json@teczno.com>
* @author Matt Knapp <mdknapp[at]gmail[dot]com>
* @author Brett Stimmerman <brettstimmerman[at]gmail[dot]com>
* @copyright 2005 Michal Migurski
* @version CVS: $Id: JSON.php 305040 2010-11-02 23:19:03Z alan_k $
* @license http://www.opensource.org/licenses/bsd-license.php
* @link http://pear.php.net/pepr/pepr-proposal-show.php?id=198
* Marker constant for Services_JSON::decode(), used to flag stack state
define('SERVICES_JSON_SLICE', 1);
* Marker constant for Services_JSON::decode(), used to flag stack state
define('SERVICES_JSON_IN_STR', 2);
* Marker constant for Services_JSON::decode(), used to flag stack state
define('SERVICES_JSON_IN_ARR', 3);
* Marker constant for Services_JSON::decode(), used to flag stack state
define('SERVICES_JSON_IN_OBJ', 4);
* Marker constant for Services_JSON::decode(), used to flag stack state
define('SERVICES_JSON_IN_CMT', 5);
* Behavior switch for Services_JSON::decode()
define('SERVICES_JSON_LOOSE_TYPE', 16);
* Behavior switch for Services_JSON::decode()
define('SERVICES_JSON_SUPPRESS_ERRORS', 32);
* Behavior switch for Services_JSON::decode()
define('SERVICES_JSON_USE_TO_JSON', 64);
* Converts to and from JSON format.
* // create a new instance of Services_JSON
* $json = new Services_JSON();
* // convert a complex value to JSON notation, and send it to the browser
* $value = array('foo', 'bar', array(1, 2, 'baz'), array(3, array(4)));
* $output = $json->encode($value);
* // prints: ["foo","bar",[1,2,"baz"],[3,[4]]]
* // accept incoming POST data, assumed to be in JSON notation
* $input = file_get_contents('php://input', 1000000);
* $value = $json->decode($input);
* constructs a new JSON instance
* @deprecated 5.3.0 Use the PHP native JSON extension instead.
* @param int $use object behavior flags; combine with boolean-OR
* - SERVICES_JSON_LOOSE_TYPE: loose typing.
* "{...}" syntax creates associative arrays
* instead of objects in decode().
* - SERVICES_JSON_SUPPRESS_ERRORS: error suppression.
* Values which can't be encoded (e.g. resources)
* appear as NULL instead of throwing errors.
* By default, a deeply-nested resource will
* bubble up with an error, so all return values
* from encode() should be checked with isError()
* - SERVICES_JSON_USE_TO_JSON: call toJSON when serializing objects
* It serializes the return value from the toJSON call rather
* than the object itself, toJSON can return associative arrays,
* strings or numbers, if you return an object, make sure it does
* not have a toJSON method, otherwise an error will occur.
function __construct( $use = 0 )
_deprecated_function( __METHOD__, '5.3.0', 'The PHP native JSON extension' );
$this->_mb_strlen = function_exists('mb_strlen');
$this->_mb_convert_encoding = function_exists('mb_convert_encoding');
$this->_mb_substr = function_exists('mb_substr');
* @deprecated 5.3.0 Use __construct() instead.
* @see Services_JSON::__construct()
public function Services_JSON( $use = 0 ) {
_deprecated_constructor( 'Services_JSON', '5.3.0', get_class( $this ) );
self::__construct( $use );
// private - cache the mbstring lookup results..
var $_mb_convert_encoding = false;
* convert a string from one UTF-16 char to one UTF-8 char
* Normally should be handled by mb_convert_encoding, but
* provides a slower PHP-only method for installations
* that lack the multibye string extension.
* @deprecated 5.3.0 Use the PHP native JSON extension instead.
* @param string $utf16 UTF-16 character
* @return string UTF-8 character
function utf162utf8($utf16)
_deprecated_function( __METHOD__, '5.3.0', 'The PHP native JSON extension' );
// oh please oh please oh please oh please oh please
if($this->_mb_convert_encoding) {
return mb_convert_encoding($utf16, 'UTF-8', 'UTF-16');
$bytes = (ord($utf16[0]) << 8) | ord($utf16[1]);
case ((0x7F & $bytes) == $bytes):
// this case should never be reached, because we are in ASCII range
// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
return chr(0x7F & $bytes);
case (0x07FF & $bytes) == $bytes:
// return a 2-byte UTF-8 character
// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
return chr(0xC0 | (($bytes >> 6) & 0x1F))
. chr(0x80 | ($bytes & 0x3F));
case (0xFFFF & $bytes) == $bytes:
// return a 3-byte UTF-8 character
// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
return chr(0xE0 | (($bytes >> 12) & 0x0F))
. chr(0x80 | (($bytes >> 6) & 0x3F))
. chr(0x80 | ($bytes & 0x3F));
// ignoring UTF-32 for now, sorry
* convert a string from one UTF-8 char to one UTF-16 char
* Normally should be handled by mb_convert_encoding, but
* provides a slower PHP-only method for installations
* that lack the multibyte string extension.
* @deprecated 5.3.0 Use the PHP native JSON extension instead.
* @param string $utf8 UTF-8 character
* @return string UTF-16 character
function utf82utf16($utf8)
_deprecated_function( __METHOD__, '5.3.0', 'The PHP native JSON extension' );
// oh please oh please oh please oh please oh please
if($this->_mb_convert_encoding) {
return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8');
switch($this->strlen8($utf8)) {
// this case should never be reached, because we are in ASCII range
// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
// return a UTF-16 character from a 2-byte UTF-8 char
// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
return chr(0x07 & (ord($utf8[0]) >> 2))
. chr((0xC0 & (ord($utf8[0]) << 6))
| (0x3F & ord($utf8[1])));
// return a UTF-16 character from a 3-byte UTF-8 char
// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
return chr((0xF0 & (ord($utf8[0]) << 4))
| (0x0F & (ord($utf8[1]) >> 2)))
. chr((0xC0 & (ord($utf8[1]) << 6))
| (0x7F & ord($utf8[2])));
// ignoring UTF-32 for now, sorry
* encodes an arbitrary variable into JSON format (and sends JSON Header)
* @deprecated 5.3.0 Use the PHP native JSON extension instead.
* @param mixed $var any number, boolean, string, array, or object to be encoded.
* see argument 1 to Services_JSON() above for array-parsing behavior.
* if var is a string, note that encode() always expects it
* to be in ASCII or UTF-8 format!
* @return mixed JSON string representation of input var or an error if a problem occurs
_deprecated_function( __METHOD__, '5.3.0', 'The PHP native JSON extension' );
header('Content-type: application/json');
return $this->encodeUnsafe($var);
* encodes an arbitrary variable into JSON format without JSON Header - warning - may allow XSS!!!!)
* @deprecated 5.3.0 Use the PHP native JSON extension instead.
* @param mixed $var any number, boolean, string, array, or object to be encoded.
* see argument 1 to Services_JSON() above for array-parsing behavior.
* if var is a string, note that encode() always expects it
* to be in ASCII or UTF-8 format!
* @return mixed JSON string representation of input var or an error if a problem occurs
function encodeUnsafe($var)
_deprecated_function( __METHOD__, '5.3.0', 'The PHP native JSON extension' );
// see bug #16908 - regarding numeric locale printing
$lc = setlocale(LC_NUMERIC, 0);
setlocale(LC_NUMERIC, 'C');
$ret = $this->_encode($var);
setlocale(LC_NUMERIC, $lc);
* PRIVATE CODE that does the work of encodes an arbitrary variable into JSON format
* @deprecated 5.3.0 Use the PHP native JSON extension instead.
* @param mixed $var any number, boolean, string, array, or object to be encoded.
* see argument 1 to Services_JSON() above for array-parsing behavior.
* if var is a string, note that encode() always expects it
* to be in ASCII or UTF-8 format!
* @return mixed JSON string representation of input var or an error if a problem occurs
_deprecated_function( __METHOD__, '5.3.0', 'The PHP native JSON extension' );
return $var ? 'true' : 'false';
// STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT
$strlen_var = $this->strlen8($var);
* Iterate over every character in the string,
* escaping with a slash or encoding to UTF-8 where necessary
for ($c = 0; $c < $strlen_var; ++$c) {
$ord_var_c = ord($var[$c]);
// double quote, slash, slosh
case (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)):
// characters U-00000000 - U-0000007F (same as ASCII)
case (($ord_var_c & 0xE0) == 0xC0):
// characters U-00000080 - U-000007FF, mask 110XXXXX
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
if ($c+1 >= $strlen_var) {
$char = pack('C*', $ord_var_c, ord($var[$c + 1]));
$utf16 = $this->utf82utf16($char);
$ascii .= sprintf('\u%04s', bin2hex($utf16));
case (($ord_var_c & 0xF0) == 0xE0):
if ($c+2 >= $strlen_var) {
// characters U-00000800 - U-0000FFFF, mask 1110XXXX
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
$char = pack('C*', $ord_var_c,
$utf16 = $this->utf82utf16($char);
$ascii .= sprintf('\u%04s', bin2hex($utf16));
case (($ord_var_c & 0xF8) == 0xF0):
if ($c+3 >= $strlen_var) {
// characters U-00010000 - U-001FFFFF, mask 11110XXX
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
$char = pack('C*', $ord_var_c,
$utf16 = $this->utf82utf16($char);
$ascii .= sprintf('\u%04s', bin2hex($utf16));
case (($ord_var_c & 0xFC) == 0xF8):
// characters U-00200000 - U-03FFFFFF, mask 111110XX
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
if ($c+4 >= $strlen_var) {
$char = pack('C*', $ord_var_c,
$utf16 = $this->utf82utf16($char);
$ascii .= sprintf('\u%04s', bin2hex($utf16));
case (($ord_var_c & 0xFE) == 0xFC):
if ($c+5 >= $strlen_var) {
// characters U-04000000 - U-7FFFFFFF, mask 1111110X
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
$char = pack('C*', $ord_var_c,
$utf16 = $this->utf82utf16($char);
$ascii .= sprintf('\u%04s', bin2hex($utf16));
* As per JSON spec if any array key is not an integer
* we must treat the whole array as an object. We
* also try to catch a sparsely populated associative
* array with numeric keys here because some JS engines
* will create an array with empty indexes up to
* max_index which can cause memory issues and because
* the keys, which may be relevant, will be remapped
* As per the ECMA and JSON specification an object may
* have any string as a property. Unfortunately due to
* a hole in the ECMA specification if the key is a
* ECMA reserved word or starts with a digit the
* parameter is only accessible using ECMAScript's
// treat as a JSON object
if (is_array($var) && count($var) && (array_keys($var) !== range(0, sizeof($var) - 1))) {
$properties = array_map(array($this, 'name_value'),
foreach($properties as $property) {