1 : /*
2 : +----------------------------------------------------------------------+
3 : | PHP Version 5 |
4 : +----------------------------------------------------------------------+
5 : | Copyright (c) 1997-2009 The PHP Group |
6 : +----------------------------------------------------------------------+
7 : | This source file is subject to version 3.01 of the PHP license, |
8 : | that is bundled with this package in the file LICENSE, and is |
9 : | available through the world-wide-web at the following url: |
10 : | http://www.php.net/license/3_01.txt |
11 : | If you did not receive a copy of the PHP license and are unable to |
12 : | obtain it through the world-wide-web, please send a note to |
13 : | license@php.net so we can mail you a copy immediately. |
14 : +----------------------------------------------------------------------+
15 : | Author: Omar Kilani <omar@php.net> |
16 : +----------------------------------------------------------------------+
17 : */
18 :
19 : /* $Id: json.c 282593 2009-06-22 18:41:13Z stas $ */
20 :
21 : #ifdef HAVE_CONFIG_H
22 : #include "config.h"
23 : #endif
24 :
25 : #include "php.h"
26 : #include "php_ini.h"
27 : #include "ext/standard/info.h"
28 : #include "ext/standard/php_smart_str.h"
29 : #include "utf8_to_utf16.h"
30 : #include "JSON_parser.h"
31 : #include "php_json.h"
32 :
33 : static PHP_MINFO_FUNCTION(json);
34 :
35 : static PHP_FUNCTION(json_encode);
36 : static PHP_FUNCTION(json_decode);
37 :
38 : static const char digits[] = "0123456789abcdef";
39 :
40 : /* {{{ json_functions[]
41 : *
42 : * Every user visible function must have an entry in json_functions[].
43 : */
44 : static function_entry json_functions[] = {
45 : PHP_FE(json_encode, NULL)
46 : PHP_FE(json_decode, NULL)
47 : {NULL, NULL, NULL} /* Must be the last line in json_functions[] */
48 : };
49 : /* }}} */
50 :
51 : /* {{{ json_module_entry
52 : */
53 : zend_module_entry json_module_entry = {
54 : #if ZEND_MODULE_API_NO >= 20010901
55 : STANDARD_MODULE_HEADER,
56 : #endif
57 : "json",
58 : json_functions,
59 : NULL,
60 : NULL,
61 : NULL,
62 : NULL,
63 : PHP_MINFO(json),
64 : #if ZEND_MODULE_API_NO >= 20010901
65 : PHP_JSON_VERSION,
66 : #endif
67 : STANDARD_MODULE_PROPERTIES
68 : };
69 : /* }}} */
70 :
71 : #ifdef COMPILE_DL_JSON
72 : ZEND_GET_MODULE(json)
73 : #endif
74 :
75 : /* {{{ PHP_MINFO_FUNCTION
76 : */
77 : static PHP_MINFO_FUNCTION(json)
78 6 : {
79 6 : php_info_print_table_start();
80 6 : php_info_print_table_row(2, "json support", "enabled");
81 6 : php_info_print_table_row(2, "json version", PHP_JSON_VERSION);
82 6 : php_info_print_table_end();
83 6 : }
84 : /* }}} */
85 :
86 : static void json_escape_string(smart_str *buf, char *s, int len TSRMLS_DC);
87 :
88 : static int json_determine_array_type(zval **val TSRMLS_DC) /* {{{ */
89 95 : {
90 : int i;
91 95 : HashTable *myht = HASH_OF(*val);
92 :
93 95 : i = myht ? zend_hash_num_elements(myht) : 0;
94 95 : if (i > 0) {
95 : char *key;
96 : ulong index, idx;
97 : uint key_len;
98 : HashPosition pos;
99 :
100 81 : zend_hash_internal_pointer_reset_ex(myht, &pos);
101 81 : idx = 0;
102 190 : for (;; zend_hash_move_forward_ex(myht, &pos)) {
103 271 : i = zend_hash_get_current_key_ex(myht, &key, &key_len, &index, 0, &pos);
104 271 : if (i == HASH_KEY_NON_EXISTANT)
105 68 : break;
106 :
107 203 : if (i == HASH_KEY_IS_STRING) {
108 10 : return 1;
109 : } else {
110 193 : if (index != idx) {
111 3 : return 1;
112 : }
113 : }
114 190 : idx++;
115 190 : }
116 : }
117 :
118 82 : return 0;
119 : }
120 : /* }}} */
121 :
122 114 : static void json_encode_array(smart_str *buf, zval **val TSRMLS_DC) { /* {{{ */
123 : int i, r;
124 : HashTable *myht;
125 :
126 114 : if (Z_TYPE_PP(val) == IS_ARRAY) {
127 95 : myht = HASH_OF(*val);
128 95 : r = json_determine_array_type(val TSRMLS_CC);
129 : } else {
130 19 : myht = Z_OBJPROP_PP(val);
131 19 : r = 1;
132 : }
133 :
134 114 : if (myht && myht->nApplyCount > 1) {
135 2 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "recursion detected");
136 2 : smart_str_appendl(buf, "null", 4);
137 2 : return;
138 : }
139 :
140 112 : if (r == 0)
141 : {
142 81 : smart_str_appendc(buf, '[');
143 : }
144 : else
145 : {
146 31 : smart_str_appendc(buf, '{');
147 : }
148 :
149 112 : i = myht ? zend_hash_num_elements(myht) : 0;
150 112 : if (i > 0) {
151 : char *key;
152 : zval **data;
153 : ulong index;
154 : uint key_len;
155 : HashPosition pos;
156 : HashTable *tmp_ht;
157 94 : int need_comma = 0;
158 :
159 94 : zend_hash_internal_pointer_reset_ex(myht, &pos);
160 369 : for (;; zend_hash_move_forward_ex(myht, &pos)) {
161 463 : i = zend_hash_get_current_key_ex(myht, &key, &key_len, &index, 0, &pos);
162 463 : if (i == HASH_KEY_NON_EXISTANT)
163 94 : break;
164 :
165 369 : if (zend_hash_get_current_data_ex(myht, (void **) &data, &pos) == SUCCESS) {
166 369 : tmp_ht = HASH_OF(*data);
167 369 : if (tmp_ht) {
168 91 : tmp_ht->nApplyCount++;
169 : }
170 :
171 369 : if (r == 0) {
172 188 : if (need_comma) {
173 121 : smart_str_appendc(buf, ',');
174 : } else {
175 67 : need_comma = 1;
176 : }
177 :
178 188 : php_json_encode(buf, *data TSRMLS_CC);
179 181 : } else if (r == 1) {
180 181 : if (i == HASH_KEY_IS_STRING) {
181 169 : if (key[0] == '\0' && Z_TYPE_PP(val) == IS_OBJECT) {
182 : /* Skip protected and private members. */
183 1 : if (tmp_ht) {
184 1 : tmp_ht->nApplyCount--;
185 : }
186 1 : continue;
187 : }
188 :
189 168 : if (need_comma) {
190 146 : smart_str_appendc(buf, ',');
191 : } else {
192 22 : need_comma = 1;
193 : }
194 :
195 168 : json_escape_string(buf, key, key_len - 1 TSRMLS_CC);
196 168 : smart_str_appendc(buf, ':');
197 :
198 168 : php_json_encode(buf, *data TSRMLS_CC);
199 : } else {
200 12 : if (need_comma) {
201 8 : smart_str_appendc(buf, ',');
202 : } else {
203 4 : need_comma = 1;
204 : }
205 :
206 12 : smart_str_appendc(buf, '"');
207 12 : smart_str_append_long(buf, (long) index);
208 12 : smart_str_appendc(buf, '"');
209 12 : smart_str_appendc(buf, ':');
210 :
211 12 : php_json_encode(buf, *data TSRMLS_CC);
212 : }
213 : }
214 :
215 368 : if (tmp_ht) {
216 90 : tmp_ht->nApplyCount--;
217 : }
218 : }
219 369 : }
220 : }
221 :
222 112 : if (r == 0)
223 : {
224 81 : smart_str_appendc(buf, ']');
225 : }
226 : else
227 : {
228 31 : smart_str_appendc(buf, '}');
229 : }
230 : }
231 : /* }}} */
232 :
233 : #define REVERSE16(us) (((us & 0xf) << 12) | (((us >> 4) & 0xf) << 8) | (((us >> 8) & 0xf) << 4) | ((us >> 12) & 0xf))
234 :
235 : static void json_escape_string(smart_str *buf, char *s, int len TSRMLS_DC) /* {{{ */
236 314 : {
237 314 : int pos = 0;
238 : unsigned short us;
239 : unsigned short *utf16;
240 :
241 314 : if (len == 0)
242 : {
243 9 : smart_str_appendl(buf, "\"\"", 2);
244 9 : return;
245 : }
246 :
247 305 : utf16 = (unsigned short *) safe_emalloc(len, sizeof(unsigned short), 0);
248 :
249 305 : len = utf8_to_utf16(utf16, s, len);
250 305 : if (len <= 0)
251 : {
252 4 : if (utf16)
253 : {
254 4 : efree(utf16);
255 : }
256 4 : if(len < 0) {
257 4 : if(!PG(display_errors)) {
258 0 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid UTF-8 sequence in argument");
259 : }
260 4 : smart_str_appendl(buf, "null", 4);
261 : } else {
262 0 : smart_str_appendl(buf, "\"\"", 2);
263 : }
264 4 : return;
265 : }
266 :
267 301 : smart_str_appendc(buf, '"');
268 :
269 3412 : while(pos < len)
270 : {
271 2810 : us = utf16[pos++];
272 :
273 2810 : switch (us)
274 : {
275 : case '"':
276 : {
277 32 : smart_str_appendl(buf, "\\\"", 2);
278 : }
279 32 : break;
280 : case '\\':
281 : {
282 8 : smart_str_appendl(buf, "\\\\", 2);
283 : }
284 8 : break;
285 : case '/':
286 : {
287 48 : smart_str_appendl(buf, "\\/", 2);
288 : }
289 48 : break;
290 : case '\b':
291 : {
292 8 : smart_str_appendl(buf, "\\b", 2);
293 : }
294 8 : break;
295 : case '\f':
296 : {
297 8 : smart_str_appendl(buf, "\\f", 2);
298 : }
299 8 : break;
300 : case '\n':
301 : {
302 9 : smart_str_appendl(buf, "\\n", 2);
303 : }
304 9 : break;
305 : case '\r':
306 : {
307 8 : smart_str_appendl(buf, "\\r", 2);
308 : }
309 8 : break;
310 : case '\t':
311 : {
312 10 : smart_str_appendl(buf, "\\t", 2);
313 : }
314 10 : break;
315 : default:
316 : {
317 5216 : if (us >= ' ' && (us & 127) == us)
318 : {
319 2537 : smart_str_appendc(buf, (unsigned char) us);
320 : }
321 : else
322 : {
323 142 : smart_str_appendl(buf, "\\u", 2);
324 142 : us = REVERSE16(us);
325 :
326 142 : smart_str_appendc(buf, digits[us & ((1 << 4) - 1)]);
327 142 : us >>= 4;
328 142 : smart_str_appendc(buf, digits[us & ((1 << 4) - 1)]);
329 142 : us >>= 4;
330 142 : smart_str_appendc(buf, digits[us & ((1 << 4) - 1)]);
331 142 : us >>= 4;
332 142 : smart_str_appendc(buf, digits[us & ((1 << 4) - 1)]);
333 : }
334 : }
335 : break;
336 : }
337 : }
338 :
339 301 : smart_str_appendc(buf, '"');
340 301 : efree(utf16);
341 : }
342 : /* }}} */
343 :
344 : PHP_JSON_API void php_json_encode(smart_str *buf, zval *val TSRMLS_DC) /* {{{ */
345 445 : {
346 445 : switch (Z_TYPE_P(val)) {
347 : case IS_NULL:
348 14 : smart_str_appendl(buf, "null", 4);
349 14 : break;
350 : case IS_BOOL:
351 22 : if (Z_BVAL_P(val))
352 : {
353 12 : smart_str_appendl(buf, "true", 4);
354 : }
355 : else
356 : {
357 10 : smart_str_appendl(buf, "false", 5);
358 : }
359 22 : break;
360 : case IS_LONG:
361 109 : smart_str_append_long(buf, Z_LVAL_P(val));
362 109 : break;
363 : case IS_DOUBLE:
364 : {
365 39 : char *d = NULL;
366 : int len;
367 39 : double dbl = Z_DVAL_P(val);
368 :
369 74 : if (!zend_isinf(dbl) && !zend_isnan(dbl)) {
370 35 : len = spprintf(&d, 0, "%.*k", (int) EG(precision), dbl);
371 35 : smart_str_appendl(buf, d, len);
372 35 : efree(d);
373 : } else {
374 4 : zend_error(E_WARNING, "[json] (php_json_encode) double %.9g does not conform to the JSON spec, encoded as 0", dbl);
375 4 : smart_str_appendc(buf, '0');
376 : }
377 : }
378 39 : break;
379 : case IS_STRING:
380 146 : json_escape_string(buf, Z_STRVAL_P(val), Z_STRLEN_P(val) TSRMLS_CC);
381 146 : break;
382 : case IS_ARRAY:
383 : case IS_OBJECT:
384 114 : json_encode_array(buf, &val TSRMLS_CC);
385 114 : break;
386 : default:
387 1 : zend_error(E_WARNING, "[json] (php_json_encode) type is unsupported, encoded as null");
388 1 : smart_str_appendl(buf, "null", 4);
389 : break;
390 : }
391 :
392 : return;
393 : }
394 : /* }}} */
395 :
396 : PHP_JSON_API void php_json_decode(zval *return_value, char *buf, int buf_len, zend_bool assoc TSRMLS_DC) /* {{{ */
397 129 : {
398 : unsigned short *utf16;
399 : int utf16_len;
400 : zval *z;
401 :
402 129 : utf16 = (unsigned short *) safe_emalloc((buf_len+1), sizeof(unsigned short), 1);
403 :
404 129 : utf16_len = utf8_to_utf16(utf16, buf, buf_len);
405 129 : if (utf16_len <= 0)
406 : {
407 0 : if (utf16)
408 : {
409 0 : efree(utf16);
410 : }
411 :
412 0 : RETURN_NULL();
413 : }
414 :
415 129 : ALLOC_INIT_ZVAL(z);
416 129 : if (JSON_parser(z, utf16, utf16_len, assoc TSRMLS_CC))
417 : {
418 49 : *return_value = *z;
419 :
420 49 : FREE_ZVAL(z);
421 49 : efree(utf16);
422 : }
423 : else
424 : {
425 : double d;
426 : int type;
427 : long p;
428 :
429 80 : zval_dtor(z);
430 80 : FREE_ZVAL(z);
431 80 : efree(utf16);
432 :
433 80 : if (buf_len == 4) {
434 10 : if (!strcasecmp(buf, "null")) {
435 2 : RETURN_NULL();
436 8 : } else if (!strcasecmp(buf, "true")) {
437 2 : RETURN_BOOL(1);
438 : }
439 70 : } else if (buf_len == 5 && !strcasecmp(buf, "false")) {
440 2 : RETURN_BOOL(0);
441 : }
442 74 : if ((type = is_numeric_string(buf, buf_len, &p, &d, 0)) != 0) {
443 17 : if (type == IS_LONG) {
444 14 : RETURN_LONG(p);
445 3 : } else if (type == IS_DOUBLE) {
446 3 : RETURN_DOUBLE(d);
447 : }
448 : }
449 57 : RETURN_NULL();
450 : }
451 : }
452 : /* }}} */
453 :
454 : /* {{{ proto string json_encode(mixed data)
455 : Returns the JSON representation of a value */
456 : static PHP_FUNCTION(json_encode)
457 79 : {
458 : zval *parameter;
459 79 : smart_str buf = {0};
460 :
461 79 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", ¶meter) == FAILURE) {
462 2 : return;
463 : }
464 :
465 77 : php_json_encode(&buf, parameter TSRMLS_CC);
466 :
467 77 : ZVAL_STRINGL(return_value, buf.c, buf.len, 1);
468 :
469 77 : smart_str_free(&buf);
470 : }
471 : /* }}} */
472 :
473 : /* {{{ proto mixed json_decode(string json [, bool assoc])
474 : Decodes the JSON representation into a PHP value */
475 : static PHP_FUNCTION(json_decode)
476 136 : {
477 : char *parameter;
478 : int parameter_len;
479 136 : zend_bool assoc = 0; /* return JS objects as PHP objects by default */
480 :
481 136 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|b", ¶meter, ¶meter_len, &assoc) == FAILURE) {
482 3 : return;
483 : }
484 :
485 133 : if (!parameter_len)
486 : {
487 4 : RETURN_NULL();
488 : }
489 :
490 129 : php_json_decode(return_value, parameter, parameter_len, assoc TSRMLS_CC);
491 : }
492 : /* }}} */
493 :
494 : /*
495 : * Local variables:
496 : * tab-width: 4
497 : * c-basic-offset: 4
498 : * End:
499 : * vim600: noet sw=4 ts=4
500 : * vim<600: noet sw=4 ts=4
501 : */
|