iTx Technologies offre gratuitement
cet espace pour SugarCRM !

title

Body

[fermer]

/include/ -> JSON.php (source)

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


Generé en: Thu Mar 4 09:44:50 2010 | Cross-referenced par PHPXref 0.7