1 /*
2 +----------------------------------------------------------------------+
3 | Xdebug |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2002-2021 Derick Rethans |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 1.01 of the Xdebug license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available at through the world-wide-web at |
10 | https://xdebug.org/license.php |
11 | If you did not receive a copy of the Xdebug license and are unable |
12 | to obtain it through the world-wide-web, please send a note to |
13 | derick@xdebug.org so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
15 */
16
17 #include "php.h"
18 #include "ext/standard/php_string.h"
19 #include "ext/standard/url.h"
20 #include "zend.h"
21 #include "zend_exceptions.h"
22 #include "zend_extensions.h"
23 #include "ext/standard/php_smart_string.h"
24 #include "zend_smart_str.h"
25
26 #include "php_xdebug.h"
27
28 #include "lib/compat.h"
29 #include "lib/lib_private.h"
30 #include "lib/mm.h"
31 #include "lib/var.h"
32 #include "lib/var_export_html.h"
33 #include "lib/var_export_line.h"
34 #include "lib/xml.h"
35
ZEND_EXTERN_MODULE_GLOBALS(xdebug)36 ZEND_EXTERN_MODULE_GLOBALS(xdebug)
37
38 static inline int object_or_ancestor_is_internal(zval dzval)
39 {
40 zend_class_entry *tmp_ce = Z_OBJCE(dzval);
41
42 do {
43 if (tmp_ce->type == ZEND_INTERNAL_CLASS) {
44 return 1;
45 }
46 tmp_ce = tmp_ce->parent;
47 } while (tmp_ce);
48
49 return 0;
50 }
51 #if PHP_VERSION_ID >= 70400
xdebug_objdebug_pp(zval ** zval_pp,int flags)52 HashTable *xdebug_objdebug_pp(zval **zval_pp, int flags)
53 #else
54 HashTable *xdebug_objdebug_pp(zval **zval_pp, int *is_tmp, int flags)
55 #endif
56 {
57 zval dzval = **zval_pp;
58 HashTable *tmp;
59
60 if (
61 !XG_BASE(in_debug_info) &&
62 (object_or_ancestor_is_internal(dzval) || (flags & XDEBUG_VAR_OBJDEBUG_USE_DEBUGINFO)) &&
63 Z_OBJ_HANDLER(dzval, get_debug_info)
64 ) {
65 void *original_trace_context;
66 zend_object *orig_exception;
67
68 xdebug_tracing_save_trace_context(&original_trace_context);
69 XG_BASE(in_debug_info) = 1;
70 orig_exception = EG(exception);
71 EG(exception) = NULL;
72
73 #if PHP_VERSION_ID >= 70400
74 tmp = zend_get_properties_for(&dzval, ZEND_PROP_PURPOSE_DEBUG);
75 #else
76 tmp = Z_OBJ_HANDLER(dzval, get_debug_info)(&dzval, is_tmp);
77 #endif
78 XG_BASE(in_debug_info) = 0;
79 xdebug_tracing_restore_trace_context(original_trace_context);
80 EG(exception) = orig_exception;
81
82 return tmp;
83 } else {
84 #if PHP_VERSION_ID >= 70400
85 return zend_get_properties_for(&dzval, ZEND_PROP_PURPOSE_VAR_EXPORT);
86 #else
87 *is_tmp = 0;
88 if (Z_OBJ_HANDLER(dzval, get_properties)) {
89 return Z_OBJPROP(dzval);
90 }
91 #endif
92 }
93 return NULL;
94 }
95
xdebug_error_type_simple(int type)96 char* xdebug_error_type_simple(int type)
97 {
98 switch (type) {
99 case E_ERROR:
100 case E_CORE_ERROR:
101 case E_COMPILE_ERROR:
102 case E_USER_ERROR:
103 return xdstrdup("fatal-error");
104 break;
105 case E_RECOVERABLE_ERROR:
106 return xdstrdup("recoverable-fatal-error");
107 break;
108 case E_WARNING:
109 case E_CORE_WARNING:
110 case E_COMPILE_WARNING:
111 case E_USER_WARNING:
112 return xdstrdup("warning");
113 break;
114 case E_PARSE:
115 return xdstrdup("parse-error");
116 break;
117 case E_NOTICE:
118 case E_USER_NOTICE:
119 return xdstrdup("notice");
120 break;
121 case E_STRICT:
122 return xdstrdup("strict-standards");
123 break;
124 case E_DEPRECATED:
125 case E_USER_DEPRECATED:
126 return xdstrdup("deprecated");
127 break;
128 case 0:
129 return xdstrdup("xdebug");
130 break;
131 default:
132 return xdstrdup("unknown-error");
133 break;
134 }
135 }
136
xdebug_error_type(int type)137 char* xdebug_error_type(int type)
138 {
139 switch (type) {
140 case E_ERROR:
141 case E_CORE_ERROR:
142 case E_COMPILE_ERROR:
143 case E_USER_ERROR:
144 return xdstrdup("Fatal error");
145 break;
146 case E_RECOVERABLE_ERROR:
147 return xdstrdup("Recoverable fatal error");
148 break;
149 case E_WARNING:
150 case E_CORE_WARNING:
151 case E_COMPILE_WARNING:
152 case E_USER_WARNING:
153 return xdstrdup("Warning");
154 break;
155 case E_PARSE:
156 return xdstrdup("Parse error");
157 break;
158 case E_NOTICE:
159 case E_USER_NOTICE:
160 return xdstrdup("Notice");
161 break;
162 case E_STRICT:
163 return xdstrdup("Strict standards");
164 break;
165 case E_DEPRECATED:
166 case E_USER_DEPRECATED:
167 return xdstrdup("Deprecated");
168 break;
169 case 0:
170 return xdstrdup("Xdebug");
171 break;
172 default:
173 return xdstrdup("Unknown error");
174 break;
175 }
176 }
177
178 /*************************************************************************************************************************************/
179 #define T(offset) (*(union _temp_variable *)((char*)zdata->current_execute_data->Ts + offset))
180
xdebug_get_zval_with_opline(zend_execute_data * zdata,const zend_op * opline,int node_type,const znode_op * node,int * is_var)181 zval *xdebug_get_zval_with_opline(zend_execute_data *zdata, const zend_op *opline, int node_type, const znode_op *node, int *is_var)
182 {
183 #if PHP_VERSION_ID >= 80000
184 return zend_get_zval_ptr(opline, node_type, node, zdata);
185 #else
186 zend_free_op should_free;
187
188 # if PHP_VERSION_ID >= 70300
189 return zend_get_zval_ptr(opline, node_type, node, zdata, &should_free, BP_VAR_IS);
190 # else
191 return zend_get_zval_ptr(node_type, node, zdata, &should_free, BP_VAR_IS);
192 # endif
193 #endif
194 }
195
xdebug_get_zval(zend_execute_data * zdata,int node_type,const znode_op * node,int * is_var)196 zval *xdebug_get_zval(zend_execute_data *zdata, int node_type, const znode_op *node, int *is_var)
197 {
198 return xdebug_get_zval_with_opline(zdata, zdata->opline, node_type, node, is_var);
199 }
200
201
202 /*****************************************************************************
203 ** PHP Variable related utility functions
204 */
205
206 /*****************************************************************************
207 ** Data returning functions
208 */
209 #define XF_ST_ROOT 0
210 #define XF_ST_ARRAY_INDEX_NUM 1
211 #define XF_ST_ARRAY_INDEX_ASSOC 2
212 #define XF_ST_OBJ_PROPERTY 3
213 #define XF_ST_STATIC_ROOT 4
214 #define XF_ST_STATIC_PROPERTY 5
215 #define XF_ST_ESCAPED_OBJ_PROPERTY 6
216
fetch_ht_from_zval(zval * z)217 inline static HashTable *fetch_ht_from_zval(zval *z)
218 {
219 switch (Z_TYPE_P(z)) {
220 case IS_ARRAY:
221 return Z_ARRVAL_P(z);
222 break;
223 case IS_OBJECT:
224 return Z_OBJPROP_P(z);
225 break;
226 }
227 return NULL;
228 }
229
fetch_classname_from_zval(zval * z,int * length,zend_class_entry ** ce)230 inline static char *fetch_classname_from_zval(zval *z, int *length, zend_class_entry **ce)
231 {
232 zend_string *class_name;
233
234 if (Z_TYPE_P(z) == IS_INDIRECT) {
235 z = z->value.zv;
236 }
237 if (Z_TYPE_P(z) == IS_REFERENCE) {
238 z = &z->value.ref->val;
239 }
240
241 if (Z_TYPE_P(z) != IS_OBJECT) {
242 return NULL;
243 }
244
245 class_name = Z_OBJ_HANDLER_P(z, get_class_name)(Z_OBJ_P(z));
246
247 *ce = Z_OBJCE_P(z);
248 *length = class_name->len;
249
250 return estrdup(class_name->val);
251 }
252
prepare_search_key(char * name,unsigned int * name_length,const char * prefix,int prefix_length)253 static char* prepare_search_key(char *name, unsigned int *name_length, const char *prefix, int prefix_length)
254 {
255 char *element;
256 int extra_length = 0;
257
258 if (prefix_length) {
259 if (prefix[0] == '*') {
260 extra_length = 3;
261 } else {
262 extra_length = 2 + prefix_length;
263 }
264 }
265
266 element = malloc(*name_length + 1 + extra_length);
267 memset(element, 0, *name_length + 1 + extra_length);
268 if (extra_length) {
269 memcpy(element + 1, prefix, extra_length - 2);
270 }
271 memcpy(element + extra_length, name, *name_length);
272 *name_length += extra_length;
273
274 return element;
275 }
276
replace_star_by_null(const char * name,int name_length)277 char *replace_star_by_null(const char *name, int name_length)
278 {
279 char *tmp = xdstrdup(name);
280 int i;
281
282 for (i = 0; i < name_length; i++) {
283 if (tmp[i] == '*') {
284 tmp[i] = '\0';
285 }
286 }
287
288 return tmp;
289 }
290
291 #if PHP_VERSION_ID >= 70400
get_splobjectstorage_storage(zval * parent,HashTable ** properties)292 static zval *get_splobjectstorage_storage(zval *parent, HashTable **properties)
293 {
294 *properties = zend_get_properties_for(parent, ZEND_PROP_PURPOSE_DEBUG);
295 return zend_hash_str_find(*properties, "\0SplObjectStorage\0storage", sizeof("*SplObjectStorage*storage") - 1);
296 }
297 #else
get_splobjectstorage_storage(zval * parent,HashTable ** properties,int * is_temp)298 static zval *get_splobjectstorage_storage(zval *parent, HashTable **properties, int *is_temp)
299 {
300 *properties = Z_OBJDEBUG_P(parent, *is_temp);
301 return zend_hash_str_find(*properties, "\0SplObjectStorage\0storage", sizeof("*SplObjectStorage*storage") - 1);
302 }
303 #endif
304
305 #if PHP_VERSION_ID < 70400
xdebug_var_maybe_destroy_ht(HashTable * ht,int is_temp)306 void xdebug_var_maybe_destroy_ht(HashTable *ht, int is_temp)
307 {
308 if (ht && is_temp) {
309 zend_hash_destroy(ht);
310 FREE_HASHTABLE(ht);
311 }
312 }
313 #endif
314
fetch_zval_from_symbol_table(zval * value_in,char * name,unsigned int name_length,int type,char * ccn,int ccnl,zend_class_entry * cce)315 static void fetch_zval_from_symbol_table(
316 zval *value_in, char *name, unsigned int name_length,
317 int type, char* ccn, int ccnl, zend_class_entry *cce)
318 {
319 HashTable *ht = NULL;
320 char *element = NULL;
321 unsigned int element_length = name_length;
322 zend_property_info *zpp;
323 #if PHP_VERSION_ID < 70400
324 int is_temp = 0;
325 #endif
326 int free_duplicated_name = 0;
327 HashTable *myht = NULL;
328 zval *orig_value_in = value_in;
329 zval tmp_retval;
330
331 ZVAL_UNDEF(&tmp_retval);
332
333 if (Z_TYPE_P(value_in) == IS_INDIRECT) {
334 value_in = Z_INDIRECT_P(value_in);
335 }
336 ZVAL_DEREF(value_in);
337
338 ht = fetch_ht_from_zval(value_in);
339
340 switch (type) {
341 case XF_ST_STATIC_ROOT:
342 case XF_ST_STATIC_PROPERTY:
343 /* First we try a public,private,protected property */
344 #if PHP_VERSION_ID >= 70400
345 if (cce && (cce->type == ZEND_INTERNAL_CLASS || (cce->ce_flags & ZEND_ACC_IMMUTABLE))) {
346 zend_class_init_statics(cce);
347 }
348 #endif
349 element = prepare_search_key(name, &element_length, "", 0);
350 if (cce && ((zpp = zend_hash_str_find_ptr(&cce->properties_info, element, element_length)) != NULL) && cce->default_static_members_count && CE_STATIC_MEMBERS(cce)) {
351 ZVAL_COPY(&tmp_retval, &CE_STATIC_MEMBERS(cce)[zpp->offset]);
352 goto cleanup;
353 }
354 element_length = name_length;
355
356 /* Then we try to see whether the first char is * and use the part between * and * as class name for the private property */
357 if (name[0] == '*') {
358 char *secondStar;
359
360 secondStar = strstr(name + 1, "*");
361 if (secondStar) {
362 free(element);
363 element_length = name_length - (secondStar + 1 - name);
364 element = prepare_search_key(secondStar + 1, &element_length, "", 0);
365 if (cce && ((zpp = zend_hash_str_find_ptr(&cce->properties_info, element, element_length)) != NULL)) {
366 ZVAL_COPY(&tmp_retval, &CE_STATIC_MEMBERS(cce)[zpp->offset]);
367 goto cleanup;
368 }
369 }
370 }
371
372 break;
373
374 case XF_ST_ROOT:
375 /* Check for compiled vars */
376 element = prepare_search_key(name, &element_length, "", 0);
377 if (xdebug_lib_has_active_data() && xdebug_lib_has_active_function()) {
378 int i = 0;
379 zend_ulong hash_value = zend_inline_hash_func(element, element_length);
380 zend_op_array *opa = xdebug_lib_get_active_func_oparray();
381 zval **CV;
382
383 while (i < opa->last_var) {
384 if (ZSTR_H(opa->vars[i]) == hash_value &&
385 ZSTR_LEN(opa->vars[i]) == element_length &&
386 strncmp(STR_NAME_VAL(opa->vars[i]), element, element_length) == 0)
387 {
388 zval *CV_z = ZEND_CALL_VAR_NUM(xdebug_lib_get_active_data(), i);
389 CV = &CV_z;
390 if (CV) {
391 ZVAL_COPY(&tmp_retval, *CV);
392 goto cleanup;
393 }
394 }
395 i++;
396 }
397 }
398 free(element);
399 ht = xdebug_lib_get_active_symbol_table();
400
401 XDEBUG_BREAK_INTENTIONALLY_MISSING
402
403 case XF_ST_ARRAY_INDEX_ASSOC:
404 element = prepare_search_key(name, &name_length, "", 0);
405 xdebug_stripcslashes(element, (int *) &name_length);
406
407 /* Handle "this" in a different way */
408 if (type == XF_ST_ROOT && strcmp("this", element) == 0) {
409 if (xdebug_lib_has_active_object()) {
410 ZVAL_COPY(&tmp_retval, xdebug_lib_get_active_object());
411 } else {
412 ZVAL_NULL(&tmp_retval);
413 }
414 goto cleanup;
415 }
416
417 if (ht) {
418 zval *tmp = zend_hash_str_find(ht, element, name_length);
419 if (tmp != NULL) {
420 ZVAL_COPY(&tmp_retval, tmp);
421 goto cleanup;
422 }
423 }
424 break;
425
426 case XF_ST_ARRAY_INDEX_NUM:
427 element = prepare_search_key(name, &name_length, "", 0);
428 if (ht) {
429 zval *tmp = zend_hash_index_find(ht, strtoull(element, NULL, 10));
430 if (tmp != NULL) {
431 ZVAL_COPY(&tmp_retval, tmp);
432 goto cleanup;
433 }
434 }
435 break;
436
437 case XF_ST_ESCAPED_OBJ_PROPERTY:
438 name = xdstrndup(name, name_length);
439 free_duplicated_name = 1;
440 xdebug_stripcslashes(name, (int *) &name_length);
441
442 XDEBUG_BREAK_INTENTIONALLY_MISSING
443
444 case XF_ST_OBJ_PROPERTY:
445 /* Let's see if there is a debug handler */
446 if (value_in && Z_TYPE_P(value_in) == IS_OBJECT) {
447 #if PHP_VERSION_ID >= 70400
448 myht = xdebug_objdebug_pp(&value_in, XDEBUG_VAR_OBJDEBUG_DEFAULT);
449 #else
450 myht = xdebug_objdebug_pp(&value_in, &is_temp, XDEBUG_VAR_OBJDEBUG_DEFAULT);
451 #endif
452 if (myht) {
453 /* As a normal (public) property */
454 zval *tmp = zend_symtable_str_find(myht, name, name_length);
455 if (tmp != NULL) {
456 ZVAL_COPY(&tmp_retval, tmp);
457 #if PHP_VERSION_ID >= 70400
458 zend_release_properties(myht);
459 #else
460 xdebug_var_maybe_destroy_ht(myht, is_temp);
461 #endif
462 goto cleanup;
463 }
464
465 /* As a private property */
466 {
467 char *unmangled = replace_star_by_null(name, name_length);
468 zval *tmp = zend_symtable_str_find(myht, unmangled, name_length);
469 if (tmp != NULL) {
470 ZVAL_COPY(&tmp_retval, tmp);
471 #if PHP_VERSION_ID >= 70400
472 zend_release_properties(myht);
473 #else
474 xdebug_var_maybe_destroy_ht(myht, is_temp);
475 #endif
476 xdfree(unmangled);
477 goto cleanup;
478 }
479 xdfree(unmangled);
480 }
481
482 #if PHP_VERSION_ID >= 70400
483 zend_release_properties(myht);
484 #else
485 xdebug_var_maybe_destroy_ht(myht, is_temp);
486 #endif
487 }
488 }
489 /* First we try an object handler */
490 if (cce) {
491 zval *tmp_val;
492 #if PHP_VERSION_ID >= 80000
493 tmp_val = zend_read_property(cce, Z_OBJ_P(value_in), name, name_length, 1, &tmp_retval);
494 #else
495 tmp_val = zend_read_property(cce, value_in, name, name_length, 1, &tmp_retval);
496 #endif
497 if (tmp_val != &tmp_retval && tmp_val != &EG(uninitialized_zval)) {
498 ZVAL_COPY(&tmp_retval, tmp_val);
499 goto cleanup;
500 }
501
502 if (EG(exception)) {
503 zend_clear_exception();
504 }
505 }
506
507 /* Then we try a public property */
508 element = prepare_search_key(name, &element_length, "", 0);
509 if (ht) {
510 zval *tmp = zend_symtable_str_find(ht, element, element_length);
511 if (tmp != NULL) {
512 ZVAL_COPY(&tmp_retval, tmp);
513 goto cleanup;
514 }
515 }
516 element_length = name_length;
517
518 /* Then we try it again as protected property */
519 free(element);
520 element = prepare_search_key(name, &element_length, "*", 1);
521 if (ht) {
522 zval *tmp = zend_hash_str_find(ht, element, element_length);
523 if (tmp != NULL) {
524 ZVAL_COPY(&tmp_retval, tmp);
525 goto cleanup;
526 }
527 }
528 element_length = name_length;
529
530 /* Then we try it again as private property */
531 free(element);
532 element = prepare_search_key(name, &element_length, ccn, ccnl);
533 if (ht) {
534 zval *tmp = zend_hash_str_find(ht, element, element_length);
535 if (tmp != NULL) {
536 ZVAL_COPY(&tmp_retval, tmp);
537 goto cleanup;
538 }
539 }
540 element_length = name_length;
541
542 /* All right, time for a mega hack. It's SplObjectStorage access time! */
543 if (strncmp(ccn, "SplObjectStorage", ccnl) == 0 && strncmp(name, "storage", name_length) == 0) {
544 #if PHP_VERSION_ID >= 70400
545 zval *tmp = get_splobjectstorage_storage(value_in, &myht);
546 #else
547 zval *tmp = get_splobjectstorage_storage(value_in, &myht, &is_temp);
548 #endif
549 element = NULL;
550 if (tmp != NULL) {
551 ZVAL_COPY(&tmp_retval, tmp);
552 #if PHP_VERSION_ID >= 70400
553 zend_release_properties(myht);
554 #else
555 xdebug_var_maybe_destroy_ht(myht, is_temp);
556 #endif
557 goto cleanup;
558 }
559 #if PHP_VERSION_ID >= 70400
560 zend_release_properties(myht);
561 #else
562 xdebug_var_maybe_destroy_ht(myht, is_temp);
563 #endif
564 }
565
566 break;
567 }
568
569 cleanup:
570 if (element) {
571 free(element);
572 }
573 if (free_duplicated_name && name) {
574 xdfree(name);
575 }
576
577 zval_ptr_dtor_nogc(orig_value_in);
578 ZVAL_COPY_VALUE(orig_value_in, &tmp_retval);
579 }
580
is_objectish(zval * value)581 inline static int is_objectish(zval *value)
582 {
583 switch (Z_TYPE_P(value)) {
584 case IS_OBJECT:
585 return 1;
586
587 case IS_INDIRECT:
588 if (Z_TYPE_P(Z_INDIRECT_P(value)) == IS_OBJECT) {
589 return 1;
590 }
591 break;
592
593 case IS_REFERENCE:
594 if (Z_TYPE_P(Z_REFVAL_P(value)) == IS_OBJECT) {
595 return 1;
596 }
597 break;
598 }
599
600 return 0;
601 }
602
xdebug_get_php_symbol(zval * retval,xdebug_str * name)603 void xdebug_get_php_symbol(zval *retval, xdebug_str* name)
604 {
605 int found = -1;
606 int state = 0;
607 char *ptr = name->d;
608 size_t ctr = 0;
609 char *keyword = NULL, *keyword_end = NULL;
610 int type = XF_ST_ROOT;
611 char *current_classname = NULL;
612 zend_class_entry *current_ce = NULL;
613 int cc_length = 0;
614 char quotechar = 0;
615
616 ZVAL_UNDEF(retval);
617
618 do {
619 if (ctr == name->l) {
620 found = 0;
621 } else {
622 switch (state) {
623 case 0:
624 if (ptr[ctr] == '$') {
625 keyword = &ptr[ctr] + 1;
626 break;
627 }
628 if (ptr[ctr] == ':') { /* special tricks */
629 keyword = &ptr[ctr];
630 state = 7;
631 break;
632 }
633 keyword = &ptr[ctr];
634 state = 1;
635
636 XDEBUG_BREAK_INTENTIONALLY_MISSING
637
638 case 1:
639 if (ptr[ctr] == '[') {
640 keyword_end = &ptr[ctr];
641 if (keyword) {
642 fetch_zval_from_symbol_table(retval, keyword, keyword_end - keyword, type, current_classname, cc_length, current_ce);
643 if (current_classname) {
644 efree(current_classname);
645 }
646 current_classname = NULL;
647 cc_length = 0;
648 current_ce = NULL;
649 keyword = NULL;
650 }
651 state = 3;
652 } else if (ptr[ctr] == '-') {
653 keyword_end = &ptr[ctr];
654 if (keyword) {
655 fetch_zval_from_symbol_table(retval, keyword, keyword_end - keyword, type, current_classname, cc_length, current_ce);
656 if (current_classname) {
657 efree(current_classname);
658 }
659 current_classname = NULL;
660 cc_length = 0;
661 current_ce = NULL;
662 if (is_objectish(retval)) {
663 current_classname = fetch_classname_from_zval(retval, &cc_length, ¤t_ce);
664 }
665 keyword = NULL;
666 }
667 state = 2;
668 type = XF_ST_OBJ_PROPERTY;
669 } else if (ptr[ctr] == ':') {
670 keyword_end = &ptr[ctr];
671 if (keyword) {
672 fetch_zval_from_symbol_table(retval, keyword, keyword_end - keyword, type, current_classname, cc_length, current_ce);
673 if (current_classname) {
674 efree(current_classname);
675 }
676 current_classname = NULL;
677 cc_length = 0;
678 if (is_objectish(retval)) {
679 current_classname = fetch_classname_from_zval(retval, &cc_length, ¤t_ce);
680 }
681 keyword = NULL;
682 }
683 state = 8;
684 type = XF_ST_STATIC_PROPERTY;
685 }
686 break;
687 case 2:
688 if (ptr[ctr] != '>') {
689 if (ptr[ctr] == '{') {
690 state = 11;
691 } else {
692 keyword = &ptr[ctr];
693 state = 1;
694 }
695 }
696 break;
697 case 8:
698 if (ptr[ctr] != ':') {
699 keyword = &ptr[ctr];
700 state = 1;
701 }
702 break;
703 case 3: /* Parsing in [...] */
704 /* Associative arrays */
705 if (ptr[ctr] == '\'' || ptr[ctr] == '"') {
706 state = 4;
707 keyword = &ptr[ctr] + 1;
708 quotechar = ptr[ctr];
709 type = XF_ST_ARRAY_INDEX_ASSOC;
710 }
711 /* Numerical index */
712 if (ptr[ctr] >= '0' && ptr[ctr] <= '9') {
713 cc_length = 0;
714 state = 6;
715 keyword = &ptr[ctr];
716 type = XF_ST_ARRAY_INDEX_NUM;
717 }
718 /* Numerical index starting with a - */
719 if (ptr[ctr] == '-') {
720 state = 9;
721 keyword = &ptr[ctr];
722 }
723 break;
724 case 9:
725 /* Numerical index starting with a - */
726 if (ptr[ctr] >= '0' && ptr[ctr] <= '9') {
727 state = 6;
728 type = XF_ST_ARRAY_INDEX_NUM;
729 }
730 break;
731 case 4:
732 if (ptr[ctr] == '\\') {
733 state = 10; /* Escaped character */
734 } else if (ptr[ctr] == quotechar) {
735 quotechar = 0;
736 state = 5;
737 keyword_end = &ptr[ctr];
738 fetch_zval_from_symbol_table(retval, keyword, keyword_end - keyword, type, current_classname, cc_length, current_ce);
739 if (current_classname) {
740 efree(current_classname);
741 }
742 current_classname = NULL;
743 cc_length = 0;
744 if (is_objectish(retval)) {
745 current_classname = fetch_classname_from_zval(retval, &cc_length, ¤t_ce);
746 }
747 keyword = NULL;
748 }
749 break;
750 case 10: /* Escaped character */
751 state = 4;
752 break;
753 case 5:
754 if (ptr[ctr] == ']') {
755 state = 1;
756 }
757 break;
758 case 6:
759 if (ptr[ctr] == ']') {
760 state = 1;
761 keyword_end = &ptr[ctr];
762 fetch_zval_from_symbol_table(retval, keyword, keyword_end - keyword, type, current_classname, cc_length, current_ce);
763 if (current_classname) {
764 efree(current_classname);
765 }
766 current_classname = NULL;
767 cc_length = 0;
768 if (is_objectish(retval)) {
769 current_classname = fetch_classname_from_zval(retval, &cc_length, ¤t_ce);
770 }
771 keyword = NULL;
772 }
773 break;
774 case 7: /* special cases, started with a ":" */
775 if (ptr[ctr] == ':') {
776 function_stack_entry *active_fse = xdebug_lib_get_active_stack_entry();
777 state = 1;
778 keyword_end = &ptr[ctr];
779
780 if (strncmp(keyword, "::", 2) == 0 && active_fse->function.object_class) { /* static class properties */
781 zend_class_entry *ce = zend_fetch_class(active_fse->function.object_class, ZEND_FETCH_CLASS_SELF);
782
783 current_classname = estrdup(STR_NAME_VAL(ce->name));
784 cc_length = strlen(STR_NAME_VAL(ce->name));
785 current_ce = ce;
786 keyword = &ptr[ctr] + 1;
787
788 type = XF_ST_STATIC_ROOT;
789 } else {
790 keyword = NULL;
791 }
792 }
793 break;
794
795 case 11:
796 if (ptr[ctr] == '\'' || ptr[ctr] == '"') {
797 state = 12;
798 keyword = &ptr[ctr] + 1;
799 quotechar = ptr[ctr];
800 type = XF_ST_ESCAPED_OBJ_PROPERTY;
801 }
802 break;
803
804 case 12: /* Inside {" */
805 if (ptr[ctr] == '\\') {
806 state = 13; /* Escaped character */
807 } else if (ptr[ctr] == quotechar) {
808 quotechar = 0;
809 state = 14;
810 keyword_end = &ptr[ctr];
811 fetch_zval_from_symbol_table(retval, keyword, keyword_end - keyword, type, current_classname, cc_length, current_ce);
812 if (current_classname) {
813 efree(current_classname);
814 }
815 current_classname = NULL;
816 cc_length = 0;
817 if (is_objectish(retval)) {
818 current_classname = fetch_classname_from_zval(retval, &cc_length, ¤t_ce);
819 }
820 keyword = NULL;
821 }
822 break;
823 case 13: /* Escaped character */
824 state = 12;
825 break;
826 case 14:
827 if (ptr[ctr] == '}') {
828 state = 1;
829 }
830 break;
831 }
832 ctr++;
833 }
834 } while (found < 0);
835 if (keyword != NULL) {
836 fetch_zval_from_symbol_table(retval, keyword, &ptr[ctr] - keyword, type, current_classname, cc_length, current_ce);
837 }
838 if (current_classname) {
839 efree(current_classname);
840 }
841 }
842
843
844 #if PHP_VERSION_ID >= 70400
xdebug_get_property_type(zval * object,zval * val)845 xdebug_str* xdebug_get_property_type(zval* object, zval *val)
846 {
847 xdebug_str *type_str = NULL;
848 zend_property_info *info;
849
850 if (Z_TYPE_P(val) != IS_INDIRECT) {
851 return NULL;
852 }
853 val = Z_INDIRECT_P(val);
854
855 info = zend_get_typed_property_info_for_slot(Z_OBJ_P(object), val);
856
857 if (info) {
858 #if PHP_VERSION_ID >= 80000
859 zend_string *type_info = zend_type_to_string(info->type);
860 type_str = xdebug_str_new();
861
862 # if PHP_VERSION_ID >= 80100
863 if (info->flags & ZEND_ACC_READONLY) {
864 xdebug_str_add_literal(type_str, "readonly ");
865 }
866 # endif
867
868 xdebug_str_add_zstr(type_str, type_info);
869
870 zend_string_release(type_info);
871
872 #else
873 type_str = xdebug_str_new();
874
875 if (ZEND_TYPE_ALLOW_NULL(info->type)) {
876 xdebug_str_addc(type_str, '?');
877 }
878 if (ZEND_TYPE_IS_CLASS(info->type)) {
879 xdebug_str_add(
880 type_str,
881 ZSTR_VAL(
882 ZEND_TYPE_IS_CE(info->type) ? ZEND_TYPE_CE(info->type)->name : ZEND_TYPE_NAME(info->type)
883 ),
884 0
885 );
886 } else {
887 xdebug_str_add(type_str, zend_get_type_by_const(ZEND_TYPE_CODE(info->type)), 0);
888 }
889 #endif
890 }
891
892 return type_str;
893 }
894 #endif
895
xdebug_get_property_info(char * mangled_property,int mangled_len,const char ** modifier,char ** class_name)896 xdebug_str* xdebug_get_property_info(char *mangled_property, int mangled_len, const char **modifier, char **class_name)
897 {
898 const char *cls_name, *tmp_prop_name;
899 size_t tmp_prop_name_len;
900 xdebug_str *property_name;
901
902 zend_string *i_mangled = zend_string_init(mangled_property, mangled_len - 1, 0);
903 zend_unmangle_property_name_ex(i_mangled, &cls_name, &tmp_prop_name, &tmp_prop_name_len);
904 property_name = xdebug_str_create((char*) tmp_prop_name, tmp_prop_name_len);
905 *class_name = cls_name ? xdstrdup(cls_name) : NULL;
906 zend_string_release(i_mangled);
907
908 if (*class_name) {
909 if (*class_name[0] == '*') {
910 *modifier = "protected";
911 } else {
912 *modifier = "private";
913 }
914 } else {
915 *modifier = "public";
916 }
917
918 return property_name;
919 }
920
921 #define XDEBUG_MAX_INT 2147483647
922
xdebug_var_export_options_from_ini(void)923 xdebug_var_export_options* xdebug_var_export_options_from_ini(void)
924 {
925 xdebug_var_export_options *options;
926 options = xdmalloc(sizeof(xdebug_var_export_options));
927
928 options->max_children = XINI_LIB(display_max_children);
929 options->max_data = XINI_LIB(display_max_data);
930 options->max_depth = XINI_LIB(display_max_depth);
931 options->show_hidden = 0;
932 options->show_location = 1;
933 options->extended_properties = 0;
934 options->encode_as_extended_property = 0;
935
936 if (options->max_children == -1 || options->max_children > XDEBUG_MAX_INT) {
937 options->max_children = XDEBUG_MAX_INT;
938 } else if (options->max_children < 1) {
939 options->max_children = 0;
940 }
941
942 if (options->max_data == -1 || options->max_data > XDEBUG_MAX_INT) {
943 options->max_data = XDEBUG_MAX_INT;
944 } else if (options->max_data < 1) {
945 options->max_data = 0;
946 }
947
948 if (options->max_depth == -1 || options->max_depth > 1023) {
949 options->max_depth = 1023;
950 } else if (options->max_depth < 1) {
951 options->max_depth = 0;
952 }
953
954 options->runtime = (xdebug_var_runtime_page*) xdmalloc((options->max_depth + 1) * sizeof(xdebug_var_runtime_page));
955 options->no_decoration = 0;
956
957 return options;
958 }
959
960 xdebug_var_export_options xdebug_var_nolimit_options = { XDEBUG_MAX_INT, XDEBUG_MAX_INT, 1023, 1, 0, 0, 0, NULL, 0 };
961
xdebug_var_get_nolimit_options(void)962 xdebug_var_export_options* xdebug_var_get_nolimit_options(void)
963 {
964 return &xdebug_var_nolimit_options;
965 }
966
967 /*****************************************************************************
968 ** Normal variable printing routines
969 */
970
971 #define XDEBUG_VAR_ATTR_TEXT 0
972 #define XDEBUG_VAR_ATTR_FANCY 1
973
xdebug_add_variable_attributes(xdebug_str * str,zval * struc,zend_bool html)974 void xdebug_add_variable_attributes(xdebug_str *str, zval *struc, zend_bool html)
975 {
976 if (html) {
977 xdebug_str_add_literal(str, "<i>(");
978 } else {
979 xdebug_str_add_literal(str, "(");
980 }
981
982 if (Z_TYPE_P(struc) >= IS_STRING && Z_TYPE_P(struc) != IS_INDIRECT) {
983 if (Z_TYPE_P(struc) == IS_STRING && ZSTR_IS_INTERNED(Z_STR_P(struc))) {
984 xdebug_str_add_literal(str, "interned");
985 } else if (Z_TYPE_P(struc) == IS_ARRAY && (GC_FLAGS(Z_ARRVAL_P(struc)) & IS_ARRAY_IMMUTABLE)) {
986 xdebug_str_add_literal(str, "immutable");
987 } else {
988 xdebug_str_add_fmt(str, "refcount=%d", Z_REFCOUNT_P(struc));
989 }
990 xdebug_str_add_fmt(str, ", is_ref=%d", Z_TYPE_P(struc) == IS_REFERENCE);
991 } else {
992 xdebug_str_add_literal(str, "refcount=0, is_ref=0");
993 }
994
995 if (html) {
996 xdebug_str_add_literal(str, ")</i>");
997 } else {
998 xdebug_str_add_literal(str, ")=");
999 }
1000 }
1001
1002
1003 /*****************************************************************************
1004 ** XML encoding function
1005 */
1006 static const char xml_encode_count[256] = {
1007 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 1, 1, 5, 1, 1,
1008 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1009 1, 1, 6, 1, 1, 1, 5, 5, 1, 1, 1, 1, 1, 1, 1, 1,
1010 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, 1, 4, 1,
1011 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1012 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1013 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1014 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1015 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1016 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1017 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1018 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1019 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1020 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1021 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1022 };
1023
1024 static const char* xml_encode_map[64] = {
1025 "�", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, " ", NULL, NULL, " ", NULL, NULL,
1026 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1027 NULL, NULL, """, NULL, NULL, NULL, "&", "'", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1028 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "<", NULL, ">", NULL,
1029 };
1030
xdebug_xmlize(char * s_string,size_t len,size_t * newlen)1031 char* xdebug_xmlize(char *s_string, size_t len, size_t *newlen)
1032 {
1033 int i, w_pos;
1034 int encoded_string_length = 0;
1035 char *new_string;
1036 const unsigned char *string = (unsigned char*) s_string;
1037
1038 /* Quick bailout for empty strings */
1039 if (!len) {
1040 *newlen = 0;
1041 return estrdup("");
1042 }
1043
1044 /* Calculate new memory requirement */
1045 for (i = 0; i < len; i++) {
1046 encoded_string_length += xml_encode_count[string[i]];
1047 }
1048
1049 /* No characters need to be encoded, so just duplicate and return */
1050 if (encoded_string_length == len) {
1051 *newlen = len;
1052 return estrdup(s_string);
1053 }
1054
1055 new_string = emalloc(encoded_string_length + 1);
1056 w_pos = 0;
1057 for (i = 0; i < len; i++) {
1058 int replacement_length = xml_encode_count[string[i]];
1059
1060 if (replacement_length != 1) {
1061 int j;
1062
1063 for (j = 0; j < replacement_length; j++) {
1064 new_string[w_pos] = xml_encode_map[string[i]][j];
1065 w_pos++;
1066 }
1067 continue;
1068 }
1069
1070 new_string[w_pos] = string[i];
1071 w_pos++;
1072 }
1073
1074 new_string[w_pos] = '\0';
1075 *newlen = encoded_string_length; /* remove one for null byte */
1076
1077 return new_string;
1078 }
1079
1080 /*****************************************************************************
1081 ** Function name printing function
1082 */
xdebug_create_doc_link(xdebug_func f)1083 static char* xdebug_create_doc_link(xdebug_func f)
1084 {
1085 char *tmp_target = NULL, *p, *retval;
1086
1087 switch (f.type) {
1088 case XFUNC_NORMAL: {
1089 tmp_target = xdebug_sprintf("function.%s", f.function);
1090 break;
1091 }
1092
1093 case XFUNC_STATIC_MEMBER:
1094 case XFUNC_MEMBER: {
1095 if (strcmp(f.function, "__construct") == 0) {
1096 tmp_target = xdebug_sprintf("%s.construct", ZSTR_VAL(f.object_class));
1097 } else {
1098 tmp_target = xdebug_sprintf("%s.%s", ZSTR_VAL(f.object_class), f.function);
1099 }
1100 break;
1101 }
1102 }
1103
1104 while ((p = strchr(tmp_target, '_')) != NULL) {
1105 *p = '-';
1106 }
1107
1108 retval = xdebug_sprintf("<a href='%s%s%s' target='_new'>%s</a>",
1109 (PG(docref_root) && PG(docref_root)[0]) ? PG(docref_root) : "http://www.php.net/",
1110 tmp_target, PG(docref_ext), f.function);
1111
1112 xdfree(tmp_target);
1113
1114 return retval;
1115 }
1116
xdebug_show_fname(xdebug_func f,int flags)1117 char* xdebug_show_fname(xdebug_func f, int flags)
1118 {
1119 switch (f.type) {
1120 case XFUNC_NORMAL: {
1121 if (PG(html_errors) && (flags & XDEBUG_SHOW_FNAME_ALLOW_HTML) && f.internal) {
1122 return xdebug_create_doc_link(f);
1123 } else {
1124 return xdstrdup(f.function);
1125 }
1126 break;
1127 }
1128
1129 case XFUNC_STATIC_MEMBER:
1130 case XFUNC_MEMBER: {
1131 if (PG(html_errors) && (flags & XDEBUG_SHOW_FNAME_ALLOW_HTML) && f.internal) {
1132 return xdebug_create_doc_link(f);
1133 } else {
1134 if (f.scope_class && !(flags & XDEBUG_SHOW_FNAME_IGNORE_SCOPE)) {
1135 return xdebug_sprintf("%s%s%s",
1136 ZSTR_VAL(f.scope_class),
1137 f.type == XFUNC_STATIC_MEMBER ? "::" : "->",
1138 f.function ? f.function : "?"
1139 );
1140 }
1141
1142 return xdebug_sprintf("%s%s%s",
1143 f.object_class ? ZSTR_VAL(f.object_class) : "?",
1144 f.type == XFUNC_STATIC_MEMBER ? "::" : "->",
1145 f.function ? f.function : "?"
1146 );
1147 }
1148 break;
1149 }
1150
1151 case XFUNC_EVAL:
1152 return xdstrdup("eval");
1153 break;
1154
1155 case XFUNC_INCLUDE:
1156 return xdstrdup("include");
1157 break;
1158
1159 case XFUNC_INCLUDE_ONCE:
1160 return xdstrdup("include_once");
1161 break;
1162
1163 case XFUNC_REQUIRE:
1164 return xdstrdup("require");
1165 break;
1166
1167 case XFUNC_REQUIRE_ONCE:
1168 return xdstrdup("require_once");
1169 break;
1170
1171 case XFUNC_MAIN:
1172 return xdstrdup("{main}");
1173 break;
1174
1175 case XFUNC_ZEND_PASS:
1176 return xdstrdup("{zend_pass}");
1177 break;
1178
1179 #if PHP_VERSION_ID >= 80100
1180 case XFUNC_FIBER:
1181 return xdstrdup(f.function);
1182 break;
1183 #endif
1184
1185 default:
1186 return xdstrdup("{unknown}");
1187 }
1188 }
1189