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