00001 <?php
00025 require_once 'Zend/Json.php';
00026
00035 class Zend_Json_Decoder
00036 {
00042 const EOF = 0;
00043 const DATUM = 1;
00044 const LBRACE = 2;
00045 const LBRACKET = 3;
00046 const RBRACE = 4;
00047 const RBRACKET = 5;
00048 const COMMA = 6;
00049 const COLON = 7;
00050
00056 protected $_source;
00057
00063 protected $_sourceLength;
00064
00071 protected $_offset;
00072
00078 protected $_token;
00079
00086 protected $_decodeType;
00087
00097 protected function __construct($source, $decodeType)
00098 {
00099
00100 $this->_source = self::decodeUnicodeString($source);
00101 $this->_sourceLength = strlen($this->_source);
00102 $this->_token = self::EOF;
00103 $this->_offset = 0;
00104
00105
00106 if (!in_array($decodeType, array(Zend_Json::TYPE_ARRAY, Zend_Json::TYPE_OBJECT)))
00107 {
00108 $decodeType = Zend_Json::TYPE_ARRAY;
00109 }
00110 $this->_decodeType = $decodeType;
00111
00112
00113 $this->_getNextToken();
00114 }
00115
00144 public static function decode($source = null, $objectDecodeType = Zend_Json::TYPE_ARRAY)
00145 {
00146 if (null === $source) {
00147 require_once 'Zend/Json/Exception.php';
00148 throw new Zend_Json_Exception('Must specify JSON encoded source for decoding');
00149 } elseif (!is_string($source)) {
00150 require_once 'Zend/Json/Exception.php';
00151 throw new Zend_Json_Exception('Can only decode JSON encoded strings');
00152 }
00153
00154 $decoder = new self($source, $objectDecodeType);
00155
00156 return $decoder->_decodeValue();
00157 }
00158
00159
00165 protected function _decodeValue()
00166 {
00167 switch ($this->_token) {
00168 case self::DATUM:
00169 $result = $this->_tokenValue;
00170 $this->_getNextToken();
00171 return($result);
00172 break;
00173 case self::LBRACE:
00174 return($this->_decodeObject());
00175 break;
00176 case self::LBRACKET:
00177 return($this->_decodeArray());
00178 break;
00179 default:
00180 return null;
00181 break;
00182 }
00183 }
00184
00199 protected function _decodeObject()
00200 {
00201 $members = array();
00202 $tok = $this->_getNextToken();
00203
00204 while ($tok && $tok != self::RBRACE) {
00205 if ($tok != self::DATUM || ! is_string($this->_tokenValue)) {
00206 require_once 'Zend/Json/Exception.php';
00207 throw new Zend_Json_Exception('Missing key in object encoding: ' . $this->_source);
00208 }
00209
00210 $key = $this->_tokenValue;
00211 $tok = $this->_getNextToken();
00212
00213 if ($tok != self::COLON) {
00214 require_once 'Zend/Json/Exception.php';
00215 throw new Zend_Json_Exception('Missing ":" in object encoding: ' . $this->_source);
00216 }
00217
00218 $tok = $this->_getNextToken();
00219 $members[$key] = $this->_decodeValue();
00220 $tok = $this->_token;
00221
00222 if ($tok == self::RBRACE) {
00223 break;
00224 }
00225
00226 if ($tok != self::COMMA) {
00227 require_once 'Zend/Json/Exception.php';
00228 throw new Zend_Json_Exception('Missing "," in object encoding: ' . $this->_source);
00229 }
00230
00231 $tok = $this->_getNextToken();
00232 }
00233
00234 switch ($this->_decodeType) {
00235 case Zend_Json::TYPE_OBJECT:
00236
00237 $result = new StdClass();
00238 foreach ($members as $key => $value) {
00239 $result->$key = $value;
00240 }
00241 break;
00242 case Zend_Json::TYPE_ARRAY:
00243 default:
00244 $result = $members;
00245 break;
00246 }
00247
00248 $this->_getNextToken();
00249 return $result;
00250 }
00251
00258 protected function _decodeArray()
00259 {
00260 $result = array();
00261 $starttok = $tok = $this->_getNextToken();
00262 $index = 0;
00263
00264 while ($tok && $tok != self::RBRACKET) {
00265 $result[$index++] = $this->_decodeValue();
00266
00267 $tok = $this->_token;
00268
00269 if ($tok == self::RBRACKET || !$tok) {
00270 break;
00271 }
00272
00273 if ($tok != self::COMMA) {
00274 require_once 'Zend/Json/Exception.php';
00275 throw new Zend_Json_Exception('Missing "," in array encoding: ' . $this->_source);
00276 }
00277
00278 $tok = $this->_getNextToken();
00279 }
00280
00281 $this->_getNextToken();
00282 return($result);
00283 }
00284
00285
00289 protected function _eatWhitespace()
00290 {
00291 if (preg_match(
00292 '/([\t\b\f\n\r ])*/s',
00293 $this->_source,
00294 $matches,
00295 PREG_OFFSET_CAPTURE,
00296 $this->_offset)
00297 && $matches[0][1] == $this->_offset)
00298 {
00299 $this->_offset += strlen($matches[0][0]);
00300 }
00301 }
00302
00303
00309 protected function _getNextToken()
00310 {
00311 $this->_token = self::EOF;
00312 $this->_tokenValue = null;
00313 $this->_eatWhitespace();
00314
00315 if ($this->_offset >= $this->_sourceLength) {
00316 return(self::EOF);
00317 }
00318
00319 $str = $this->_source;
00320 $str_length = $this->_sourceLength;
00321 $i = $this->_offset;
00322 $start = $i;
00323
00324 switch ($str{$i}) {
00325 case '{':
00326 $this->_token = self::LBRACE;
00327 break;
00328 case '}':
00329 $this->_token = self::RBRACE;
00330 break;
00331 case '[':
00332 $this->_token = self::LBRACKET;
00333 break;
00334 case ']':
00335 $this->_token = self::RBRACKET;
00336 break;
00337 case ',':
00338 $this->_token = self::COMMA;
00339 break;
00340 case ':':
00341 $this->_token = self::COLON;
00342 break;
00343 case '"':
00344 $result = '';
00345 do {
00346 $i++;
00347 if ($i >= $str_length) {
00348 break;
00349 }
00350
00351 $chr = $str{$i};
00352
00353 if ($chr == '\\') {
00354 $i++;
00355 if ($i >= $str_length) {
00356 break;
00357 }
00358 $chr = $str{$i};
00359 switch ($chr) {
00360 case '"' :
00361 $result .= '"';
00362 break;
00363 case '\\':
00364 $result .= '\\';
00365 break;
00366 case '/' :
00367 $result .= '/';
00368 break;
00369 case 'b' :
00370 $result .= chr(8);
00371 break;
00372 case 'f' :
00373 $result .= chr(12);
00374 break;
00375 case 'n' :
00376 $result .= chr(10);
00377 break;
00378 case 'r' :
00379 $result .= chr(13);
00380 break;
00381 case 't' :
00382 $result .= chr(9);
00383 break;
00384 case '\'' :
00385 $result .= '\'';
00386 break;
00387 default:
00388 require_once 'Zend/Json/Exception.php';
00389 throw new Zend_Json_Exception("Illegal escape "
00390 . "sequence '" . $chr . "'");
00391 }
00392 } elseif($chr == '"') {
00393 break;
00394 } else {
00395 $result .= $chr;
00396 }
00397 } while ($i < $str_length);
00398
00399 $this->_token = self::DATUM;
00400
00401 $this->_tokenValue = $result;
00402 break;
00403 case 't':
00404 if (($i+ 3) < $str_length && substr($str, $start, 4) == "true") {
00405 $this->_token = self::DATUM;
00406 }
00407 $this->_tokenValue = true;
00408 $i += 3;
00409 break;
00410 case 'f':
00411 if (($i+ 4) < $str_length && substr($str, $start, 5) == "false") {
00412 $this->_token = self::DATUM;
00413 }
00414 $this->_tokenValue = false;
00415 $i += 4;
00416 break;
00417 case 'n':
00418 if (($i+ 3) < $str_length && substr($str, $start, 4) == "null") {
00419 $this->_token = self::DATUM;
00420 }
00421 $this->_tokenValue = NULL;
00422 $i += 3;
00423 break;
00424 }
00425
00426 if ($this->_token != self::EOF) {
00427 $this->_offset = $i + 1;
00428 return($this->_token);
00429 }
00430
00431 $chr = $str{$i};
00432 if ($chr == '-' || $chr == '.' || ($chr >= '0' && $chr <= '9')) {
00433 if (preg_match('/-?([0-9])*(\.[0-9]*)?((e|E)((-|\+)?)[0-9]+)?/s',
00434 $str, $matches, PREG_OFFSET_CAPTURE, $start) && $matches[0][1] == $start) {
00435
00436 $datum = $matches[0][0];
00437
00438 if (is_numeric($datum)) {
00439 if (preg_match('/^0\d+$/', $datum)) {
00440 require_once 'Zend/Json/Exception.php';
00441 throw new Zend_Json_Exception("Octal notation not supported by JSON (value: $datum)");
00442 } else {
00443 $val = intval($datum);
00444 $fVal = floatval($datum);
00445 $this->_tokenValue = ($val == $fVal ? $val : $fVal);
00446 }
00447 } else {
00448 require_once 'Zend/Json/Exception.php';
00449 throw new Zend_Json_Exception("Illegal number format: $datum");
00450 }
00451
00452 $this->_token = self::DATUM;
00453 $this->_offset = $start + strlen($datum);
00454 }
00455 } else {
00456 require_once 'Zend/Json/Exception.php';
00457 throw new Zend_Json_Exception('Illegal Token');
00458 }
00459
00460 return($this->_token);
00461 }
00462
00474 public static function decodeUnicodeString($chrs)
00475 {
00476 $delim = substr($chrs, 0, 1);
00477 $utf8 = '';
00478 $strlen_chrs = strlen($chrs);
00479
00480 for($i = 0; $i < $strlen_chrs; $i++) {
00481
00482 $substr_chrs_c_2 = substr($chrs, $i, 2);
00483 $ord_chrs_c = ord($chrs[$i]);
00484
00485 switch (true) {
00486 case preg_match('/\\\u[0-9A-F]{4}/i', substr($chrs, $i, 6)):
00487
00488 $utf16 = chr(hexdec(substr($chrs, ($i + 2), 2)))
00489 . chr(hexdec(substr($chrs, ($i + 4), 2)));
00490 $utf8 .= self::_utf162utf8($utf16);
00491 $i += 5;
00492 break;
00493 case ($ord_chrs_c >= 0x20) && ($ord_chrs_c <= 0x7F):
00494 $utf8 .= $chrs{$i};
00495 break;
00496 case ($ord_chrs_c & 0xE0) == 0xC0:
00497
00498
00499 $utf8 .= substr($chrs, $i, 2);
00500 ++$i;
00501 break;
00502 case ($ord_chrs_c & 0xF0) == 0xE0:
00503
00504
00505 $utf8 .= substr($chrs, $i, 3);
00506 $i += 2;
00507 break;
00508 case ($ord_chrs_c & 0xF8) == 0xF0:
00509
00510
00511 $utf8 .= substr($chrs, $i, 4);
00512 $i += 3;
00513 break;
00514 case ($ord_chrs_c & 0xFC) == 0xF8:
00515
00516
00517 $utf8 .= substr($chrs, $i, 5);
00518 $i += 4;
00519 break;
00520 case ($ord_chrs_c & 0xFE) == 0xFC:
00521
00522
00523 $utf8 .= substr($chrs, $i, 6);
00524 $i += 5;
00525 break;
00526 }
00527 }
00528
00529 return $utf8;
00530 }
00531
00545 protected static function _utf162utf8($utf16)
00546 {
00547
00548 if( function_exists('mb_convert_encoding') ) {
00549 return mb_convert_encoding($utf16, 'UTF-8', 'UTF-16');
00550 }
00551
00552 $bytes = (ord($utf16{0}) << 8) | ord($utf16{1});
00553
00554 switch (true) {
00555 case ((0x7F & $bytes) == $bytes):
00556
00557
00558 return chr(0x7F & $bytes);
00559
00560 case (0x07FF & $bytes) == $bytes:
00561
00562
00563 return chr(0xC0 | (($bytes >> 6) & 0x1F))
00564 . chr(0x80 | ($bytes & 0x3F));
00565
00566 case (0xFFFF & $bytes) == $bytes:
00567
00568
00569 return chr(0xE0 | (($bytes >> 12) & 0x0F))
00570 . chr(0x80 | (($bytes >> 6) & 0x3F))
00571 . chr(0x80 | ($bytes & 0x3F));
00572 }
00573
00574
00575 return '';
00576 }
00577 }
00578