1 | /* |
2 | +----------------------------------------------------------------------+ |
3 | | PHP Version 5 | |
4 | +----------------------------------------------------------------------+ |
5 | | Copyright (c) 1997-2015 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 | | Authors: Jani Lehtimäki <jkl@njet.net> | |
16 | | Thies C. Arntzen <thies@thieso.net> | |
17 | | Sascha Schumann <sascha@schumann.cx> | |
18 | +----------------------------------------------------------------------+ |
19 | */ |
20 | |
21 | /* $Id$ */ |
22 | |
23 | /* {{{ includes |
24 | */ |
25 | #include <stdio.h> |
26 | #include <stdlib.h> |
27 | #include <errno.h> |
28 | #include "php.h" |
29 | #include "php_string.h" |
30 | #include "php_var.h" |
31 | #include "php_smart_str.h" |
32 | #include "basic_functions.h" |
33 | #include "php_incomplete_class.h" |
34 | |
35 | #define COMMON (Z_ISREF_PP(struc) ? "&" : "") |
36 | /* }}} */ |
37 | |
38 | static int php_array_element_dump(zval **zv TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key) /* {{{ */ |
39 | { |
40 | int level; |
41 | |
42 | level = va_arg(args, int); |
43 | |
44 | if (hash_key->nKeyLength == 0) { /* numeric key */ |
45 | php_printf("%*c[%ld]=>\n" , level + 1, ' ', hash_key->h); |
46 | } else { /* string key */ |
47 | php_printf("%*c[\"" , level + 1, ' '); |
48 | PHPWRITE(hash_key->arKey, hash_key->nKeyLength - 1); |
49 | php_printf("\"]=>\n" ); |
50 | } |
51 | php_var_dump(zv, level + 2 TSRMLS_CC); |
52 | return 0; |
53 | } |
54 | /* }}} */ |
55 | |
56 | static int php_object_property_dump(zval **zv TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key) /* {{{ */ |
57 | { |
58 | int level; |
59 | const char *prop_name, *class_name; |
60 | |
61 | level = va_arg(args, int); |
62 | |
63 | if (hash_key->nKeyLength == 0) { /* numeric key */ |
64 | php_printf("%*c[%ld]=>\n" , level + 1, ' ', hash_key->h); |
65 | } else { /* string key */ |
66 | int unmangle = zend_unmangle_property_name(hash_key->arKey, hash_key->nKeyLength - 1, &class_name, &prop_name); |
67 | php_printf("%*c[" , level + 1, ' '); |
68 | |
69 | if (class_name && unmangle == SUCCESS) { |
70 | if (class_name[0] == '*') { |
71 | php_printf("\"%s\":protected" , prop_name); |
72 | } else { |
73 | php_printf("\"%s\":\"%s\":private" , prop_name, class_name); |
74 | } |
75 | } else { |
76 | php_printf("\"" ); |
77 | PHPWRITE(hash_key->arKey, hash_key->nKeyLength - 1); |
78 | php_printf("\"" ); |
79 | } |
80 | ZEND_PUTS("]=>\n" ); |
81 | } |
82 | php_var_dump(zv, level + 2 TSRMLS_CC); |
83 | return 0; |
84 | } |
85 | /* }}} */ |
86 | |
87 | PHPAPI void php_var_dump(zval **struc, int level TSRMLS_DC) /* {{{ */ |
88 | { |
89 | HashTable *myht; |
90 | const char *class_name; |
91 | zend_uint class_name_len; |
92 | int (*php_element_dump_func)(zval** TSRMLS_DC, int, va_list, zend_hash_key*); |
93 | int is_temp; |
94 | |
95 | if (level > 1) { |
96 | php_printf("%*c" , level - 1, ' '); |
97 | } |
98 | |
99 | switch (Z_TYPE_PP(struc)) { |
100 | case IS_BOOL: |
101 | php_printf("%sbool(%s)\n" , COMMON, Z_LVAL_PP(struc) ? "true" : "false" ); |
102 | break; |
103 | case IS_NULL: |
104 | php_printf("%sNULL\n" , COMMON); |
105 | break; |
106 | case IS_LONG: |
107 | php_printf("%sint(%ld)\n" , COMMON, Z_LVAL_PP(struc)); |
108 | break; |
109 | case IS_DOUBLE: |
110 | php_printf("%sfloat(%.*G)\n" , COMMON, (int) EG(precision), Z_DVAL_PP(struc)); |
111 | break; |
112 | case IS_STRING: |
113 | php_printf("%sstring(%d) \"" , COMMON, Z_STRLEN_PP(struc)); |
114 | PHPWRITE(Z_STRVAL_PP(struc), Z_STRLEN_PP(struc)); |
115 | PUTS("\"\n" ); |
116 | break; |
117 | case IS_ARRAY: |
118 | myht = Z_ARRVAL_PP(struc); |
119 | if (++myht->nApplyCount > 1) { |
120 | PUTS("*RECURSION*\n" ); |
121 | --myht->nApplyCount; |
122 | return; |
123 | } |
124 | php_printf("%sarray(%d) {\n" , COMMON, zend_hash_num_elements(myht)); |
125 | php_element_dump_func = php_array_element_dump; |
126 | is_temp = 0; |
127 | goto head_done; |
128 | case IS_OBJECT: |
129 | myht = Z_OBJDEBUG_PP(struc, is_temp); |
130 | if (myht && ++myht->nApplyCount > 1) { |
131 | PUTS("*RECURSION*\n" ); |
132 | --myht->nApplyCount; |
133 | return; |
134 | } |
135 | |
136 | if (Z_OBJ_HANDLER(**struc, get_class_name)) { |
137 | Z_OBJ_HANDLER(**struc, get_class_name)(*struc, &class_name, &class_name_len, 0 TSRMLS_CC); |
138 | php_printf("%sobject(%s)#%d (%d) {\n" , COMMON, class_name, Z_OBJ_HANDLE_PP(struc), myht ? zend_hash_num_elements(myht) : 0); |
139 | efree((char*)class_name); |
140 | } else { |
141 | php_printf("%sobject(unknown class)#%d (%d) {\n" , COMMON, Z_OBJ_HANDLE_PP(struc), myht ? zend_hash_num_elements(myht) : 0); |
142 | } |
143 | php_element_dump_func = php_object_property_dump; |
144 | head_done: |
145 | if (myht) { |
146 | zend_hash_apply_with_arguments(myht TSRMLS_CC, (apply_func_args_t) php_element_dump_func, 1, level); |
147 | --myht->nApplyCount; |
148 | if (is_temp) { |
149 | zend_hash_destroy(myht); |
150 | efree(myht); |
151 | } |
152 | } |
153 | if (level > 1) { |
154 | php_printf("%*c" , level-1, ' '); |
155 | } |
156 | PUTS("}\n" ); |
157 | break; |
158 | case IS_RESOURCE: { |
159 | const char *type_name = zend_rsrc_list_get_rsrc_type(Z_LVAL_PP(struc) TSRMLS_CC); |
160 | php_printf("%sresource(%ld) of type (%s)\n" , COMMON, Z_LVAL_PP(struc), type_name ? type_name : "Unknown" ); |
161 | break; |
162 | } |
163 | default: |
164 | php_printf("%sUNKNOWN:0\n" , COMMON); |
165 | break; |
166 | } |
167 | } |
168 | /* }}} */ |
169 | |
170 | /* {{{ proto void var_dump(mixed var) |
171 | Dumps a string representation of variable to output */ |
172 | PHP_FUNCTION(var_dump) |
173 | { |
174 | zval ***args; |
175 | int argc; |
176 | int i; |
177 | |
178 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "+" , &args, &argc) == FAILURE) { |
179 | return; |
180 | } |
181 | |
182 | for (i = 0; i < argc; i++) { |
183 | php_var_dump(args[i], 1 TSRMLS_CC); |
184 | } |
185 | efree(args); |
186 | } |
187 | /* }}} */ |
188 | |
189 | static int zval_array_element_dump(zval **zv TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key) /* {{{ */ |
190 | { |
191 | int level; |
192 | |
193 | level = va_arg(args, int); |
194 | |
195 | if (hash_key->nKeyLength == 0) { /* numeric key */ |
196 | php_printf("%*c[%ld]=>\n" , level + 1, ' ', hash_key->h); |
197 | } else { /* string key */ |
198 | /* XXX: perphaps when we are inside the class we should permit access to |
199 | * private & protected values |
200 | */ |
201 | if (va_arg(args, int) && hash_key->arKey[0] == '\0') { |
202 | return 0; |
203 | } |
204 | php_printf("%*c[\"" , level + 1, ' '); |
205 | PHPWRITE(hash_key->arKey, hash_key->nKeyLength - 1); |
206 | php_printf("\"]=>\n" ); |
207 | } |
208 | php_debug_zval_dump(zv, level + 2 TSRMLS_CC); |
209 | return 0; |
210 | } |
211 | /* }}} */ |
212 | |
213 | static int zval_object_property_dump(zval **zv TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key) /* {{{ */ |
214 | { |
215 | int level; |
216 | const char *prop_name, *class_name; |
217 | |
218 | level = va_arg(args, int); |
219 | |
220 | if (hash_key->nKeyLength == 0) { /* numeric key */ |
221 | php_printf("%*c[%ld]=>\n" , level + 1, ' ', hash_key->h); |
222 | } else { /* string key */ |
223 | zend_unmangle_property_name(hash_key->arKey, hash_key->nKeyLength - 1, &class_name, &prop_name); |
224 | php_printf("%*c[" , level + 1, ' '); |
225 | |
226 | if (class_name) { |
227 | if (class_name[0] == '*') { |
228 | php_printf("\"%s\":protected" , prop_name); |
229 | } else { |
230 | php_printf("\"%s\":\"%s\":private" , prop_name, class_name); |
231 | } |
232 | } else { |
233 | php_printf("\"%s\"" , prop_name); |
234 | } |
235 | ZEND_PUTS("]=>\n" ); |
236 | } |
237 | php_debug_zval_dump(zv, level + 2 TSRMLS_CC); |
238 | return 0; |
239 | } |
240 | /* }}} */ |
241 | |
242 | PHPAPI void php_debug_zval_dump(zval **struc, int level TSRMLS_DC) /* {{{ */ |
243 | { |
244 | HashTable *myht = NULL; |
245 | const char *class_name; |
246 | zend_uint class_name_len; |
247 | int (*zval_element_dump_func)(zval** TSRMLS_DC, int, va_list, zend_hash_key*); |
248 | int is_temp = 0; |
249 | |
250 | if (level > 1) { |
251 | php_printf("%*c" , level - 1, ' '); |
252 | } |
253 | |
254 | switch (Z_TYPE_PP(struc)) { |
255 | case IS_BOOL: |
256 | php_printf("%sbool(%s) refcount(%u)\n" , COMMON, Z_LVAL_PP(struc)?"true" :"false" , Z_REFCOUNT_PP(struc)); |
257 | break; |
258 | case IS_NULL: |
259 | php_printf("%sNULL refcount(%u)\n" , COMMON, Z_REFCOUNT_PP(struc)); |
260 | break; |
261 | case IS_LONG: |
262 | php_printf("%slong(%ld) refcount(%u)\n" , COMMON, Z_LVAL_PP(struc), Z_REFCOUNT_PP(struc)); |
263 | break; |
264 | case IS_DOUBLE: |
265 | php_printf("%sdouble(%.*G) refcount(%u)\n" , COMMON, (int) EG(precision), Z_DVAL_PP(struc), Z_REFCOUNT_PP(struc)); |
266 | break; |
267 | case IS_STRING: |
268 | php_printf("%sstring(%d) \"" , COMMON, Z_STRLEN_PP(struc)); |
269 | PHPWRITE(Z_STRVAL_PP(struc), Z_STRLEN_PP(struc)); |
270 | php_printf("\" refcount(%u)\n" , Z_REFCOUNT_PP(struc)); |
271 | break; |
272 | case IS_ARRAY: |
273 | myht = Z_ARRVAL_PP(struc); |
274 | if (myht->nApplyCount > 1) { |
275 | PUTS("*RECURSION*\n" ); |
276 | return; |
277 | } |
278 | php_printf("%sarray(%d) refcount(%u){\n" , COMMON, zend_hash_num_elements(myht), Z_REFCOUNT_PP(struc)); |
279 | zval_element_dump_func = zval_array_element_dump; |
280 | goto head_done; |
281 | case IS_OBJECT: |
282 | myht = Z_OBJDEBUG_PP(struc, is_temp); |
283 | if (myht && myht->nApplyCount > 1) { |
284 | PUTS("*RECURSION*\n" ); |
285 | return; |
286 | } |
287 | Z_OBJ_HANDLER_PP(struc, get_class_name)(*struc, &class_name, &class_name_len, 0 TSRMLS_CC); |
288 | php_printf("%sobject(%s)#%d (%d) refcount(%u){\n" , COMMON, class_name, Z_OBJ_HANDLE_PP(struc), myht ? zend_hash_num_elements(myht) : 0, Z_REFCOUNT_PP(struc)); |
289 | efree((char*)class_name); |
290 | zval_element_dump_func = zval_object_property_dump; |
291 | head_done: |
292 | if (myht) { |
293 | zend_hash_apply_with_arguments(myht TSRMLS_CC, (apply_func_args_t) zval_element_dump_func, 1, level, (Z_TYPE_PP(struc) == IS_ARRAY ? 0 : 1)); |
294 | if (is_temp) { |
295 | zend_hash_destroy(myht); |
296 | efree(myht); |
297 | } |
298 | } |
299 | if (level > 1) { |
300 | php_printf("%*c" , level - 1, ' '); |
301 | } |
302 | PUTS("}\n" ); |
303 | break; |
304 | case IS_RESOURCE: { |
305 | const char *type_name = zend_rsrc_list_get_rsrc_type(Z_LVAL_PP(struc) TSRMLS_CC); |
306 | php_printf("%sresource(%ld) of type (%s) refcount(%u)\n" , COMMON, Z_LVAL_PP(struc), type_name ? type_name : "Unknown" , Z_REFCOUNT_PP(struc)); |
307 | break; |
308 | } |
309 | default: |
310 | php_printf("%sUNKNOWN:0\n" , COMMON); |
311 | break; |
312 | } |
313 | } |
314 | /* }}} */ |
315 | |
316 | /* {{{ proto void debug_zval_dump(mixed var) |
317 | Dumps a string representation of an internal zend value to output. */ |
318 | PHP_FUNCTION(debug_zval_dump) |
319 | { |
320 | zval ***args; |
321 | int argc; |
322 | int i; |
323 | |
324 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "+" , &args, &argc) == FAILURE) { |
325 | return; |
326 | } |
327 | |
328 | for (i = 0; i < argc; i++) { |
329 | php_debug_zval_dump(args[i], 1 TSRMLS_CC); |
330 | } |
331 | efree(args); |
332 | } |
333 | /* }}} */ |
334 | |
335 | #define buffer_append_spaces(buf, num_spaces) \ |
336 | do { \ |
337 | char *tmp_spaces; \ |
338 | int tmp_spaces_len; \ |
339 | tmp_spaces_len = spprintf(&tmp_spaces, 0,"%*c", num_spaces, ' '); \ |
340 | smart_str_appendl(buf, tmp_spaces, tmp_spaces_len); \ |
341 | efree(tmp_spaces); \ |
342 | } while(0); |
343 | |
344 | static int php_array_element_export(zval **zv TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key) /* {{{ */ |
345 | { |
346 | int level; |
347 | smart_str *buf; |
348 | |
349 | level = va_arg(args, int); |
350 | buf = va_arg(args, smart_str *); |
351 | |
352 | if (hash_key->nKeyLength == 0) { /* numeric key */ |
353 | buffer_append_spaces(buf, level+1); |
354 | smart_str_append_long(buf, (long) hash_key->h); |
355 | smart_str_appendl(buf, " => " , 4); |
356 | |
357 | } else { /* string key */ |
358 | char *key, *tmp_str; |
359 | int key_len, tmp_len; |
360 | key = php_addcslashes(hash_key->arKey, hash_key->nKeyLength - 1, &key_len, 0, "'\\" , 2 TSRMLS_CC); |
361 | tmp_str = php_str_to_str_ex(key, key_len, "\0" , 1, "' . \"\\0\" . '" , 12, &tmp_len, 0, NULL); |
362 | |
363 | buffer_append_spaces(buf, level + 1); |
364 | |
365 | smart_str_appendc(buf, '\''); |
366 | smart_str_appendl(buf, tmp_str, tmp_len); |
367 | smart_str_appendl(buf, "' => " , 5); |
368 | |
369 | efree(key); |
370 | efree(tmp_str); |
371 | } |
372 | php_var_export_ex(zv, level + 2, buf TSRMLS_CC); |
373 | |
374 | smart_str_appendc(buf, ','); |
375 | smart_str_appendc(buf, '\n'); |
376 | |
377 | return 0; |
378 | } |
379 | /* }}} */ |
380 | |
381 | static int php_object_element_export(zval **zv TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key) /* {{{ */ |
382 | { |
383 | int level; |
384 | smart_str *buf; |
385 | |
386 | level = va_arg(args, int); |
387 | buf = va_arg(args, smart_str *); |
388 | |
389 | buffer_append_spaces(buf, level + 2); |
390 | if (hash_key->nKeyLength != 0) { |
391 | const char *class_name; /* ignored, but must be passed to unmangle */ |
392 | const char *pname; |
393 | char *pname_esc; |
394 | int pname_esc_len; |
395 | |
396 | zend_unmangle_property_name(hash_key->arKey, hash_key->nKeyLength - 1, |
397 | &class_name, &pname); |
398 | pname_esc = php_addcslashes(pname, strlen(pname), &pname_esc_len, 0, |
399 | "'\\" , 2 TSRMLS_CC); |
400 | |
401 | smart_str_appendc(buf, '\''); |
402 | smart_str_appendl(buf, pname_esc, pname_esc_len); |
403 | smart_str_appendc(buf, '\''); |
404 | efree(pname_esc); |
405 | } else { |
406 | smart_str_append_long(buf, (long) hash_key->h); |
407 | } |
408 | smart_str_appendl(buf, " => " , 4); |
409 | php_var_export_ex(zv, level + 2, buf TSRMLS_CC); |
410 | smart_str_appendc(buf, ','); |
411 | smart_str_appendc(buf, '\n'); |
412 | return 0; |
413 | } |
414 | /* }}} */ |
415 | |
416 | PHPAPI void php_var_export_ex(zval **struc, int level, smart_str *buf TSRMLS_DC) /* {{{ */ |
417 | { |
418 | HashTable *myht; |
419 | char *tmp_str, *tmp_str2; |
420 | int tmp_len, tmp_len2; |
421 | const char *class_name; |
422 | zend_uint class_name_len; |
423 | |
424 | switch (Z_TYPE_PP(struc)) { |
425 | case IS_BOOL: |
426 | if (Z_LVAL_PP(struc)) { |
427 | smart_str_appendl(buf, "true" , 4); |
428 | } else { |
429 | smart_str_appendl(buf, "false" , 5); |
430 | } |
431 | break; |
432 | case IS_NULL: |
433 | smart_str_appendl(buf, "NULL" , 4); |
434 | break; |
435 | case IS_LONG: |
436 | smart_str_append_long(buf, Z_LVAL_PP(struc)); |
437 | break; |
438 | case IS_DOUBLE: |
439 | tmp_len = spprintf(&tmp_str, 0,"%.*H" , PG(serialize_precision), Z_DVAL_PP(struc)); |
440 | smart_str_appendl(buf, tmp_str, tmp_len); |
441 | efree(tmp_str); |
442 | break; |
443 | case IS_STRING: |
444 | tmp_str = php_addcslashes(Z_STRVAL_PP(struc), Z_STRLEN_PP(struc), &tmp_len, 0, "'\\" , 2 TSRMLS_CC); |
445 | tmp_str2 = php_str_to_str_ex(tmp_str, tmp_len, "\0" , 1, "' . \"\\0\" . '" , 12, &tmp_len2, 0, NULL); |
446 | |
447 | smart_str_appendc(buf, '\''); |
448 | smart_str_appendl(buf, tmp_str2, tmp_len2); |
449 | smart_str_appendc(buf, '\''); |
450 | |
451 | efree(tmp_str2); |
452 | efree(tmp_str); |
453 | break; |
454 | case IS_ARRAY: |
455 | myht = Z_ARRVAL_PP(struc); |
456 | if (myht->nApplyCount > 0){ |
457 | smart_str_appendl(buf, "NULL" , 4); |
458 | zend_error(E_WARNING, "var_export does not handle circular references" ); |
459 | return; |
460 | } |
461 | if (level > 1) { |
462 | smart_str_appendc(buf, '\n'); |
463 | buffer_append_spaces(buf, level - 1); |
464 | } |
465 | smart_str_appendl(buf, "array (\n" , 8); |
466 | zend_hash_apply_with_arguments(myht TSRMLS_CC, (apply_func_args_t) php_array_element_export, 2, level, buf); |
467 | |
468 | if (level > 1) { |
469 | buffer_append_spaces(buf, level - 1); |
470 | } |
471 | smart_str_appendc(buf, ')'); |
472 | |
473 | break; |
474 | |
475 | case IS_OBJECT: |
476 | myht = Z_OBJPROP_PP(struc); |
477 | if(myht && myht->nApplyCount > 0){ |
478 | smart_str_appendl(buf, "NULL" , 4); |
479 | zend_error(E_WARNING, "var_export does not handle circular references" ); |
480 | return; |
481 | } |
482 | if (level > 1) { |
483 | smart_str_appendc(buf, '\n'); |
484 | buffer_append_spaces(buf, level - 1); |
485 | } |
486 | Z_OBJ_HANDLER(**struc, get_class_name)(*struc, &class_name, &class_name_len, 0 TSRMLS_CC); |
487 | |
488 | smart_str_appendl(buf, class_name, class_name_len); |
489 | smart_str_appendl(buf, "::__set_state(array(\n" , 21); |
490 | |
491 | efree((char*)class_name); |
492 | if (myht) { |
493 | zend_hash_apply_with_arguments(myht TSRMLS_CC, (apply_func_args_t) php_object_element_export, 1, level, buf); |
494 | } |
495 | if (level > 1) { |
496 | buffer_append_spaces(buf, level - 1); |
497 | } |
498 | smart_str_appendl(buf, "))" , 2); |
499 | |
500 | break; |
501 | default: |
502 | smart_str_appendl(buf, "NULL" , 4); |
503 | break; |
504 | } |
505 | } |
506 | /* }}} */ |
507 | |
508 | /* FOR BC reasons, this will always perform and then print */ |
509 | PHPAPI void php_var_export(zval **struc, int level TSRMLS_DC) /* {{{ */ |
510 | { |
511 | smart_str buf = {0}; |
512 | php_var_export_ex(struc, level, &buf TSRMLS_CC); |
513 | smart_str_0 (&buf); |
514 | PHPWRITE(buf.c, buf.len); |
515 | smart_str_free(&buf); |
516 | } |
517 | /* }}} */ |
518 | |
519 | |
520 | /* {{{ proto mixed var_export(mixed var [, bool return]) |
521 | Outputs or returns a string representation of a variable */ |
522 | PHP_FUNCTION(var_export) |
523 | { |
524 | zval *var; |
525 | zend_bool return_output = 0; |
526 | smart_str buf = {0}; |
527 | |
528 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|b" , &var, &return_output) == FAILURE) { |
529 | return; |
530 | } |
531 | |
532 | php_var_export_ex(&var, 1, &buf TSRMLS_CC); |
533 | smart_str_0 (&buf); |
534 | |
535 | if (return_output) { |
536 | RETVAL_STRINGL(buf.c, buf.len, 1); |
537 | } else { |
538 | PHPWRITE(buf.c, buf.len); |
539 | } |
540 | smart_str_free(&buf); |
541 | } |
542 | /* }}} */ |
543 | |
544 | static void php_var_serialize_intern(smart_str *buf, zval *struc, HashTable *var_hash TSRMLS_DC); |
545 | |
546 | static inline int php_add_var_hash(HashTable *var_hash, zval *var, void *var_old TSRMLS_DC) /* {{{ */ |
547 | { |
548 | ulong var_no; |
549 | char id[32], *p; |
550 | register int len; |
551 | |
552 | if ((Z_TYPE_P(var) == IS_OBJECT) && Z_OBJ_HT_P(var)->get_class_entry) { |
553 | p = smart_str_print_long(id + sizeof(id) - 1, |
554 | (long) zend_objects_get_address(var TSRMLS_CC)); |
555 | *(--p) = 'O'; |
556 | len = id + sizeof(id) - 1 - p; |
557 | } else { |
558 | p = smart_str_print_long(id + sizeof(id) - 1, (long) var); |
559 | len = id + sizeof(id) - 1 - p; |
560 | } |
561 | |
562 | if (var_old && zend_hash_find(var_hash, p, len, var_old) == SUCCESS) { |
563 | if (!Z_ISREF_P(var)) { |
564 | /* we still need to bump up the counter, since non-refs will |
565 | * be counted separately by unserializer */ |
566 | var_no = -1; |
567 | zend_hash_next_index_insert(var_hash, &var_no, sizeof(var_no), NULL); |
568 | } |
569 | #if 0 |
570 | fprintf(stderr, "- had var (%d): %lu\n" , Z_TYPE_P(var), **(ulong**)var_old); |
571 | #endif |
572 | return FAILURE; |
573 | } |
574 | |
575 | /* +1 because otherwise hash will think we are trying to store NULL pointer */ |
576 | var_no = zend_hash_num_elements(var_hash) + 1; |
577 | zend_hash_add(var_hash, p, len, &var_no, sizeof(var_no), NULL); |
578 | #if 0 |
579 | fprintf(stderr, "+ add var (%d): %lu\n" , Z_TYPE_P(var), var_no); |
580 | #endif |
581 | return SUCCESS; |
582 | } |
583 | /* }}} */ |
584 | |
585 | static inline void php_var_serialize_long(smart_str *buf, long val) /* {{{ */ |
586 | { |
587 | smart_str_appendl(buf, "i:" , 2); |
588 | smart_str_append_long(buf, val); |
589 | smart_str_appendc(buf, ';'); |
590 | } |
591 | /* }}} */ |
592 | |
593 | static inline void php_var_serialize_string(smart_str *buf, char *str, int len) /* {{{ */ |
594 | { |
595 | smart_str_appendl(buf, "s:" , 2); |
596 | smart_str_append_long(buf, len); |
597 | smart_str_appendl(buf, ":\"" , 2); |
598 | smart_str_appendl(buf, str, len); |
599 | smart_str_appendl(buf, "\";" , 2); |
600 | } |
601 | /* }}} */ |
602 | |
603 | static inline zend_bool php_var_serialize_class_name(smart_str *buf, zval *struc TSRMLS_DC) /* {{{ */ |
604 | { |
605 | PHP_CLASS_ATTRIBUTES; |
606 | |
607 | PHP_SET_CLASS_ATTRIBUTES(struc); |
608 | smart_str_appendl(buf, "O:" , 2); |
609 | smart_str_append_long(buf, (int)name_len); |
610 | smart_str_appendl(buf, ":\"" , 2); |
611 | smart_str_appendl(buf, class_name, name_len); |
612 | smart_str_appendl(buf, "\":" , 2); |
613 | PHP_CLEANUP_CLASS_ATTRIBUTES(); |
614 | return incomplete_class; |
615 | } |
616 | /* }}} */ |
617 | |
618 | static void php_var_serialize_class(smart_str *buf, zval *struc, zval *retval_ptr, HashTable *var_hash TSRMLS_DC) /* {{{ */ |
619 | { |
620 | int count; |
621 | zend_bool incomplete_class; |
622 | |
623 | incomplete_class = php_var_serialize_class_name(buf, struc TSRMLS_CC); |
624 | /* count after serializing name, since php_var_serialize_class_name |
625 | * changes the count if the variable is incomplete class */ |
626 | count = zend_hash_num_elements(HASH_OF(retval_ptr)); |
627 | if (incomplete_class) { |
628 | --count; |
629 | } |
630 | smart_str_append_long(buf, count); |
631 | smart_str_appendl(buf, ":{" , 2); |
632 | |
633 | if (count > 0) { |
634 | char *key; |
635 | zval **d, **name; |
636 | ulong index; |
637 | HashPosition pos; |
638 | int i; |
639 | zval nval, *nvalp; |
640 | HashTable *propers; |
641 | |
642 | ZVAL_NULL(&nval); |
643 | nvalp = &nval; |
644 | |
645 | zend_hash_internal_pointer_reset_ex(HASH_OF(retval_ptr), &pos); |
646 | |
647 | for (;; zend_hash_move_forward_ex(HASH_OF(retval_ptr), &pos)) { |
648 | i = zend_hash_get_current_key_ex(HASH_OF(retval_ptr), &key, NULL, &index, 0, &pos); |
649 | |
650 | if (i == HASH_KEY_NON_EXISTENT) { |
651 | break; |
652 | } |
653 | |
654 | if (incomplete_class && strcmp(key, MAGIC_MEMBER) == 0) { |
655 | continue; |
656 | } |
657 | zend_hash_get_current_data_ex(HASH_OF(retval_ptr), (void **) &name, &pos); |
658 | |
659 | if (Z_TYPE_PP(name) != IS_STRING) { |
660 | php_error_docref(NULL TSRMLS_CC, E_NOTICE, "__sleep should return an array only containing the names of instance-variables to serialize." ); |
661 | convert_to_string(*name); |
662 | } |
663 | propers = Z_OBJPROP_P(struc); |
664 | if (zend_hash_find(propers, Z_STRVAL_PP(name), Z_STRLEN_PP(name) + 1, (void *) &d) == SUCCESS) { |
665 | php_var_serialize_string(buf, Z_STRVAL_PP(name), Z_STRLEN_PP(name)); |
666 | php_var_serialize_intern(buf, *d, var_hash TSRMLS_CC); |
667 | } else { |
668 | zend_class_entry *ce; |
669 | ce = zend_get_class_entry(struc TSRMLS_CC); |
670 | if (ce) { |
671 | char *prot_name, *priv_name; |
672 | int prop_name_length; |
673 | |
674 | do { |
675 | zend_mangle_property_name(&priv_name, &prop_name_length, ce->name, ce->name_length, Z_STRVAL_PP(name), Z_STRLEN_PP(name), ce->type & ZEND_INTERNAL_CLASS); |
676 | if (zend_hash_find(propers, priv_name, prop_name_length + 1, (void *) &d) == SUCCESS) { |
677 | php_var_serialize_string(buf, priv_name, prop_name_length); |
678 | pefree(priv_name, ce->type & ZEND_INTERNAL_CLASS); |
679 | php_var_serialize_intern(buf, *d, var_hash TSRMLS_CC); |
680 | break; |
681 | } |
682 | pefree(priv_name, ce->type & ZEND_INTERNAL_CLASS); |
683 | zend_mangle_property_name(&prot_name, &prop_name_length, "*" , 1, Z_STRVAL_PP(name), Z_STRLEN_PP(name), ce->type & ZEND_INTERNAL_CLASS); |
684 | if (zend_hash_find(propers, prot_name, prop_name_length + 1, (void *) &d) == SUCCESS) { |
685 | php_var_serialize_string(buf, prot_name, prop_name_length); |
686 | pefree(prot_name, ce->type & ZEND_INTERNAL_CLASS); |
687 | php_var_serialize_intern(buf, *d, var_hash TSRMLS_CC); |
688 | break; |
689 | } |
690 | pefree(prot_name, ce->type & ZEND_INTERNAL_CLASS); |
691 | php_var_serialize_string(buf, Z_STRVAL_PP(name), Z_STRLEN_PP(name)); |
692 | php_var_serialize_intern(buf, nvalp, var_hash TSRMLS_CC); |
693 | php_error_docref(NULL TSRMLS_CC, E_NOTICE, "\"%s\" returned as member variable from __sleep() but does not exist" , Z_STRVAL_PP(name)); |
694 | } while (0); |
695 | } else { |
696 | php_var_serialize_string(buf, Z_STRVAL_PP(name), Z_STRLEN_PP(name)); |
697 | php_var_serialize_intern(buf, nvalp, var_hash TSRMLS_CC); |
698 | } |
699 | } |
700 | } |
701 | } |
702 | smart_str_appendc(buf, '}'); |
703 | } |
704 | /* }}} */ |
705 | |
706 | static void php_var_serialize_intern(smart_str *buf, zval *struc, HashTable *var_hash TSRMLS_DC) /* {{{ */ |
707 | { |
708 | int i; |
709 | ulong *var_already; |
710 | HashTable *myht; |
711 | |
712 | if (EG(exception)) { |
713 | return; |
714 | } |
715 | |
716 | if (var_hash && php_add_var_hash(var_hash, struc, (void *) &var_already TSRMLS_CC) == FAILURE) { |
717 | if (Z_ISREF_P(struc)) { |
718 | smart_str_appendl(buf, "R:" , 2); |
719 | smart_str_append_long(buf, (long)*var_already); |
720 | smart_str_appendc(buf, ';'); |
721 | return; |
722 | } else if (Z_TYPE_P(struc) == IS_OBJECT) { |
723 | smart_str_appendl(buf, "r:" , 2); |
724 | smart_str_append_long(buf, (long)*var_already); |
725 | smart_str_appendc(buf, ';'); |
726 | return; |
727 | } |
728 | } |
729 | |
730 | switch (Z_TYPE_P(struc)) { |
731 | case IS_BOOL: |
732 | smart_str_appendl(buf, "b:" , 2); |
733 | smart_str_append_long(buf, Z_LVAL_P(struc)); |
734 | smart_str_appendc(buf, ';'); |
735 | return; |
736 | |
737 | case IS_NULL: |
738 | smart_str_appendl(buf, "N;" , 2); |
739 | return; |
740 | |
741 | case IS_LONG: |
742 | php_var_serialize_long(buf, Z_LVAL_P(struc)); |
743 | return; |
744 | |
745 | case IS_DOUBLE: { |
746 | char *s; |
747 | |
748 | smart_str_appendl(buf, "d:" , 2); |
749 | s = (char *) safe_emalloc(PG(serialize_precision), 1, MAX_LENGTH_OF_DOUBLE + 1); |
750 | php_gcvt(Z_DVAL_P(struc), PG(serialize_precision), '.', 'E', s); |
751 | smart_str_appends(buf, s); |
752 | smart_str_appendc(buf, ';'); |
753 | efree(s); |
754 | return; |
755 | } |
756 | |
757 | case IS_STRING: |
758 | php_var_serialize_string(buf, Z_STRVAL_P(struc), Z_STRLEN_P(struc)); |
759 | return; |
760 | |
761 | case IS_OBJECT: { |
762 | zval *retval_ptr = NULL; |
763 | zval fname; |
764 | int res; |
765 | zend_class_entry *ce = NULL; |
766 | |
767 | if (Z_OBJ_HT_P(struc)->get_class_entry) { |
768 | ce = Z_OBJCE_P(struc); |
769 | } |
770 | |
771 | if (ce && ce->serialize != NULL) { |
772 | /* has custom handler */ |
773 | unsigned char *serialized_data = NULL; |
774 | zend_uint serialized_length; |
775 | |
776 | if (ce->serialize(struc, &serialized_data, &serialized_length, (zend_serialize_data *)var_hash TSRMLS_CC) == SUCCESS) { |
777 | smart_str_appendl(buf, "C:" , 2); |
778 | smart_str_append_long(buf, (int)Z_OBJCE_P(struc)->name_length); |
779 | smart_str_appendl(buf, ":\"" , 2); |
780 | smart_str_appendl(buf, Z_OBJCE_P(struc)->name, Z_OBJCE_P(struc)->name_length); |
781 | smart_str_appendl(buf, "\":" , 2); |
782 | |
783 | smart_str_append_long(buf, (int)serialized_length); |
784 | smart_str_appendl(buf, ":{" , 2); |
785 | smart_str_appendl(buf, serialized_data, serialized_length); |
786 | smart_str_appendc(buf, '}'); |
787 | } else { |
788 | smart_str_appendl(buf, "N;" , 2); |
789 | } |
790 | if (serialized_data) { |
791 | efree(serialized_data); |
792 | } |
793 | return; |
794 | } |
795 | |
796 | if (ce && ce != PHP_IC_ENTRY && zend_hash_exists(&ce->function_table, "__sleep" , sizeof("__sleep" ))) { |
797 | INIT_PZVAL(&fname); |
798 | ZVAL_STRINGL(&fname, "__sleep" , sizeof("__sleep" ) - 1, 0); |
799 | BG(serialize_lock)++; |
800 | res = call_user_function_ex(CG(function_table), &struc, &fname, &retval_ptr, 0, 0, 1, NULL TSRMLS_CC); |
801 | BG(serialize_lock)--; |
802 | |
803 | if (EG(exception)) { |
804 | if (retval_ptr) { |
805 | zval_ptr_dtor(&retval_ptr); |
806 | } |
807 | return; |
808 | } |
809 | |
810 | if (res == SUCCESS) { |
811 | if (retval_ptr) { |
812 | if (HASH_OF(retval_ptr)) { |
813 | php_var_serialize_class(buf, struc, retval_ptr, var_hash TSRMLS_CC); |
814 | } else { |
815 | php_error_docref(NULL TSRMLS_CC, E_NOTICE, "__sleep should return an array only containing the names of instance-variables to serialize" ); |
816 | /* we should still add element even if it's not OK, |
817 | * since we already wrote the length of the array before */ |
818 | smart_str_appendl(buf,"N;" , 2); |
819 | } |
820 | zval_ptr_dtor(&retval_ptr); |
821 | } |
822 | return; |
823 | } |
824 | } |
825 | |
826 | if (retval_ptr) { |
827 | zval_ptr_dtor(&retval_ptr); |
828 | } |
829 | /* fall-through */ |
830 | } |
831 | case IS_ARRAY: { |
832 | zend_bool incomplete_class = 0; |
833 | if (Z_TYPE_P(struc) == IS_ARRAY) { |
834 | smart_str_appendl(buf, "a:" , 2); |
835 | myht = HASH_OF(struc); |
836 | } else { |
837 | incomplete_class = php_var_serialize_class_name(buf, struc TSRMLS_CC); |
838 | myht = Z_OBJPROP_P(struc); |
839 | } |
840 | /* count after serializing name, since php_var_serialize_class_name |
841 | * changes the count if the variable is incomplete class */ |
842 | i = myht ? zend_hash_num_elements(myht) : 0; |
843 | if (i > 0 && incomplete_class) { |
844 | --i; |
845 | } |
846 | smart_str_append_long(buf, i); |
847 | smart_str_appendl(buf, ":{" , 2); |
848 | if (i > 0) { |
849 | char *key; |
850 | zval **data; |
851 | ulong index; |
852 | uint key_len; |
853 | HashPosition pos; |
854 | |
855 | zend_hash_internal_pointer_reset_ex(myht, &pos); |
856 | for (;; zend_hash_move_forward_ex(myht, &pos)) { |
857 | i = zend_hash_get_current_key_ex(myht, &key, &key_len, &index, 0, &pos); |
858 | if (i == HASH_KEY_NON_EXISTENT) { |
859 | break; |
860 | } |
861 | if (incomplete_class && strcmp(key, MAGIC_MEMBER) == 0) { |
862 | continue; |
863 | } |
864 | |
865 | switch (i) { |
866 | case HASH_KEY_IS_LONG: |
867 | php_var_serialize_long(buf, index); |
868 | break; |
869 | case HASH_KEY_IS_STRING: |
870 | php_var_serialize_string(buf, key, key_len - 1); |
871 | break; |
872 | } |
873 | |
874 | /* we should still add element even if it's not OK, |
875 | * since we already wrote the length of the array before */ |
876 | if (zend_hash_get_current_data_ex(myht, (void **) &data, &pos) != SUCCESS |
877 | || !data |
878 | || data == &struc |
879 | || (Z_TYPE_PP(data) == IS_ARRAY && Z_ARRVAL_PP(data)->nApplyCount > 1) |
880 | ) { |
881 | smart_str_appendl(buf, "N;" , 2); |
882 | } else { |
883 | if (Z_TYPE_PP(data) == IS_ARRAY) { |
884 | Z_ARRVAL_PP(data)->nApplyCount++; |
885 | } |
886 | php_var_serialize_intern(buf, *data, var_hash TSRMLS_CC); |
887 | if (Z_TYPE_PP(data) == IS_ARRAY) { |
888 | Z_ARRVAL_PP(data)->nApplyCount--; |
889 | } |
890 | } |
891 | } |
892 | } |
893 | smart_str_appendc(buf, '}'); |
894 | return; |
895 | } |
896 | default: |
897 | smart_str_appendl(buf, "i:0;" , 4); |
898 | return; |
899 | } |
900 | } |
901 | /* }}} */ |
902 | |
903 | PHPAPI void php_var_serialize(smart_str *buf, zval **struc, php_serialize_data_t *var_hash TSRMLS_DC) /* {{{ */ |
904 | { |
905 | php_var_serialize_intern(buf, *struc, *var_hash TSRMLS_CC); |
906 | smart_str_0(buf); |
907 | } |
908 | /* }}} */ |
909 | |
910 | /* {{{ proto string serialize(mixed variable) |
911 | Returns a string representation of variable (which can later be unserialized) */ |
912 | PHP_FUNCTION(serialize) |
913 | { |
914 | zval **struc; |
915 | php_serialize_data_t var_hash; |
916 | smart_str buf = {0}; |
917 | |
918 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z" , &struc) == FAILURE) { |
919 | return; |
920 | } |
921 | |
922 | Z_TYPE_P(return_value) = IS_STRING; |
923 | Z_STRVAL_P(return_value) = NULL; |
924 | Z_STRLEN_P(return_value) = 0; |
925 | |
926 | PHP_VAR_SERIALIZE_INIT(var_hash); |
927 | php_var_serialize(&buf, struc, &var_hash TSRMLS_CC); |
928 | PHP_VAR_SERIALIZE_DESTROY(var_hash); |
929 | |
930 | if (EG(exception)) { |
931 | smart_str_free(&buf); |
932 | RETURN_FALSE; |
933 | } |
934 | |
935 | if (buf.c) { |
936 | RETURN_STRINGL(buf.c, buf.len, 0); |
937 | } else { |
938 | RETURN_NULL(); |
939 | } |
940 | } |
941 | /* }}} */ |
942 | |
943 | /* {{{ proto mixed unserialize(string variable_representation) |
944 | Takes a string representation of variable and recreates it */ |
945 | PHP_FUNCTION(unserialize) |
946 | { |
947 | char *buf = NULL; |
948 | int buf_len; |
949 | const unsigned char *p; |
950 | php_unserialize_data_t var_hash; |
951 | |
952 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s" , &buf, &buf_len) == FAILURE) { |
953 | RETURN_FALSE; |
954 | } |
955 | |
956 | if (buf_len == 0) { |
957 | RETURN_FALSE; |
958 | } |
959 | |
960 | p = (const unsigned char*) buf; |
961 | PHP_VAR_UNSERIALIZE_INIT(var_hash); |
962 | if (!php_var_unserialize(&return_value, &p, p + buf_len, &var_hash TSRMLS_CC)) { |
963 | PHP_VAR_UNSERIALIZE_DESTROY(var_hash); |
964 | zval_dtor(return_value); |
965 | if (!EG(exception)) { |
966 | php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Error at offset %ld of %d bytes" , (long)((char*)p - buf), buf_len); |
967 | } |
968 | RETURN_FALSE; |
969 | } |
970 | PHP_VAR_UNSERIALIZE_DESTROY(var_hash); |
971 | } |
972 | /* }}} */ |
973 | |
974 | /* {{{ proto int memory_get_usage([real_usage]) |
975 | Returns the allocated by PHP memory */ |
976 | PHP_FUNCTION(memory_get_usage) { |
977 | zend_bool real_usage = 0; |
978 | |
979 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b" , &real_usage) == FAILURE) { |
980 | RETURN_FALSE; |
981 | } |
982 | |
983 | RETURN_LONG(zend_memory_usage(real_usage TSRMLS_CC)); |
984 | } |
985 | /* }}} */ |
986 | |
987 | /* {{{ proto int memory_get_peak_usage([real_usage]) |
988 | Returns the peak allocated by PHP memory */ |
989 | PHP_FUNCTION(memory_get_peak_usage) { |
990 | zend_bool real_usage = 0; |
991 | |
992 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b" , &real_usage) == FAILURE) { |
993 | RETURN_FALSE; |
994 | } |
995 | |
996 | RETURN_LONG(zend_memory_peak_usage(real_usage TSRMLS_CC)); |
997 | } |
998 | /* }}} */ |
999 | |
1000 | /* |
1001 | * Local variables: |
1002 | * tab-width: 4 |
1003 | * c-basic-offset: 4 |
1004 | * End: |
1005 | * vim600: sw=4 ts=4 fdm=marker |
1006 | * vim<600: sw=4 ts=4 |
1007 | */ |
1008 | |