578 Zeilen
		
	
	
	
		
			18 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			578 Zeilen
		
	
	
	
		
			18 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?php
 | |
| /**
 | |
|  * Zend Framework
 | |
|  *
 | |
|  * LICENSE
 | |
|  *
 | |
|  * This source file is subject to the new BSD license that is bundled
 | |
|  * with this package in the file LICENSE.txt.
 | |
|  * It is also available through the world-wide-web at this URL:
 | |
|  * http://framework.zend.com/license/new-bsd
 | |
|  * If you did not receive a copy of the license and are unable to
 | |
|  * obtain it through the world-wide-web, please send an email
 | |
|  * to license@zend.com so we can send you a copy immediately.
 | |
|  *
 | |
|  * @category   Zend
 | |
|  * @package    Zend_Json
 | |
|  * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
 | |
|  * @license    http://framework.zend.com/license/new-bsd     New BSD License
 | |
|  * @version    $Id$
 | |
|  */
 | |
| 
 | |
| /**
 | |
|  * @see Zend_Json
 | |
|  */
 | |
| require_once 'Zend/Json.php';
 | |
| 
 | |
| /**
 | |
|  * Decode JSON encoded string to PHP variable constructs
 | |
|  *
 | |
|  * @category   Zend
 | |
|  * @package    Zend_Json
 | |
|  * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
 | |
|  * @license    http://framework.zend.com/license/new-bsd     New BSD License
 | |
|  */
 | |
| class Zend_Json_Decoder
 | |
