includes/json2.php
changeset 334 c72b545f1304
child 880 218b6d4de908
equal deleted inserted replaced
333:32429702305e 334:c72b545f1304
       
     1 <?php
       
     2 /**
       
     3  * Zend Framework
       
     4  *
       
     5  * LICENSE
       
     6  *
       
     7  * This source file is subject to the new BSD license that is bundled
       
     8  * with this package in the file LICENSE.txt.
       
     9  * It is also available through the world-wide-web at this URL:
       
    10  * http://framework.zend.com/license/new-bsd
       
    11  * If you did not receive a copy of the license and are unable to
       
    12  * obtain it through the world-wide-web, please send an email
       
    13  * to license@zend.com so we can send you a copy immediately.
       
    14  *
       
    15  * @category   Zend
       
    16  * @package    Zend_Json
       
    17  * @copyright  Copyright (c) 2005-2007 Zend Technologies USA Inc. (http://www.zend.com)
       
    18  * @license    http://framework.zend.com/license/new-bsd     New BSD License
       
    19  */
       
    20 
       
    21 
       
    22 /**
       
    23  * Encode PHP constructs to JSON
       
    24  *
       
    25  * @category   Zend
       
    26  * @package    Zend_Json
       
    27  * @copyright  Copyright (c) 2005-2007 Zend Technologies USA Inc. (http://www.zend.com)
       
    28  * @license    http://framework.zend.com/license/new-bsd     New BSD License
       
    29  */
       
    30 class Zend_Json_Encoder
       
    31 {
       
    32     /**
       
    33      * Whether or not to check for possible cycling
       
    34      *
       
    35      * @var boolean
       
    36      */
       
    37     protected $_cycleCheck;
       
    38 
       
    39     /**
       
    40      * Array of visited objects; used to prevent cycling.
       
    41      *
       
    42      * @var array
       
    43      */
       
    44     protected $_visited = array();
       
    45 
       
    46     /**
       
    47      * Constructor
       
    48      *
       
    49      * @param boolean $cycleCheck Whether or not to check for recursion when encoding
       
    50      * @return void
       
    51      */
       
    52     protected function __construct($cycleCheck = false)
       
    53     {
       
    54         $this->_cycleCheck = $cycleCheck;
       
    55     }
       
    56 
       
    57     /**
       
    58      * Use the JSON encoding scheme for the value specified
       
    59      *
       
    60      * @param mixed $value The value to be encoded
       
    61      * @param boolean $cycleCheck Whether or not to check for possible object recursion when encoding
       
    62      * @return string  The encoded value
       
    63      */
       
    64     public static function encode($value, $cycleCheck = false)
       
    65     {
       
    66         $encoder = new Zend_Json_Encoder(($cycleCheck) ? true : false);
       
    67 
       
    68         return $encoder->_encodeValue($value);
       
    69     }
       
    70 
       
    71     /**
       
    72      * Recursive driver which determines the type of value to be encoded
       
    73      * and then dispatches to the appropriate method. $values are either
       
    74      *    - objects (returns from {@link _encodeObject()})
       
    75      *    - arrays (returns from {@link _encodeArray()})
       
    76      *    - basic datums (e.g. numbers or strings) (returns from {@link _encodeDatum()})
       
    77      *
       
    78      * @param $value mixed The value to be encoded
       
    79      * @return string Encoded value
       
    80      */
       
    81     protected function _encodeValue(&$value)
       
    82     {
       
    83         if (is_object($value)) {
       
    84             return $this->_encodeObject($value);
       
    85         } else if (is_array($value)) {
       
    86             return $this->_encodeArray($value);
       
    87         }
       
    88 
       
    89         return $this->_encodeDatum($value);
       
    90     }
       
    91 
       
    92 
       
    93 
       
    94     /**
       
    95      * Encode an object to JSON by encoding each of the public properties
       
    96      *
       
    97      * A special property is added to the JSON object called '__className'
       
    98      * that contains the name of the class of $value. This is used to decode
       
    99      * the object on the client into a specific class.
       
   100      *
       
   101      * @param $value object
       
   102      * @return string
       
   103      * @throws Zend_Json_Exception If recursive checks are enabled and the object has been serialized previously
       
   104      */
       
   105     protected function _encodeObject(&$value)
       
   106     {
       
   107         if ($this->_cycleCheck) {
       
   108             if ($this->_wasVisited($value)) {
       
   109                 throw new Zend_Json_Exception(
       
   110                     'Cycles not supported in JSON encoding, cycle introduced by '
       
   111                     . 'class "' . get_class($value) . '"'
       
   112                 );
       
   113             }
       
   114 
       
   115             $this->_visited[] = $value;
       
   116         }
       
   117 
       
   118         $props = '';
       
   119         foreach (get_object_vars($value) as $name => $propValue) {
       
   120             if (isset($propValue)) {
       
   121                 $props .= ','
       
   122                         . $this->_encodeValue($name)
       
   123                         . ':'
       
   124                         . $this->_encodeValue($propValue);
       
   125             }
       
   126         }
       
   127 
       
   128         return '{"__className":"' . get_class($value) . '"'
       
   129                 . $props . '}';
       
   130     }
       
   131 
       
   132 
       
   133     /**
       
   134      * Determine if an object has been serialized already
       
   135      *
       
   136      * @param mixed $value
       
   137      * @return boolean
       
   138      */
       
   139     protected function _wasVisited(&$value)
       
   140     {
       
   141         if (in_array($value, $this->_visited, true)) {
       
   142             return true;
       
   143         }
       
   144 
       
   145         return false;
       
   146     }
       
   147 
       
   148 
       
   149     /**
       
   150      * JSON encode an array value
       
   151      *
       
   152      * Recursively encodes each value of an array and returns a JSON encoded
       
   153      * array string.
       
   154      *
       
   155      * Arrays are defined as integer-indexed arrays starting at index 0, where
       
   156      * the last index is (count($array) -1); any deviation from that is
       
   157      * considered an associative array, and will be encoded as such.
       
   158      *
       
   159      * @param $array array
       
   160      * @return string
       
   161      */
       
   162     protected function _encodeArray(&$array)
       
   163     {
       
   164         $tmpArray = array();
       
   165 
       
   166         // Check for associative array
       
   167         if (!empty($array) && (array_keys($array) !== range(0, count($array) - 1))) {
       
   168             // Associative array
       
   169             $result = '{';
       
   170             foreach ($array as $key => $value) {
       
   171                 $key = (string) $key;
       
   172                 $tmpArray[] = $this->_encodeString($key)
       
   173                             . ':'
       
   174                             . $this->_encodeValue($value);
       
   175             }
       
   176             $result .= implode(',', $tmpArray);
       
   177             $result .= '}';
       
   178         } else {
       
   179             // Indexed array
       
   180             $result = '[';
       
   181             $length = count($array);
       
   182             for ($i = 0; $i < $length; $i++) {
       
   183                 $tmpArray[] = $this->_encodeValue($array[$i]);
       
   184             }
       
   185             $result .= implode(',', $tmpArray);
       
   186             $result .= ']';
       
   187         }
       
   188 
       
   189         return $result;
       
   190     }
       
   191 
       
   192 
       
   193     /**
       
   194      * JSON encode a basic data type (string, number, boolean, null)
       
   195      *
       
   196      * If value type is not a string, number, boolean, or null, the string
       
   197      * 'null' is returned.
       
   198      *
       
   199      * @param $value mixed
       
   200      * @return string
       
   201      */
       
   202     protected function _encodeDatum(&$value)
       
   203     {
       
   204         $result = 'null';
       
   205 
       
   206         if (is_int($value) || is_float($value)) {
       
   207             $result = (string)$value;
       
   208         } elseif (is_string($value)) {
       
   209             $result = $this->_encodeString($value);
       
   210         } elseif (is_bool($value)) {
       
   211             $result = $value ? 'true' : 'false';
       
   212         }
       
   213 
       
   214         return $result;
       
   215     }
       
   216 
       
   217 
       
   218     /**
       
   219      * JSON encode a string value by escaping characters as necessary
       
   220      *
       
   221      * @param $value string
       
   222      * @return string
       
   223      */
       
   224     protected function _encodeString(&$string)
       
   225     {
       
   226         // Escape these characters with a backslash:
       
   227         // " \ / \n \r \t \b \f
       
   228         $search  = array('\\', "\n", "\t", "\r", "\b", "\f", '"');
       
   229         $replace = array('\\\\', '\\n', '\\t', '\\r', '\\b', '\\f', '\"');
       
   230         $string  = str_replace($search, $replace, $string);
       
   231 
       
   232         // Escape certain ASCII characters:
       
   233         // 0x08 => \b
       
   234         // 0x0c => \f
       
   235         $string = str_replace(array(chr(0x08), chr(0x0C)), array('\b', '\f'), $string);
       
   236 
       
   237         return '"' . $string . '"';
       
   238     }
       
   239 
       
   240 
       
   241     /**
       
   242      * Encode the constants associated with the ReflectionClass
       
   243      * parameter. The encoding format is based on the class2 format
       
   244      *
       
   245      * @param $cls ReflectionClass
       
   246      * @return string Encoded constant block in class2 format
       
   247      */
       
   248     private static function _encodeConstants(ReflectionClass $cls)
       
   249     {
       
   250         $result    = "constants : {";
       
   251         $constants = $cls->getConstants();
       
   252 
       
   253         $tmpArray = array();
       
   254         if (!empty($constants)) {
       
   255             foreach ($constants as $key => $value) {
       
   256                 $tmpArray[] = "$key: " . self::encode($value);
       
   257             }
       
   258 
       
   259             $result .= implode(', ', $tmpArray);
       
   260         }
       
   261 
       
   262         return $result . "}";
       
   263     }
       
   264 
       
   265 
       
   266     /**
       
   267      * Encode the public methods of the ReflectionClass in the
       
   268      * class2 format
       
   269      *
       
   270      * @param $cls ReflectionClass
       
   271      * @return string Encoded method fragment
       
   272      *
       
   273      */
       
   274     private static function _encodeMethods(ReflectionClass $cls)
       
   275     {
       
   276         $methods = $cls->getMethods();
       
   277         $result = 'methods:{';
       
   278 
       
   279         $started = false;
       
   280         foreach ($methods as $method) {
       
   281             if (! $method->isPublic() || !$method->isUserDefined()) {
       
   282                 continue;
       
   283             }
       
   284 
       
   285             if ($started) {
       
   286                 $result .= ',';
       
   287             }
       
   288             $started = true;
       
   289 
       
   290             $result .= '' . $method->getName(). ':function(';
       
   291 
       
   292             if ('__construct' != $method->getName()) {
       
   293                 $parameters  = $method->getParameters();
       
   294                 $paramCount  = count($parameters);
       
   295                 $argsStarted = false;
       
   296 
       
   297                 $argNames = "var argNames=[";
       
   298                 foreach ($parameters as $param) {
       
   299                     if ($argsStarted) {
       
   300                         $result .= ',';
       
   301                     }
       
   302 
       
   303                     $result .= $param->getName();
       
   304 
       
   305                     if ($argsStarted) {
       
   306                         $argNames .= ',';
       
   307                     }
       
   308 
       
   309                     $argNames .= '"' . $param->getName() . '"';
       
   310 
       
   311                     $argsStarted = true;
       
   312                 }
       
   313                 $argNames .= "];";
       
   314 
       
   315                 $result .= "){"
       
   316                          . $argNames
       
   317                          . 'var result = ZAjaxEngine.invokeRemoteMethod('
       
   318                          . "this, '" . $method->getName()
       
   319                          . "',argNames,arguments);"
       
   320                          . 'return(result);}';
       
   321             } else {
       
   322                 $result .= "){}";
       
   323             }
       
   324         }
       
   325 
       
   326         return $result . "}";
       
   327     }
       
   328 
       
   329 
       
   330     /**
       
   331      * Encode the public properties of the ReflectionClass in the class2
       
   332      * format.
       
   333      *
       
   334      * @param $cls ReflectionClass
       
   335      * @return string Encode properties list
       
   336      *
       
   337      */
       
   338     private static function _encodeVariables(ReflectionClass $cls)
       
   339     {
       
   340         $properties = $cls->getProperties();
       
   341         $propValues = get_class_vars($cls->getName());
       
   342         $result = "variables:{";
       
   343         $cnt = 0;
       
   344 
       
   345         $tmpArray = array();
       
   346         foreach ($properties as $prop) {
       
   347             if (! $prop->isPublic()) {
       
   348                 continue;
       
   349             }
       
   350 
       
   351             $tmpArray[] = $prop->getName()
       
   352                         . ':'
       
   353                         . self::encode($propValues[$prop->getName()]);
       
   354         }
       
   355         $result .= implode(',', $tmpArray);
       
   356 
       
   357         return $result . "}";
       
   358     }
       
   359 
       
   360     /**
       
   361      * Encodes the given $className into the class2 model of encoding PHP
       
   362      * classes into JavaScript class2 classes.
       
   363      * NOTE: Currently only public methods and variables are proxied onto
       
   364      * the client machine
       
   365      *
       
   366      * @param $className string The name of the class, the class must be
       
   367      * instantiable using a null constructor
       
   368      * @param $package string Optional package name appended to JavaScript
       
   369      * proxy class name
       
   370      * @return string The class2 (JavaScript) encoding of the class
       
   371      * @throws Zend_Json_Exception
       
   372      */
       
   373     public static function encodeClass($className, $package = '')
       
   374     {
       
   375         $cls = new ReflectionClass($className);
       
   376         if (! $cls->isInstantiable()) {
       
   377             throw new Zend_Json_Exception("$className must be instantiable");
       
   378         }
       
   379 
       
   380         return "Class.create('$package$className',{"
       
   381                 . self::_encodeConstants($cls)    .","
       
   382                 . self::_encodeMethods($cls)      .","
       
   383                 . self::_encodeVariables($cls)    .'});';
       
   384     }
       
   385 
       
   386 
       
   387     /**
       
   388      * Encode several classes at once
       
   389      *
       
   390      * Returns JSON encoded classes, using {@link encodeClass()}.
       
   391      *
       
   392      * @param array $classNames
       
   393      * @param string $package
       
   394      * @return string
       
   395      */
       
   396     public static function encodeClasses(array $classNames, $package = '')
       
   397     {
       
   398         $result = '';
       
   399         foreach ($classNames as $className) {
       
   400             $result .= self::encodeClass($className, $package);
       
   401         }
       
   402 
       
   403         return $result;
       
   404     }
       
   405 
       
   406 }
       
   407 
       
   408 /**
       
   409  * Decode JSON encoded string to PHP variable constructs
       
   410  *
       
   411  * @category   Zend
       
   412  * @package    Zend_Json
       
   413  * @copyright  Copyright (c) 2005-2007 Zend Technologies USA Inc. (http://www.zend.com)
       
   414  * @license    http://framework.zend.com/license/new-bsd     New BSD License
       
   415  */
       
   416 class Zend_Json_Decoder
       
   417 {
       
   418     /**
       
   419      * Parse tokens used to decode the JSON object. These are not
       
   420      * for public consumption, they are just used internally to the
       
   421      * class.
       
   422      */
       
   423     const EOF          = 0;
       
   424     const DATUM        = 1;
       
   425     const LBRACE       = 2;
       
   426     const LBRACKET     = 3;
       
   427     const RBRACE       = 4;
       
   428     const RBRACKET     = 5;
       
   429     const COMMA        = 6;
       
   430     const COLON        = 7;
       
   431 
       
   432     /**
       
   433      * Use to maintain a "pointer" to the source being decoded
       
   434      *
       
   435      * @var string
       
   436      */
       
   437     protected $_source;
       
   438 
       
   439     /**
       
   440      * Caches the source length
       
   441      *
       
   442      * @var int
       
   443      */
       
   444     protected $_sourceLength;
       
   445 
       
   446     /**
       
   447      * The offset within the souce being decoded
       
   448      *
       
   449      * @var int
       
   450      *
       
   451      */
       
   452     protected $_offset;
       
   453 
       
   454     /**
       
   455      * The current token being considered in the parser cycle
       
   456      *
       
   457      * @var int
       
   458      */
       
   459     protected $_token;
       
   460 
       
   461     /**
       
   462      * Flag indicating how objects should be decoded
       
   463      *
       
   464      * @var int
       
   465      * @access protected
       
   466      */
       
   467     protected $_decodeType;
       
   468 
       
   469     /**
       
   470      * Constructor
       
   471      *
       
   472      * @param string $source String source to decode
       
   473      * @param int $decodeType How objects should be decoded -- see
       
   474      * {@link Zend_Json::TYPE_ARRAY} and {@link Zend_Json::TYPE_OBJECT} for
       
   475      * valid values
       
   476      * @return void
       
   477      */
       
   478     protected function __construct($source, $decodeType)
       
   479     {
       
   480         
       
   481         // eliminate comments
       
   482         $source = preg_replace(array(
       
   483 
       
   484                   // eliminate single line comments in '// ...' form
       
   485                   '#^\s*//(.+)$#m',
       
   486     
       
   487                   // eliminate multi-line comments in '/* ... */' form, at start of string
       
   488                   '#^\s*/\*(.+)\*/#Us',
       
   489     
       
   490                   // eliminate multi-line comments in '/* ... */' form, at end of string
       
   491                   '#/\*(.+)\*/\s*$#Us'
       
   492     
       
   493               ), '', $source);
       
   494         
       
   495         // Set defaults
       
   496         $this->_source       = $source;
       
   497         $this->_sourceLength = strlen($source);
       
   498         $this->_token        = self::EOF;
       
   499         $this->_offset       = 0;
       
   500 
       
   501         // Normalize and set $decodeType
       
   502         if (!in_array($decodeType, array(Zend_Json::TYPE_ARRAY, Zend_Json::TYPE_OBJECT)))
       
   503         {
       
   504             $decodeType = Zend_Json::TYPE_ARRAY;
       
   505         }
       
   506         $this->_decodeType   = $decodeType;
       
   507 
       
   508         // Set pointer at first token
       
   509         $this->_getNextToken();
       
   510     }
       
   511 
       
   512     /**
       
   513      * Decode a JSON source string
       
   514      *
       
   515      * Decodes a JSON encoded string. The value returned will be one of the
       
   516      * following:
       
   517      *        - integer
       
   518      *        - float
       
   519      *        - boolean
       
   520      *        - null
       
   521      *      - StdClass
       
   522      *      - array
       
   523      *         - array of one or more of the above types
       
   524      *
       
   525      * By default, decoded objects will be returned as associative arrays; to
       
   526      * return a StdClass object instead, pass {@link Zend_Json::TYPE_OBJECT} to
       
   527      * the $objectDecodeType parameter.
       
   528      *
       
   529      * Throws a Zend_Json_Exception if the source string is null.
       
   530      *
       
   531      * @static
       
   532      * @access public
       
   533      * @param string $source String to be decoded
       
   534      * @param int $objectDecodeType How objects should be decoded; should be
       
   535      * either or {@link Zend_Json::TYPE_ARRAY} or
       
   536      * {@link Zend_Json::TYPE_OBJECT}; defaults to TYPE_ARRAY
       
   537      * @return mixed
       
   538      * @throws Zend_Json_Exception
       
   539      */
       
   540     public static function decode($source = null, $objectDecodeType = Zend_Json::TYPE_ARRAY)
       
   541     {
       
   542         if (null === $source) {
       
   543             throw new Zend_Json_Exception('Must specify JSON encoded source for decoding');
       
   544         } elseif (!is_string($source)) {
       
   545             throw new Zend_Json_Exception('Can only decode JSON encoded strings');
       
   546         }
       
   547 
       
   548         $decoder = new self($source, $objectDecodeType);
       
   549 
       
   550         return $decoder->_decodeValue();
       
   551     }
       
   552 
       
   553 
       
   554     /**
       
   555      * Recursive driving rountine for supported toplevel tops
       
   556      *
       
   557      * @return mixed
       
   558      */
       
   559     protected function _decodeValue()
       
   560     {
       
   561         switch ($this->_token) {
       
   562             case self::DATUM:
       
   563                 $result  = $this->_tokenValue;
       
   564                 $this->_getNextToken();
       
   565                 return($result);
       
   566                 break;
       
   567             case self::LBRACE:
       
   568                 return($this->_decodeObject());
       
   569                 break;
       
   570             case self::LBRACKET:
       
   571                 return($this->_decodeArray());
       
   572                 break;
       
   573             default:
       
   574                 return null;
       
   575                 break;
       
   576         }
       
   577     }
       
   578 
       
   579     /**
       
   580      * Decodes an object of the form:
       
   581      *  { "attribute: value, "attribute2" : value,...}
       
   582      *
       
   583      * If ZJsonEnoder or ZJAjax was used to encode the original object
       
   584      * then a special attribute called __className which specifies a class
       
   585      * name that should wrap the data contained within the encoded source.
       
   586      *
       
   587      * Decodes to either an array or StdClass object, based on the value of
       
   588      * {@link $_decodeType}. If invalid $_decodeType present, returns as an
       
   589      * array.
       
   590      *
       
   591      * @return array|StdClass
       
   592      */
       
   593     protected function _decodeObject()
       
   594     {
       
   595         $members = array();
       
   596         $tok = $this->_getNextToken();
       
   597 
       
   598         while ($tok && $tok != self::RBRACE) {
       
   599             if ($tok != self::DATUM || ! is_string($this->_tokenValue)) {
       
   600                 throw new Zend_Json_Exception('Missing key in object encoding: ' . $this->_source);
       
   601             }
       
   602 
       
   603             $key = $this->_tokenValue;
       
   604             $tok = $this->_getNextToken();
       
   605 
       
   606             if ($tok != self::COLON) {
       
   607                 throw new Zend_Json_Exception('Missing ":" in object encoding: ' . $this->_source);
       
   608             }
       
   609 
       
   610             $tok = $this->_getNextToken();
       
   611             $members[$key] = $this->_decodeValue();
       
   612             $tok = $this->_token;
       
   613 
       
   614             if ($tok == self::RBRACE) {
       
   615                 break;
       
   616             }
       
   617 
       
   618             if ($tok != self::COMMA) {
       
   619                 throw new Zend_Json_Exception('Missing "," in object encoding: ' . $this->_source);
       
   620             }
       
   621 
       
   622             $tok = $this->_getNextToken();
       
   623         }
       
   624 
       
   625         switch ($this->_decodeType) {
       
   626             case Zend_Json::TYPE_OBJECT:
       
   627                 // Create new StdClass and populate with $members
       
   628                 $result = new StdClass();
       
   629                 foreach ($members as $key => $value) {
       
   630                     $result->$key = $value;
       
   631                 }
       
   632                 break;
       
   633             case Zend_Json::TYPE_ARRAY:
       
   634             default:
       
   635                 $result = $members;
       
   636                 break;
       
   637         }
       
   638 
       
   639         $this->_getNextToken();
       
   640         return $result;
       
   641     }
       
   642 
       
   643     /**
       
   644      * Decodes a JSON array format:
       
   645      *    [element, element2,...,elementN]
       
   646      *
       
   647      * @return array
       
   648      */
       
   649     protected function _decodeArray()
       
   650     {
       
   651         $result = array();
       
   652         $starttok = $tok = $this->_getNextToken(); // Move past the '['
       
   653         $index  = 0;
       
   654 
       
   655         while ($tok && $tok != self::RBRACKET) {
       
   656             $result[$index++] = $this->_decodeValue();
       
   657 
       
   658             $tok = $this->_token;
       
   659 
       
   660             if ($tok == self::RBRACKET || !$tok) {
       
   661                 break;
       
   662             }
       
   663 
       
   664             if ($tok != self::COMMA) {
       
   665                 throw new Zend_Json_Exception('Missing "," in array encoding: ' . $this->_source);
       
   666             }
       
   667 
       
   668             $tok = $this->_getNextToken();
       
   669         }
       
   670 
       
   671         $this->_getNextToken();
       
   672         return($result);
       
   673     }
       
   674 
       
   675 
       
   676     /**
       
   677      * Removes whitepsace characters from the source input
       
   678      */
       
   679     protected function _eatWhitespace()
       
   680     {
       
   681         if (preg_match(
       
   682                 '/([\t\b\f\n\r ])*/s',
       
   683                 $this->_source,
       
   684                 $matches,
       
   685                 PREG_OFFSET_CAPTURE,
       
   686                 $this->_offset)
       
   687             && $matches[0][1] == $this->_offset)
       
   688         {
       
   689             $this->_offset += strlen($matches[0][0]);
       
   690         }
       
   691     }
       
   692 
       
   693 
       
   694     /**
       
   695      * Retrieves the next token from the source stream
       
   696      *
       
   697      * @return int Token constant value specified in class definition
       
   698      */
       
   699     protected function _getNextToken()
       
   700     {
       
   701         $this->_token      = self::EOF;
       
   702         $this->_tokenValue = null;
       
   703         $this->_eatWhitespace();
       
   704         
       
   705         if ($this->_offset >= $this->_sourceLength) {
       
   706             return(self::EOF);
       
   707         }
       
   708 
       
   709         $str        = $this->_source;
       
   710         $str_length = $this->_sourceLength;
       
   711         $i          = $this->_offset;
       
   712         $start      = $i;
       
   713         
       
   714         switch ($str{$i}) {
       
   715             case '{':
       
   716                $this->_token = self::LBRACE;
       
   717                break;
       
   718             case '}':
       
   719                 $this->_token = self::RBRACE;
       
   720                 break;
       
   721             case '[':
       
   722                 $this->_token = self::LBRACKET;
       
   723                 break;
       
   724             case ']':
       
   725                 $this->_token = self::RBRACKET;
       
   726                 break;
       
   727             case ',':
       
   728                 $this->_token = self::COMMA;
       
   729                 break;
       
   730             case ':':
       
   731                 $this->_token = self::COLON;
       
   732                 break;
       
   733             case  '"':
       
   734                 $result = '';
       
   735                 do {
       
   736                     $i++;
       
   737                     if ($i >= $str_length) {
       
   738                         break;
       
   739                     }
       
   740 
       
   741                     $chr = $str{$i};
       
   742                     if ($chr == '\\') {
       
   743                         $i++;
       
   744                         if ($i >= $str_length) {
       
   745                             break;
       
   746                         }
       
   747                         $chr = $str{$i};
       
   748                         switch ($chr) {
       
   749                             case '"' :
       
   750                                 $result .= '"';
       
   751                                 break;
       
   752                             case '\\':
       
   753                                 $result .= '\\';
       
   754                                 break;
       
   755                             case '/' :
       
   756                                 $result .= '/';
       
   757                                 break;
       
   758                             case 'b' :
       
   759                                 $result .= chr(8);
       
   760                                 break;
       
   761                             case 'f' :
       
   762                                 $result .= chr(12);
       
   763                                 break;
       
   764                             case 'n' :
       
   765                                 $result .= chr(10);
       
   766                                 break;
       
   767                             case 'r' :
       
   768                                 $result .= chr(13);
       
   769                                 break;
       
   770                             case 't' :
       
   771                                 $result .= chr(9);
       
   772                                 break;
       
   773                             case '\'' :
       
   774                                 $result .= '\'';
       
   775                                 break;
       
   776                             default:
       
   777                                 throw new Zend_Json_Exception("Illegal escape "
       
   778                                     .  "sequence '" . $chr . "'");
       
   779                             }
       
   780                     } elseif ($chr == '"') {
       
   781                         break;
       
   782                     } else {
       
   783                         $result .= $chr;
       
   784                     }
       
   785                 } while ($i < $str_length);
       
   786 
       
   787                 $this->_token = self::DATUM;
       
   788                 //$this->_tokenValue = substr($str, $start + 1, $i - $start - 1);
       
   789                 $this->_tokenValue = $result;
       
   790                 break;
       
   791             case  "'":
       
   792                 $result = '';
       
   793                 do {
       
   794                     $i++;
       
   795                     if ($i >= $str_length) {
       
   796                         break;
       
   797                     }
       
   798 
       
   799                     $chr = $str{$i};
       
   800                     if ($chr == '\\') {
       
   801                         $i++;
       
   802                         if ($i >= $str_length) {
       
   803                             break;
       
   804                         }
       
   805                         $chr = $str{$i};
       
   806                         switch ($chr) {
       
   807                             case "'" :
       
   808                                 $result .= "'";
       
   809                                 break;
       
   810                             case '\\':
       
   811                                 $result .= '\\';
       
   812                                 break;
       
   813                             case '/' :
       
   814                                 $result .= '/';
       
   815                                 break;
       
   816                             case 'b' :
       
   817                                 $result .= chr(8);
       
   818                                 break;
       
   819                             case 'f' :
       
   820                                 $result .= chr(12);
       
   821                                 break;
       
   822                             case 'n' :
       
   823                                 $result .= chr(10);
       
   824                                 break;
       
   825                             case 'r' :
       
   826                                 $result .= chr(13);
       
   827                                 break;
       
   828                             case 't' :
       
   829                                 $result .= chr(9);
       
   830                                 break;
       
   831                             case '"' :
       
   832                                 $result .= '"';
       
   833                                 break;
       
   834                             default:
       
   835                                 throw new Zend_Json_Exception("Illegal escape "
       
   836                                     .  "sequence '" . $chr . "'");
       
   837                             }
       
   838                     } elseif ($chr == "'") {
       
   839                         break;
       
   840                     } else {
       
   841                         $result .= $chr;
       
   842                     }
       
   843                 } while ($i < $str_length);
       
   844 
       
   845                 $this->_token = self::DATUM;
       
   846                 //$this->_tokenValue = substr($str, $start + 1, $i - $start - 1);
       
   847                 $this->_tokenValue = $result;
       
   848                 break;
       
   849             case 't':
       
   850                 if (($i+ 3) < $str_length && substr($str, $start, 4) == "true") {
       
   851                     $this->_token = self::DATUM;
       
   852                 }
       
   853                 $this->_tokenValue = true;
       
   854                 $i += 3;
       
   855                 break;
       
   856             case 'f':
       
   857                 if (($i+ 4) < $str_length && substr($str, $start, 5) == "false") {
       
   858                     $this->_token = self::DATUM;
       
   859                 }
       
   860                 $this->_tokenValue = false;
       
   861                 $i += 4;
       
   862                 break;
       
   863             case 'n':
       
   864                 if (($i+ 3) < $str_length && substr($str, $start, 4) == "null") {
       
   865                     $this->_token = self::DATUM;
       
   866                 }
       
   867                 $this->_tokenValue = NULL;
       
   868                 $i += 3;
       
   869                 break;
       
   870               case ' ':
       
   871                 break;
       
   872         }
       
   873 
       
   874         if ($this->_token != self::EOF) {
       
   875             $this->_offset = $i + 1; // Consume the last token character
       
   876             return($this->_token);
       
   877         }
       
   878 
       
   879         $chr = $str{$i};
       
   880         if ($chr == '-' || $chr == '.' || ($chr >= '0' && $chr <= '9')) {
       
   881             if (preg_match('/-?([0-9])*(\.[0-9]*)?((e|E)((-|\+)?)[0-9]+)?/s',
       
   882                 $str, $matches, PREG_OFFSET_CAPTURE, $start) && $matches[0][1] == $start) {
       
   883 
       
   884                 $datum = $matches[0][0];
       
   885 
       
   886                 if (is_numeric($datum)) {
       
   887                     if (preg_match('/^0\d+$/', $datum)) {
       
   888                         throw new Zend_Json_Exception("Octal notation not supported by JSON (value: $datum)");
       
   889                     } else {
       
   890                         $val  = intval($datum);
       
   891                         $fVal = floatval($datum);
       
   892                         $this->_tokenValue = ($val == $fVal ? $val : $fVal);
       
   893                     }
       
   894                 } else {
       
   895                     throw new Zend_Json_Exception("Illegal number format: $datum");
       
   896                 }
       
   897 
       
   898                 $this->_token = self::DATUM;
       
   899                 $this->_offset = $start + strlen($datum);
       
   900             }
       
   901         } else {
       
   902             throw new Zend_Json_Exception("Illegal Token at pos $i: $chr\nContext:\n--------------------------------------------------" . substr($str, $i) . "\n--------------------------------------------------");
       
   903         }
       
   904 
       
   905         return($this->_token);
       
   906     }
       
   907 }
       
   908 
       
   909 /**
       
   910  * Zend Framework
       
   911  *
       
   912  * LICENSE
       
   913  *
       
   914  * This source file is subject to the new BSD license that is bundled
       
   915  * with this package in the file LICENSE.txt.
       
   916  * It is also available through the world-wide-web at this URL:
       
   917  * http://framework.zend.com/license/new-bsd
       
   918  * If you did not receive a copy of the license and are unable to
       
   919  * obtain it through the world-wide-web, please send an email
       
   920  * to license@zend.com so we can send you a copy immediately.
       
   921  *
       
   922  * @category   Zend
       
   923  * @package    Zend_Json
       
   924  * @copyright  Copyright (c) 2005-2007 Zend Technologies USA Inc. (http://www.zend.com)
       
   925  * @license    http://framework.zend.com/license/new-bsd     New BSD License
       
   926  */
       
   927 
       
   928 
       
   929 /**
       
   930  * @category   Zend
       
   931  * @package    Zend_Json
       
   932  * @copyright  Copyright (c) 2005-2007 Zend Technologies USA Inc. (http://www.zend.com)
       
   933  * @license    http://framework.zend.com/license/new-bsd     New BSD License
       
   934  */
       
   935 class Zend_Json_Exception extends Zend_Exception
       
   936 {}
       
   937 
       
   938 /**
       
   939  * @category   Zend
       
   940  * @package    Zend
       
   941  * @copyright  Copyright (c) 2005-2007 Zend Technologies USA Inc. (http://www.zend.com)
       
   942  * @license    http://framework.zend.com/license/new-bsd     New BSD License
       
   943  */
       
   944 class Zend_Exception extends Exception
       
   945 {}
       
   946 
       
   947 /**
       
   948  * Class for encoding to and decoding from JSON.
       
   949  *
       
   950  * @category   Zend
       
   951  * @package    Zend_Json
       
   952  * @copyright  Copyright (c) 2005-2007 Zend Technologies USA Inc. (http://www.zend.com)
       
   953  * @license    http://framework.zend.com/license/new-bsd     New BSD License
       
   954  */
       
   955 class Zend_Json
       
   956 {
       
   957     /**
       
   958      * How objects should be encoded -- arrays or as StdClass. TYPE_ARRAY is 1
       
   959      * so that it is a boolean true value, allowing it to be used with
       
   960      * ext/json's functions.
       
   961      */
       
   962     const TYPE_ARRAY  = 1;
       
   963     const TYPE_OBJECT = 0;
       
   964 
       
   965     /**
       
   966      * @var bool
       
   967      */
       
   968     public static $useBuiltinEncoderDecoder = true;
       
   969 
       
   970     /**
       
   971      * Decodes the given $encodedValue string which is
       
   972      * encoded in the JSON format
       
   973      *
       
   974      * Uses ext/json's json_decode if available.
       
   975      *
       
   976      * @param string $encodedValue Encoded in JSON format
       
   977      * @param int $objectDecodeType Optional; flag indicating how to decode
       
   978      * objects. See {@link ZJsonDecoder::decode()} for details.
       
   979      * @return mixed
       
   980      */
       
   981     public static function decode($encodedValue, $objectDecodeType = Zend_Json::TYPE_ARRAY)
       
   982     {
       
   983         if (function_exists('json_decode') && self::$useBuiltinEncoderDecoder !== true) {
       
   984             return json_decode($encodedValue, $objectDecodeType);
       
   985         }
       
   986 
       
   987         return Zend_Json_Decoder::decode($encodedValue, $objectDecodeType);
       
   988     }
       
   989 
       
   990 
       
   991     /**
       
   992      * Encode the mixed $valueToEncode into the JSON format
       
   993      *
       
   994      * Encodes using ext/json's json_encode() if available.
       
   995      *
       
   996      * NOTE: Object should not contain cycles; the JSON format
       
   997      * does not allow object reference.
       
   998      *
       
   999      * NOTE: Only public variables will be encoded
       
  1000      *
       
  1001      * @param mixed $valueToEncode
       
  1002      * @param boolean $cycleCheck Optional; whether or not to check for object recursion; off by default
       
  1003      * @return string JSON encoded object
       
  1004      */
       
  1005     public static function encode($valueToEncode, $cycleCheck = false)
       
  1006     {
       
  1007         if (function_exists('json_encode') && self::$useBuiltinEncoderDecoder !== true) {
       
  1008             return json_encode($valueToEncode);
       
  1009         }
       
  1010 
       
  1011         return Zend_Json_Encoder::encode($valueToEncode, $cycleCheck);
       
  1012     }
       
  1013 }
       
  1014 
       
  1015 ?>