linea21-externals
[ class tree: linea21-externals ] [ index: linea21-externals ] [ all elements ]

Source for file JSON.php

Documentation is available at JSON.php

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

Documentation generated on Fri, 01 Apr 2011 09:31:47 +0200 by phpDocumentor 1.4.1