| {
 | |
|     /**
 | |
|      * Parse tokens used to decode the JSON object. These are not
 | |
|      * for public consumption, they are just used internally to the
 | |
|      * class.
 | |
|      */
 | |
|     const EOF         = 0;
 | |
|     const DATUM        = 1;
 | |
|     const LBRACE    = 2;
 | |
|     const LBRACKET    = 3;
 | |
|     const RBRACE     = 4;
 | |
|     const RBRACKET    = 5;
 | |
|     const COMMA       = 6;
 | |
|     const COLON        = 7;
 | |
| 
 | |
|     /**
 | |
|      * Use to maintain a "pointer" to the source being decoded
 | |
|      *
 | |
|      * @var string
 | |
|      */
 | |
|     protected $_source;
 | |
| 
 | |
|     /**
 | |
|      * Caches the source length
 | |
|      *
 | |
|      * @var int
 | |
|      */
 | |
|     protected $_sourceLength;
 | |
| 
 | |
|     /**
 | |
|      * The offset within the souce being decoded
 | |
|      *
 | |
|      * @var int
 | |
|      *
 | |
|      */
 | |
|     protected $_offset;
 | |
| 
 | |
|     /**
 | |
|      * The current token being considered in the parser cycle
 | |
|      *
 | |
|      * @var int
 | |
|      */
 | |
|     protected $_token;
 | |
| 
 | |
|     /**
 | |
|      * Flag indicating how objects should be decoded
 | |
|      *
 | |
|      * @var int
 | |
|      * @access protected
 | |
|      */
 | |
|     protected $_decodeType;
 | |
| 
 | |
|     /**
 | |
|      * Constructor
 | |
|      *
 | |
|      * @param string $source String source to decode
 | |
|      * @param int $decodeType How objects should be decoded -- see
 | |
|      * {@link Zend_Json::TYPE_ARRAY} and {@link Zend_Json::TYPE_OBJECT} for
 | |
|      * valid values
 | |
|      * @return void
 | |
|      */
 | |
|     protected function __construct($source, $decodeType)
 | |
|     {
 | |
|         // Set defaults
 | |
|         $this->_source       = self::decodeUnicodeString($source);
 | |
|         $this->_sourceLength = strlen($this->_source);
 | |
|         $this->_token        = self::EOF;
 | |
|         $this->_offset       = 0;
 | |
| 
 | |
|         // Normalize and set $decodeType
 | |
|         if (!in_array($decodeType, array(Zend_Json::TYPE_ARRAY, Zend_Json::TYPE_OBJECT)))
 | |
|         {
 | |
|             $decodeType = Zend_Json::TYPE_ARRAY;
 | |
|         }
 | |
|         $this->_decodeType   = $decodeType;
 | |
| 
 | |
|         // Set pointer at first token
 | |
|         $this->_getNextToken();
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Decode a JSON source string
 | |
|      *
 | |
|      * Decodes a JSON encoded string. The value returned will be one of the
 | |
|      * following:
 | |
|      *        - integer
 | |
|      *        - float
 | |
|      *        - boolean
 | |
|      *        - null
 | |
|      *      - StdClass
 | |
|      *      - array
 | |
|      *         - array of one or more of the above types
 | |
|      *
 | |
|      * By default, decoded objects will be returned as associative arrays; to
 | |
|      * return a StdClass object instead, pass {@link Zend_Json::TYPE_OBJECT} to
 | |
|      * the $objectDecodeType parameter.
 | |
|      *
 | |
|      * Throws a Zend_Json_Exception if the source string is null.
 | |
|      *
 | |
|      * @static
 | |
|      * @access public
 | |
|      * @param string $source String to be decoded
 | |
|      * @param int $objectDecodeType How objects should be decoded; should be
 | |
|      * either or {@link Zend_Json::TYPE_ARRAY} or
 | |
|      * {@link Zend_Json::TYPE_OBJECT}; defaults to TYPE_ARRAY
 | |
|      * @return mixed
 | |
|      * @throws Zend_Json_Exception
 | |
|      */
 | |
|     public static function decode($source = null, $objectDecodeType = Zend_Json::TYPE_ARRAY)
 | |
|     {
 | |
|         if (null === $source) {
 | |
|             require_once 'Zend/Json/Exception.php';
 | |
|             throw new Zend_Json_Exception('Must specify JSON encoded source for decoding');
 | |
|         } elseif (!is_string($source)) {
 | |
|             require_once 'Zend/Json/Exception.php';
 | |
|             throw new Zend_Json_Exception('Can only decode JSON encoded strings');
 | |
|         }
 | |
| 
 | |
|         $decoder = new self($source, $objectDecodeType);
 | |
| 
 | |
|         return $decoder->_decodeValue();
 | |
|     }
 | |
| 
 | |
| 
 | |
|     /**
 | |
|      * Recursive driving rountine for supported toplevel tops
 | |
|      *
 | |
|      * @return mixed
 | |
|      */
 | |
|     protected function _decodeValue()
 | |
|     {
 | |
|         switch ($this->_token) {
 | |
|             case self::DATUM:
 | |
|                 $result  = $this->_tokenValue;
 | |
|                 $this->_getNextToken();
 | |
|                 return($result);
 | |
|                 break;
 | |
|             case self::LBRACE:
 | |
|                 return($this->_decodeObject());
 | |
|                 break;
 | |
|             case self::LBRACKET:
 | |
|                 return($this->_decodeArray());
 | |
|                 break;
 | |
|             default:
 | |
|                 return null;
 | |
|                 break;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Decodes an object of the form:
 | |
|      *  { "attribute: value, "attribute2" : value,...}
 | |
|      *
 | |
|      * If Zend_Json_Encoder was used to encode the original object then
 | |
|      * a special attribute called __className which specifies a class
 | |
|      * name that should wrap the data contained within the encoded source.
 | |
|      *
 | |
|      * Decodes to either an array or StdClass object, based on the value of
 | |
|      * {@link $_decodeType}. If invalid $_decodeType present, returns as an
 | |
|      * array.
 | |
|      *
 | |
|      * @return array|StdClass
 | |
|      */
 | |
|     protected function _decodeObject()
 | |
|     {
 | |
|         $members = array();
 | |
|         $tok = $this->_getNextToken();
 | |
| 
 | |
|         while ($tok && $tok != self::RBRACE) {
 | |
|             if ($tok != self::DATUM || ! is_string($this->_tokenValue)) {
 | |
|                 require_once 'Zend/Json/Exception.php';
 | |
|                 throw new Zend_Json_Exception('Missing key in object encoding: ' . $this->_source);
 | |
|             }
 | |
| 
 | |
|             $key = $this->_tokenValue;
 | |
|             $tok = $this->_getNextToken();
 | |
| 
 | |
|             if ($tok != self::COLON) {
 | |
|                 require_once 'Zend/Json/Exception.php';
 | |
|                 throw new Zend_Json_Exception('Missing ":" in object encoding: ' . $this->_source);
 | |
|             }
 | |
| 
 | |
|             $tok = $this->_getNextToken();
 | |
|             $members[$key] = $this->_decodeValue();
 | |
|             $tok = $this->_token;
 | |
| 
 | |
|             if ($tok == self::RBRACE) {
 | |
|                 break;
 | |
|             }
 | |
| 
 | |
|             if ($tok != self::COMMA) {
 | |
|                 require_once 'Zend/Json/Exception.php';
 | |
|                 throw new Zend_Json_Exception('Missing "," in object encoding: ' . $this->_source);
 | |
|             }
 | |
| 
 | |
|             $tok = $this->_getNextToken();
 | |
|         }
 | |
| 
 | |
|         switch ($this->_decodeType) {
 | |
|             case Zend_Json::TYPE_OBJECT:
 | |
|                 // Create new StdClass and populate with $members
 | |
|                 $result = new StdClass();
 | |
|                 foreach ($members as $key => $value) {
 | |
|                     $result->$key = $value;
 | |
|                 }
 | |
|                 break;
 | |
|             case Zend_Json::TYPE_ARRAY:
 | |
|             default:
 | |
|                 $result = $members;
 | |
|                 break;
 | |
|         }
 | |
| 
 | |
|         $this->_getNextToken();
 | |
|         return $result;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Decodes a JSON array format:
 | |
|      *    [element, element2,...,elementN]
 | |
|      *
 | |
|      * @return array
 | |
|      */
 | |
|     protected function _decodeArray()
 | |
|     {
 | |
|         $result = array();
 | |
|         $starttok = $tok = $this->_getNextToken(); // Move past the '['
 | |
|         $index  = 0;
 | |
| 
 | |
|         while ($tok && $tok != self::RBRACKET) {
 | |
|             $result[$index++] = $this->_decodeValue();
 | |
| 
 | |
|             $tok = $this->_token;
 | |
| 
 | |
|             if ($tok == self::RBRACKET || !$tok) {
 | |
|                 break;
 | |
|             }
 | |
| 
 | |
|             if ($tok != self::COMMA) {
 | |
|                 require_once 'Zend/Json/Exception.php';
 | |
|                 throw new Zend_Json_Exception('Missing "," in array encoding: ' . $this->_source);
 | |
|             }
 | |
| 
 | |
|             $tok = $this->_getNextToken();
 | |
|         }
 | |
| 
 | |
|         $this->_getNextToken();
 | |
|         return($result);
 | |
|     }
 | |
| 
 | |
| 
 | |
|     /**
 | |
|      * Removes whitepsace characters from the source input
 | |
|      */
 | |
|     protected function _eatWhitespace()
 | |
|     {
 | |
|         if (preg_match(
 | |
|                 '/([\t\b\f\n\r ])*/s',
 | |
|                 $this->_source,
 | |
|                 $matches,
 | |
|                 PREG_OFFSET_CAPTURE,
 | |
|                 $this->_offset)
 | |
|             && $matches[0][1] == $this->_offset)
 | |
|         {
 | |
|             $this->_offset += strlen($matches[0][0]);
 | |
|         }
 | |
|     }
 | |
| 
 | |
| 
 | |
|     /**
 | |
|      * Retrieves the next token from the source stream
 | |
|      *
 | |
|      * @return int Token constant value specified in class definition
 | |
|      */
 | |
|     protected function _getNextToken()
 | |
|     {
 | |
|         $this->_token      = self::EOF;
 | |
|         $this->_tokenValue = null;
 | |
|         $this->_eatWhitespace();
 | |
| 
 | |
|         if ($this->_offset >= $this->_sourceLength) {
 | |
|             return(self::EOF);
 | |
|         }
 | |
| 
 | |
|         $str        = $this->_source;
 | |
|         $str_length = $this->_sourceLength;
 | |
|         $i          = $this->_offset;
 | |
|         $start      = $i;
 | |
| 
 | |
|         switch ($str{$i}) {
 | |
|             case '{':
 | |
|                $this->_token = self::LBRACE;
 | |
|                break;
 | |
|             case '}':
 | |
|                 $this->_token = self::RBRACE;
 | |
|                 break;
 | |
|             case '[':
 | |
|                 $this->_token = self::LBRACKET;
 | |
|                 break;
 | |
|             case ']':
 | |
|                 $this->_token = self::RBRACKET;
 | |
|                 break;
 | |
|             case ',':
 | |
|                 $this->_token = self::COMMA;
 | |
|                 break;
 | |
|             case ':':
 | |
|                 $this->_token = self::COLON;
 | |
|                 break;
 | |
|             case  '"':
 | |
|                 $result = '';
 | |
|                 do {
 | |
|                     $i++;
 | |
|                     if ($i >= $str_length) {
 | |
|                         break;
 | |
|                     }
 | |
| 
 | |
|                     $chr = $str{$i};
 | |
| 
 | |
|                     if ($chr == '\\') {
 | |
|                         $i++;
 | |
|                         if ($i >= $str_length) {
 | |
|                             break;
 | |
|                         }
 | |
|                         $chr = $str{$i};
 | |
|                         switch ($chr) {
 | |
|                             case '"' :
 | |
|                                 $result .= '"';
 | |
|                                 break;
 | |
|                             case '\\':
 | |
|                                 $result .= '\\';
 | |
|                                 break;
 | |
|                             case '/' :
 | |
|                                 $result .= '/';
 | |
|                                 break;
 | |
|                             case 'b' :
 | |
|                                 $result .= "\x08";
 | |
|                                 break;
 | |
|                             case 'f' :
 | |
|                                 $result .= "\x0c";
 | |
|                                 break;
 | |
|                             case 'n' :
 | |
|                                 $result .= "\x0a";
 | |
|                                 break;
 | |
|                             case 'r' :
 | |
|                                 $result .= "\x0d";
 | |
|                                 break;
 | |
|                             case 't' :
 | |
|                                 $result .= "\x09";
 | |
|                                 break;
 | |
|                             case '\'' :
 | |
|                                 $result .= '\'';
 | |
|                                 break;
 | |
|                             default:
 | |
|                                 require_once 'Zend/Json/Exception.php';
 | |
|                                 throw new Zend_Json_Exception("Illegal escape "
 | |
|                                     .  "sequence '" . $chr . "'");
 | |
|                         }
 | |
|                     } elseif($chr == '"') {
 | |
|                         break;
 | |
|                     } else {
 | |
|                         $result .= $chr;
 | |
|                     }
 | |
|                 } while ($i < $str_length);
 | |
| 
 | |
|                 $this->_token = self::DATUM;
 | |
|                 //$this->_tokenValue = substr($str, $start + 1, $i - $start - 1);
 | |
|                 $this->_tokenValue = $result;
 | |
|                 break;
 | |
|             case 't':
 | |
|                 if (($i+ 3) < $str_length && substr($str, $start, 4) == "true") {
 | |
|                     $this->_token = self::DATUM;
 | |
|                 }
 | |
|                 $this->_tokenValue = true;
 | |
|                 $i += 3;
 | |
|                 break;
 | |
|             case 'f':
 | |
|                 if (($i+ 4) < $str_length && substr($str, $start, 5) == "false") {
 | |
|                     $this->_token = self::DATUM;
 | |
|                 }
 | |
|                 $this->_tokenValue = false;
 | |
|                 $i += 4;
 | |
|                 break;
 | |
|             case 'n':
 | |
|                 if (($i+ 3) < $str_length && substr($str, $start, 4) == "null") {
 | |
|                     $this->_token = self::DATUM;
 | |
|                 }
 | |
|                 $this->_tokenValue = NULL;
 | |
|                 $i += 3;
 | |
|                 break;
 | |
|         }
 | |
| 
 | |
|         if ($this->_token != self::EOF) {
 | |
|             $this->_offset = $i + 1; // Consume the last token character
 | |
|             return($this->_token);
 | |
|         }
 | |
| 
 | |
|         $chr = $str{$i};
 | |
|         if ($chr == '-' || $chr == '.' || ($chr >= '0' && $chr <= '9')) {
 | |
|             if (preg_match('/-?([0-9])*(\.[0-9]*)?((e|E)((-|\+)?)[0-9]+)?/s',
 | |
|                 $str, $matches, PREG_OFFSET_CAPTURE, $start) && $matches[0][1] == $start) {
 | |
| 
 | |
|                 $datum = $matches[0][0];
 | |
| 
 | |
|                 if (is_numeric($datum)) {
 | |
|                     if (preg_match('/^0\d+$/', $datum)) {
 | |
|                         require_once 'Zend/Json/Exception.php';
 | |
|                         throw new Zend_Json_Exception("Octal notation not supported by JSON (value: $datum)");
 | |
|                     } else {
 | |
|                         $val  = intval($datum);
 | |
|                         $fVal = floatval($datum);
 | |
|                         $this->_tokenValue = ($val == $fVal ? $val : $fVal);
 | |
|                     }
 | |
|                 } else {
 | |
|                     require_once 'Zend/Json/Exception.php';
 | |
|                     throw new Zend_Json_Exception("Illegal number format: $datum");
 | |
|                 }
 | |
| 
 | |
|                 $this->_token = self::DATUM;
 | |
|                 $this->_offset = $start + strlen($datum);
 | |
|             }
 | |
|         } else {
 | |
|             require_once 'Zend/Json/Exception.php';
 | |
|             throw new Zend_Json_Exception('Illegal Token');
 | |
|         }
 | |
| 
 | |
|         return($this->_token);
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Decode Unicode Characters from \u0000 ASCII syntax.
 | |
|      *
 | |
|      * This algorithm was originally developed for the
 | |
|      * Solar Framework by Paul M. Jones
 | |
|      *
 | |
|      * @link   http://solarphp.com/
 | |
|      * @link   http://svn.solarphp.com/core/trunk/Solar/Json.php
 | |
|      * @param  string $value
 | |
|      * @return string
 | |
|      */
 | |
|     public static function decodeUnicodeString($chrs)
 | |
|     {
 | |
|         $delim       = substr($chrs, 0, 1);
 | |
|         $utf8        = '';
 | |
|         $strlen_chrs = strlen($chrs);
 | |
| 
 | |
|         for($i = 0; $i < $strlen_chrs; $i++) {
 | |
| 
 | |
|             $substr_chrs_c_2 = substr($chrs, $i, 2);
 | |
|             $ord_chrs_c = ord($chrs[$i]);
 | |
| 
 | |
|             switch (true) {
 | |
|                 case preg_match('/\\\u[0-9A-F]{4}/i', substr($chrs, $i, 6)):
 | |
|                     // single, escaped unicode character
 | |
|                     $utf16 = chr(hexdec(substr($chrs, ($i + 2), 2)))
 | |
|                            . chr(hexdec(substr($chrs, ($i + 4), 2)));
 | |
|                     $utf8 .= self::_utf162utf8($utf16);
 | |
|                     $i += 5;
 | |
|                     break;
 | |
|                 case ($ord_chrs_c >= 0x20) && ($ord_chrs_c <= 0x7F):
 | |
|                     $utf8 .= $chrs{$i};
 | |
|                     break;
 | |
|                 case ($ord_chrs_c & 0xE0) == 0xC0:
 | |
|                     // characters U-00000080 - U-000007FF, mask 110XXXXX
 | |
|                     //see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
 | |
|                     $utf8 .= substr($chrs, $i, 2);
 | |
|                     ++$i;
 | |
|                     break;
 | |
|                 case ($ord_chrs_c & 0xF0) == 0xE0:
 | |
|                     // characters U-00000800 - U-0000FFFF, mask 1110XXXX
 | |
|                     // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
 | |
|                     $utf8 .= substr($chrs, $i, 3);
 | |
|                     $i += 2;
 | |
|                     break;
 | |
|                 case ($ord_chrs_c & 0xF8) == 0xF0:
 | |
|                     // characters U-00010000 - U-001FFFFF, mask 11110XXX
 | |
|                     // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
 | |
|                     $utf8 .= substr($chrs, $i, 4);
 | |
|                     $i += 3;
 | |
|                     break;
 | |
|                 case ($ord_chrs_c & 0xFC) == 0xF8:
 | |
|                     // characters U-00200000 - U-03FFFFFF, mask 111110XX
 | |
|                     // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
 | |
|                     $utf8 .= substr($chrs, $i, 5);
 | |
|                     $i += 4;
 | |
|                     break;
 | |
|                 case ($ord_chrs_c & 0xFE) == 0xFC:
 | |
|                     // characters U-04000000 - U-7FFFFFFF, mask 1111110X
 | |
|                     // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
 | |
|                     $utf8 .= substr($chrs, $i, 6);
 | |
|                     $i += 5;
 | |
|                     break;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         return $utf8;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Convert a string from one UTF-16 char to one UTF-8 char.
 | |
|      *
 | |
|      * Normally should be handled by mb_convert_encoding, but
 | |
|      * provides a slower PHP-only method for installations
 | |
|      * that lack the multibye string extension.
 | |
|      *
 | |
|      * This method is from the Solar Framework by Paul M. Jones
 | |
|      *
 | |
|      * @link   http://solarphp.com
 | |
|      * @param  string $utf16 UTF-16 character
 | |
|      * @return string UTF-8 character
 | |
|      */
 | |
|     protected static function _utf162utf8($utf16)
 | |
|     {
 | |
|         // Check for mb extension otherwise do by hand.
 | |
|         if( function_exists('mb_convert_encoding') ) {
 | |
|             return mb_convert_encoding($utf16, 'UTF-8', 'UTF-16');
 | |
|         }
 | |
| 
 | |
|         $bytes = (ord($utf16{0}) << 8) | ord($utf16{1});
 | |
| 
 | |
|         switch (true) {
 | |
|             case ((0x7F & $bytes) == $bytes):
 | |
|                 // this case should never be reached, because we are in ASCII range
 | |
|                 // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
 | |
|                 return chr(0x7F & $bytes);
 | |
| 
 | |
|             case (0x07FF & $bytes) == $bytes:
 | |
|                 // return a 2-byte UTF-8 character
 | |
|                 // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
 | |
|                 return chr(0xC0 | (($bytes >> 6) & 0x1F))
 | |
|                      . chr(0x80 | ($bytes & 0x3F));
 | |
| 
 | |
|             case (0xFFFF & $bytes) == $bytes:
 | |
|                 // return a 3-byte UTF-8 character
 | |
|                 // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
 | |
|                 return chr(0xE0 | (($bytes >> 12) & 0x0F))
 | |
|                      . chr(0x80 | (($bytes >> 6) & 0x3F))
 | |
|                      . chr(0x80 | ($bytes & 0x3F));
 | |
|         }
 | |
| 
 | |
|         // ignoring UTF-32 for now, sorry
 | |
|         return '';
 | |
|     }
 | |
| }
 | |
| 
 |