Add analytics
[bus.git] / busui / owa / includes / JSON.php
blob:a/busui/owa/includes/JSON.php -> blob:b/busui/owa/includes/JSON.php
  <?php
  /* 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
  * overhead
  *
  * 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
  * distribution.
  *
  * 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
  * DAMAGE.
  *
  * @category
  * @package Services_JSON
  * @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 288200 2009-09-09 15:41:29Z 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);
   
  /**
  * Converts to and from JSON format.
  *
  * Brief example of use:
  *
  * <code>
  * // create a new instance of Services_JSON
  * $json = new Services_JSON();
  *
  * // convert a complexe 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);
  *
  * print($output);
  * // 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);
  * </code>
  */
  class Services_JSON
  {
  /**
  * constructs a new JSON instance
  *
  * @param int $use object behavior flags; combine with boolean-OR
  *
  * possible values:
  * - 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()
  */
  function Services_JSON($use = 0)
  {
  $this->use = $use;
  }
   
  /**
  * 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.
  *
  * @param string $utf16 UTF-16 character
  * @return string UTF-8 character
  * @access private
  */
  function utf162utf8($utf16)
  {
  // oh please oh please oh please oh please oh please
  if(function_exists('mb_convert_encoding')) {
  return mb_convert_encoding($utf16, 'UTF-8', 'UTF-16');
  }
   
  $bytes = (ord($utf16{0}) << 8) | ord($utf16{1});
   
  switch(true) {
  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
  return '';
  }
   
  /**
  * 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 multibye string extension.
  *
  * @param string $utf8 UTF-8 character
  * @return string UTF-16 character
  * @access private
  */
  function utf82utf16($utf8)
  {
  // oh please oh please oh please oh please oh please
  if(function_exists('mb_convert_encoding')) {
  return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8');
  }
   
  switch(strlen($utf8)) {
  case 1:
  // 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 $utf8;
   
  case 2:
  // 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})));
   
  case 3:
  // 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
  return '';
  }
   
  /**
  * encodes an arbitrary variable into JSON format (and sends JSON Header)
  *
  * @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 strng, 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
  * @access public
  */
  function encode($var)
  {
  header('Content-type: application/json');
  return $this->_encode($var);
  }
  /**
  * encodes an arbitrary variable into JSON format without JSON Header - warning - may allow CSS!!!!)
  *
  * @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 strng, 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
  * @access public
  */
  function encodeUnsafe($var)
  {
  return $this->_encode($var);
  }
  /**
  * PRIVATE CODE that does the work of encodes an arbitrary variable into JSON format
  *
  * @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 strng, 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
  * @access public
  */
  function _encode($var)
  {
   
  switch (gettype($var)) {
  case 'boolean':
  return $var ? 'true' : 'false';
   
  case 'NULL':
  return 'null';
   
  case 'integer':
  return (int) $var;
   
  case 'double':
  case 'float':
  return (float) $var;
   
  case 'string':
  // STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT
  $ascii = '';
  $strlen_var = strlen($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});
   
  switch (true) {
  case $ord_var_c == 0x08:
  $ascii .= '\b';
  break;
  case $ord_var_c == 0x09:
  $ascii .= '\t';
  break;
  case $ord_var_c == 0x0A:
  $ascii .= '\n';
  break;
  case $ord_var_c == 0x0C:
  $ascii .= '\f';
  break;
  case $ord_var_c == 0x0D:
  $ascii .= '\r';
  break;
   
  case $ord_var_c == 0x22:
  case $ord_var_c == 0x2F:
  case $ord_var_c == 0x5C:
  // double quote, slash, slosh
  $ascii .= '\\'.$var{$c};
  break;
   
  case (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)):
  // characters U-00000000 - U-0000007F (same as ASCII)
  $ascii .= $var{$c};
  break;
   
  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) {
  $c += 1;
  $ascii .= '?';
  break;
  }
   
  $char = pack('C*', $ord_var_c, ord($var{$c + 1}));
  $c += 1;
  $utf16 = $this->utf82utf16($char);
  $ascii .= sprintf('\u%04s', bin2hex($utf16));
  break;
   
  case (($ord_var_c & 0xF0) == 0xE0):
  if ($c+2 >= $strlen_var) {
  $c += 2;
  $ascii .= '?';
  break;
  }
  // 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,
  @ord($var{$c + 1}),
  @ord($var{$c + 2}));
  $c += 2;
  $utf16 = $this->utf82utf16($char);
  $ascii .= sprintf('\u%04s', bin2hex($utf16));
  break;
   
  case (($ord_var_c & 0xF8) == 0xF0):
  if ($c+3 >= $strlen_var) {
  $c += 3;
  $ascii .= '?';