includes/json2.php
changeset 334 c72b545f1304
child 880 218b6d4de908
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/includes/json2.php	Wed Dec 26 00:37:26 2007 -0500
@@ -0,0 +1,1015 @@
+<?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-2007 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+
+
+/**
+ * Encode PHP constructs to JSON
+ *
+ * @category   Zend
+ * @package    Zend_Json
+ * @copyright  Copyright (c) 2005-2007 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Json_Encoder
+{
+    /**
+     * Whether or not to check for possible cycling
+     *
+     * @var boolean
+     */
+    protected $_cycleCheck;
+
+    /**
+     * Array of visited objects; used to prevent cycling.
+     *
+     * @var array
+     */
+    protected $_visited = array();
+
+    /**
+     * Constructor
+     *
+     * @param boolean $cycleCheck Whether or not to check for recursion when encoding
+     * @return void
+     */
+    protected function __construct($cycleCheck = false)
+    {
+        $this->_cycleCheck = $cycleCheck;
+    }
+
+    /**
+     * Use the JSON encoding scheme for the value specified
+     *
+     * @param mixed $value The value to be encoded
+     * @param boolean $cycleCheck Whether or not to check for possible object recursion when encoding
+     * @return string  The encoded value
+     */
+    public static function encode($value, $cycleCheck = false)
+    {
+        $encoder = new Zend_Json_Encoder(($cycleCheck) ? true : false);
+
+        return $encoder->_encodeValue($value);
+    }
+
+    /**
+     * Recursive driver which determines the type of value to be encoded
+     * and then dispatches to the appropriate method. $values are either
+     *    - objects (returns from {@link _encodeObject()})
+     *    - arrays (returns from {@link _encodeArray()})
+     *    - basic datums (e.g. numbers or strings) (returns from {@link _encodeDatum()})
+     *
+     * @param $value mixed The value to be encoded
+     * @return string Encoded value
+     */
+    protected function _encodeValue(&$value)
+    {
+        if (is_object($value)) {
+            return $this->_encodeObject($value);
+        } else if (is_array($value)) {
+            return $this->_encodeArray($value);
+        }
+
+        return $this->_encodeDatum($value);
+    }
+
+
+
+    /**
+     * Encode an object to JSON by encoding each of the public properties
+     *
+     * A special property is added to the JSON object called '__className'
+     * that contains the name of the class of $value. This is used to decode
+     * the object on the client into a specific class.
+     *
+     * @param $value object
+     * @return string
+     * @throws Zend_Json_Exception If recursive checks are enabled and the object has been serialized previously
+     */
+    protected function _encodeObject(&$value)
+    {
+        if ($this->_cycleCheck) {
+            if ($this->_wasVisited($value)) {
+                throw new Zend_Json_Exception(
+                    'Cycles not supported in JSON encoding, cycle introduced by '
+                    . 'class "' . get_class($value) . '"'
+                );
+            }
+
+            $this->_visited[] = $value;
+        }
+
+        $props = '';
+        foreach (get_object_vars($value) as $name => $propValue) {
+            if (isset($propValue)) {
+                $props .= ','
+                        . $this->_encodeValue($name)
+                        . ':'
+                        . $this->_encodeValue($propValue);
+            }
+        }
+
+        return '{"__className":"' . get_class($value) . '"'
+                . $props . '}';
+    }
+
+
+    /**
+     * Determine if an object has been serialized already
+     *
+     * @param mixed $value
+     * @return boolean
+     */
+    protected function _wasVisited(&$value)
+    {
+        if (in_array($value, $this->_visited, true)) {
+            return true;
+        }
+
+        return false;
+    }
+
+
+    /**
+     * JSON encode an array value
+     *
+     * Recursively encodes each value of an array and returns a JSON encoded
+     * array string.
+     *
+     * Arrays are defined as integer-indexed arrays starting at index 0, where
+     * the last index is (count($array) -1); any deviation from that is
+     * considered an associative array, and will be encoded as such.
+     *
+     * @param $array array
+     * @return string
+     */
+    protected function _encodeArray(&$array)
+    {
+        $tmpArray = array();
+
+        // Check for associative array
+        if (!empty($array) && (array_keys($array) !== range(0, count($array) - 1))) {
+            // Associative array
+            $result = '{';
+            foreach ($array as $key => $value) {
+                $key = (string) $key;
+                $tmpArray[] = $this->_encodeString($key)
+                            . ':'
+                            . $this->_encodeValue($value);
+            }
+            $result .= implode(',', $tmpArray);
+            $result .= '}';
+        } else {
+            // Indexed array
+            $result = '[';
+            $length = count($array);
+            for ($i = 0; $i < $length; $i++) {
+                $tmpArray[] = $this->_encodeValue($array[$i]);
+            }
+            $result .= implode(',', $tmpArray);
+            $result .= ']';
+        }
+
+        return $result;
+    }
+
+
+    /**
+     * JSON encode a basic data type (string, number, boolean, null)
+     *
+     * If value type is not a string, number, boolean, or null, the string
+     * 'null' is returned.
+     *
+     * @param $value mixed
+     * @return string
+     */
+    protected function _encodeDatum(&$value)
+    {
+        $result = 'null';
+
+        if (is_int($value) || is_float($value)) {
+            $result = (string)$value;
+        } elseif (is_string($value)) {
+            $result = $this->_encodeString($value);
+        } elseif (is_bool($value)) {
+            $result = $value ? 'true' : 'false';
+        }
+
+        return $result;
+    }
+
+
+    /**
+     * JSON encode a string value by escaping characters as necessary
+     *
+     * @param $value string
+     * @return string
+     */
+    protected function _encodeString(&$string)
+    {
+        // Escape these characters with a backslash:
+        // " \ / \n \r \t \b \f
+        $search  = array('\\', "\n", "\t", "\r", "\b", "\f", '"');
+        $replace = array('\\\\', '\\n', '\\t', '\\r', '\\b', '\\f', '\"');
+        $string  = str_replace($search, $replace, $string);
+
+        // Escape certain ASCII characters:
+        // 0x08 => \b
+        // 0x0c => \f
+        $string = str_replace(array(chr(0x08), chr(0x0C)), array('\b', '\f'), $string);
+
+        return '"' . $string . '"';
+    }
+
+
+    /**
+     * Encode the constants associated with the ReflectionClass
+     * parameter. The encoding format is based on the class2 format
+     *
+     * @param $cls ReflectionClass
+     * @return string Encoded constant block in class2 format
+     */
+    private static function _encodeConstants(ReflectionClass $cls)
+    {
+        $result    = "constants : {";
+        $constants = $cls->getConstants();
+
+        $tmpArray = array();
+        if (!empty($constants)) {
+            foreach ($constants as $key => $value) {
+                $tmpArray[] = "$key: " . self::encode($value);
+            }
+
+            $result .= implode(', ', $tmpArray);
+        }
+
+        return $result . "}";
+    }
+
+
+    /**
+     * Encode the public methods of the ReflectionClass in the
+     * class2 format
+     *
+     * @param $cls ReflectionClass
+     * @return string Encoded method fragment
+     *
+     */
+    private static function _encodeMethods(ReflectionClass $cls)
+    {
+        $methods = $cls->getMethods();
+        $result = 'methods:{';
+
+        $started = false;
+        foreach ($methods as $method) {
+            if (! $method->isPublic() || !$method->isUserDefined()) {
+                continue;
+            }
+
+            if ($started) {
+                $result .= ',';
+            }
+            $started = true;
+
+            $result .= '' . $method->getName(). ':function(';
+
+            if ('__construct' != $method->getName()) {
+                $parameters  = $method->getParameters();
+                $paramCount  = count($parameters);
+                $argsStarted = false;
+
+                $argNames = "var argNames=[";
+                foreach ($parameters as $param) {
+                    if ($argsStarted) {
+                        $result .= ',';
+                    }
+
+                    $result .= $param->getName();
+
+                    if ($argsStarted) {
+                        $argNames .= ',';
+                    }
+
+                    $argNames .= '"' . $param->getName() . '"';
+
+                    $argsStarted = true;
+                }
+                $argNames .= "];";
+
+                $result .= "){"
+                         . $argNames
+                         . 'var result = ZAjaxEngine.invokeRemoteMethod('
+                         . "this, '" . $method->getName()
+                         . "',argNames,arguments);"
+                         . 'return(result);}';
+            } else {
+                $result .= "){}";
+            }
+        }
+
+        return $result . "}";
+    }
+
+
+    /**
+     * Encode the public properties of the ReflectionClass in the class2
+     * format.
+     *
+     * @param $cls ReflectionClass
+     * @return string Encode properties list
+     *
+     */
+    private static function _encodeVariables(ReflectionClass $cls)
+    {
+        $properties = $cls->getProperties();
+        $propValues = get_class_vars($cls->getName());
+        $result = "variables:{";
+        $cnt = 0;
+
+        $tmpArray = array();
+        foreach ($properties as $prop) {
+            if (! $prop->isPublic()) {
+                continue;
+            }
+
+            $tmpArray[] = $prop->getName()
+                        . ':'
+                        . self::encode($propValues[$prop->getName()]);
+        }
+        $result .= implode(',', $tmpArray);
+
+        return $result . "}";
+    }
+
+    /**
+     * Encodes the given $className into the class2 model of encoding PHP
+     * classes into JavaScript class2 classes.
+     * NOTE: Currently only public methods and variables are proxied onto
+     * the client machine
+     *
+     * @param $className string The name of the class, the class must be
+     * instantiable using a null constructor
+     * @param $package string Optional package name appended to JavaScript
+     * proxy class name
+     * @return string The class2 (JavaScript) encoding of the class
+     * @throws Zend_Json_Exception
+     */
+    public static function encodeClass($className, $package = '')
+    {
+        $cls = new ReflectionClass($className);
+        if (! $cls->isInstantiable()) {
+            throw new Zend_Json_Exception("$className must be instantiable");
+        }
+
+        return "Class.create('$package$className',{"
+                . self::_encodeConstants($cls)    .","
+                . self::_encodeMethods($cls)      .","
+                . self::_encodeVariables($cls)    .'});';
+    }
+
+
+    /**
+     * Encode several classes at once
+     *
+     * Returns JSON encoded classes, using {@link encodeClass()}.
+     *
+     * @param array $classNames
+     * @param string $package
+     * @return string
+     */
+    public static function encodeClasses(array $classNames, $package = '')
+    {
+        $result = '';
+        foreach ($classNames as $className) {
+            $result .= self::encodeClass($className, $package);
+        }
+
+        return $result;
+    }
+
+}
+
+/**
+ * Decode JSON encoded string to PHP variable constructs
+ *
+ * @category   Zend
+ * @package    Zend_Json
+ * @copyright  Copyright (c) 2005-2007 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)
+    {
+        
+        // eliminate comments
+        $source = preg_replace(array(
+
+                  // eliminate single line comments in '// ...' form
+                  '#^\s*//(.+)$#m',
+    
+                  // eliminate multi-line comments in '/* ... */' form, at start of string
+                  '#^\s*/\*(.+)\*/#Us',
+    
+                  // eliminate multi-line comments in '/* ... */' form, at end of string
+                  '#/\*(.+)\*/\s*$#Us'
+    
+              ), '', $source);
+        
+        // Set defaults
+        $this->_source       = $source;
+        $this->_sourceLength = strlen($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) {
+            throw new Zend_Json_Exception('Must specify JSON encoded source for decoding');
+        } elseif (!is_string($source)) {
+            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 ZJsonEnoder or ZJAjax 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)) {
+                throw new Zend_Json_Exception('Missing key in object encoding: ' . $this->_source);
+            }
+
+            $key = $this->_tokenValue;
+            $tok = $this->_getNextToken();
+
+            if ($tok != self::COLON) {
+                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) {
+                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) {
+                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 .= chr(8);
+                                break;
+                            case 'f' :
+                                $result .= chr(12);
+                                break;
+                            case 'n' :
+                                $result .= chr(10);
+                                break;
+                            case 'r' :
+                                $result .= chr(13);
+                                break;
+                            case 't' :
+                                $result .= chr(9);
+                                break;
+                            case '\'' :
+                                $result .= '\'';
+                                break;
+                            default:
+                                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  "'":
+                $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 .= chr(8);
+                                break;
+                            case 'f' :
+                                $result .= chr(12);
+                                break;
+                            case 'n' :
+                                $result .= chr(10);
+                                break;
+                            case 'r' :
+                                $result .= chr(13);
+                                break;
+                            case 't' :
+                                $result .= chr(9);
+                                break;
+                            case '"' :
+                                $result .= '"';
+                                break;
+                            default:
+                                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;
+              case ' ':
+                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)) {
+                        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 {
+                    throw new Zend_Json_Exception("Illegal number format: $datum");
+                }
+
+                $this->_token = self::DATUM;
+                $this->_offset = $start + strlen($datum);
+            }
+        } else {
+            throw new Zend_Json_Exception("Illegal Token at pos $i: $chr\nContext:\n--------------------------------------------------" . substr($str, $i) . "\n--------------------------------------------------");
+        }
+
+        return($this->_token);
+    }
+}
+
+/**
+ * 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-2007 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+
+
+/**
+ * @category   Zend
+ * @package    Zend_Json
+ * @copyright  Copyright (c) 2005-2007 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Json_Exception extends Zend_Exception
+{}
+
+/**
+ * @category   Zend
+ * @package    Zend
+ * @copyright  Copyright (c) 2005-2007 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Exception extends Exception
+{}
+
+/**
+ * Class for encoding to and decoding from JSON.
+ *
+ * @category   Zend
+ * @package    Zend_Json
+ * @copyright  Copyright (c) 2005-2007 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Json
+{
+    /**
+     * How objects should be encoded -- arrays or as StdClass. TYPE_ARRAY is 1
+     * so that it is a boolean true value, allowing it to be used with
+     * ext/json's functions.
+     */
+    const TYPE_ARRAY  = 1;
+    const TYPE_OBJECT = 0;
+
+    /**
+     * @var bool
+     */
+    public static $useBuiltinEncoderDecoder = true;
+
+    /**
+     * Decodes the given $encodedValue string which is
+     * encoded in the JSON format
+     *
+     * Uses ext/json's json_decode if available.
+     *
+     * @param string $encodedValue Encoded in JSON format
+     * @param int $objectDecodeType Optional; flag indicating how to decode
+     * objects. See {@link ZJsonDecoder::decode()} for details.
+     * @return mixed
+     */
+    public static function decode($encodedValue, $objectDecodeType = Zend_Json::TYPE_ARRAY)
+    {
+        if (function_exists('json_decode') && self::$useBuiltinEncoderDecoder !== true) {
+            return json_decode($encodedValue, $objectDecodeType);
+        }
+
+        return Zend_Json_Decoder::decode($encodedValue, $objectDecodeType);
+    }
+
+
+    /**
+     * Encode the mixed $valueToEncode into the JSON format
+     *
+     * Encodes using ext/json's json_encode() if available.
+     *
+     * NOTE: Object should not contain cycles; the JSON format
+     * does not allow object reference.
+     *
+     * NOTE: Only public variables will be encoded
+     *
+     * @param mixed $valueToEncode
+     * @param boolean $cycleCheck Optional; whether or not to check for object recursion; off by default
+     * @return string JSON encoded object
+     */
+    public static function encode($valueToEncode, $cycleCheck = false)
+    {
+        if (function_exists('json_encode') && self::$useBuiltinEncoderDecoder !== true) {
+            return json_encode($valueToEncode);
+        }
+
+        return Zend_Json_Encoder::encode($valueToEncode, $cycleCheck);
+    }
+}
+
+?>