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.
17
* @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
18
* @license http://framework.zend.com/license/new-bsd New BSD License
24
require_once 'Zend/Json.php';
27
* @see Zend_Json_Exception
29
require_once 'Zend/Json/Exception.php';
33
* Decode JSON encoded string to PHP variable constructs
37
* @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
38
* @license http://framework.zend.com/license/new-bsd New BSD License
40
class Zend_Json_Decoder
43
* Parse tokens used to decode the JSON object. These are not
44
* for public consumption, they are just used internally to the
57
* Use to maintain a "pointer" to the source being decoded
64
* Caches the source length
68
protected $_sourceLength;
71
* The offset within the souce being decoded
79
* The current token being considered in the parser cycle
86
* Flag indicating how objects should be decoded
91
protected $_decodeType;
96
* @param string $source String source to decode
97
* @param int $decodeType How objects should be decoded -- see
98
* {@link Zend_Json::TYPE_ARRAY} and {@link Zend_Json::TYPE_OBJECT} for
102
protected function __construct($source, $decodeType)
105
$this->_source = $source;
106
$this->_sourceLength = strlen($source);
107
$this->_token = self::EOF;
110
// Normalize and set $decodeType
111
if (!in_array($decodeType, array(Zend_Json::TYPE_ARRAY, Zend_Json::TYPE_OBJECT)))
113
$decodeType = Zend_Json::TYPE_ARRAY;
115
$this->_decodeType = $decodeType;
117
// Set pointer at first token
118
$this->_getNextToken();
122
* Decode a JSON source string
124
* Decodes a JSON encoded string. The value returned will be one of the
132
* - array of one or more of the above types
134
* By default, decoded objects will be returned as associative arrays; to
135
* return a StdClass object instead, pass {@link Zend_Json::TYPE_OBJECT} to
136
* the $objectDecodeType parameter.
138
* Throws a Zend_Json_Exception if the source string is null.
142
* @param string $source String to be decoded
143
* @param int $objectDecodeType How objects should be decoded; should be
144
* either or {@link Zend_Json::TYPE_ARRAY} or
145
* {@link Zend_Json::TYPE_OBJECT}; defaults to TYPE_ARRAY
147
* @throws Zend_Json_Exception
149
public static function decode($source = null, $objectDecodeType = Zend_Json::TYPE_ARRAY)
151
if (null === $source) {
152
throw new Zend_Json_Exception('Must specify JSON encoded source for decoding');
153
} elseif (!is_string($source)) {
154
throw new Zend_Json_Exception('Can only decode JSON encoded strings');
157
$decoder = new self($source, $objectDecodeType);
159
return $decoder->_decodeValue();
164
* Recursive driving rountine for supported toplevel tops
168
protected function _decodeValue()
170
switch ($this->_token) {
172
$result = $this->_tokenValue;
173
$this->_getNextToken();
177
return($this->_decodeObject());
180
return($this->_decodeArray());
189
* Decodes an object of the form:
190
* { "attribute: value, "attribute2" : value,...}
192
* If Zend_Json_Encoder was used to encode the original object then
193
* a special attribute called __className which specifies a class
194
* name that should wrap the data contained within the encoded source.
196
* Decodes to either an array or StdClass object, based on the value of
197
* {@link $_decodeType}. If invalid $_decodeType present, returns as an
200
* @return array|StdClass
202
protected function _decodeObject()
205
$tok = $this->_getNextToken();
207
while ($tok && $tok != self::RBRACE) {
208
if ($tok != self::DATUM || ! is_string($this->_tokenValue)) {
209
throw new Zend_Json_Exception('Missing key in object encoding: ' . $this->_source);
212
$key = $this->_tokenValue;
213
$tok = $this->_getNextToken();
215
if ($tok != self::COLON) {
216
throw new Zend_Json_Exception('Missing ":" in object encoding: ' . $this->_source);
219
$tok = $this->_getNextToken();
220
$members[$key] = $this->_decodeValue();
221
$tok = $this->_token;
223
if ($tok == self::RBRACE) {
227
if ($tok != self::COMMA) {
228
throw new Zend_Json_Exception('Missing "," in object encoding: ' . $this->_source);
231
$tok = $this->_getNextToken();
234
switch ($this->_decodeType) {
235
case Zend_Json::TYPE_OBJECT:
236
// Create new StdClass and populate with $members
237
$result = new StdClass();
238
foreach ($members as $key => $value) {
239
$result->$key = $value;
242
case Zend_Json::TYPE_ARRAY:
248
$this->_getNextToken();
253
* Decodes a JSON array format:
254
* [element, element2,...,elementN]
258
protected function _decodeArray()
261
$starttok = $tok = $this->_getNextToken(); // Move past the '['
264
while ($tok && $tok != self::RBRACKET) {
265
$result[$index++] = $this->_decodeValue();
267
$tok = $this->_token;
269
if ($tok == self::RBRACKET || !$tok) {
273
if ($tok != self::COMMA) {
274
throw new Zend_Json_Exception('Missing "," in array encoding: ' . $this->_source);
277
$tok = $this->_getNextToken();
280
$this->_getNextToken();
286
* Removes whitepsace characters from the source input
288
protected function _eatWhitespace()
291
'/([\t\b\f\n\r ])*/s',
296
&& $matches[0][1] == $this->_offset)
298
$this->_offset += strlen($matches[0][0]);
304
* Retrieves the next token from the source stream
306
* @return int Token constant value specified in class definition
308
protected function _getNextToken()
310
$this->_token = self::EOF;
311
$this->_tokenValue = null;
312
$this->_eatWhitespace();
314
if ($this->_offset >= $this->_sourceLength) {
318
$str = $this->_source;
319
$str_length = $this->_sourceLength;
325
$this->_token = self::LBRACE;
328
$this->_token = self::RBRACE;
331
$this->_token = self::LBRACKET;
334
$this->_token = self::RBRACKET;
337
$this->_token = self::COMMA;
340
$this->_token = self::COLON;
346
if ($i >= $str_length) {
353
if ($i >= $str_length) {
386
throw new Zend_Json_Exception("Illegal escape "
387
. "sequence '" . $chr . "'");
389
} elseif ($chr == '"') {
394
} while ($i < $str_length);
396
$this->_token = self::DATUM;
397
//$this->_tokenValue = substr($str, $start + 1, $i - $start - 1);
398
$this->_tokenValue = $result;
401
if (($i+ 3) < $str_length && substr($str, $start, 4) == "true") {
402
$this->_token = self::DATUM;
404
$this->_tokenValue = true;
408
if (($i+ 4) < $str_length && substr($str, $start, 5) == "false") {
409
$this->_token = self::DATUM;
411
$this->_tokenValue = false;
415
if (($i+ 3) < $str_length && substr($str, $start, 4) == "null") {
416
$this->_token = self::DATUM;
418
$this->_tokenValue = NULL;
423
if ($this->_token != self::EOF) {
424
$this->_offset = $i + 1; // Consume the last token character
425
return($this->_token);
429
if ($chr == '-' || $chr == '.' || ($chr >= '0' && $chr <= '9')) {
430
if (preg_match('/-?([0-9])*(\.[0-9]*)?((e|E)((-|\+)?)[0-9]+)?/s',
431
$str, $matches, PREG_OFFSET_CAPTURE, $start) && $matches[0][1] == $start) {
433
$datum = $matches[0][0];
435
if (is_numeric($datum)) {
436
if (preg_match('/^0\d+$/', $datum)) {
437
throw new Zend_Json_Exception("Octal notation not supported by JSON (value: $datum)");
439
$val = intval($datum);
440
$fVal = floatval($datum);
441
$this->_tokenValue = ($val == $fVal ? $val : $fVal);
444
throw new Zend_Json_Exception("Illegal number format: $datum");
447
$this->_token = self::DATUM;
448
$this->_offset = $start + strlen($datum);
451
throw new Zend_Json_Exception('Illegal Token');
454
return($this->_token);