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 #include "php_xdebug.h"
17
18 #include "main/php_ini.h"
19
20 #include "ext/standard/html.h"
21 #include "ext/standard/php_smart_string.h"
22 #include "zend_exceptions.h"
23 #include "zend_generators.h"
24
25 #include "monitor.h"
26 #include "stack.h"
27 #include "superglobals.h"
28
29 #include "base/filter.h"
30 #include "coverage/code_coverage.h"
31 #include "lib/compat.h"
32 #include "lib/lib_private.h"
33 #include "lib/str.h"
34 #include "lib/var_export_html.h"
35 #include "lib/var_export_line.h"
36 #include "profiler/profiler.h"
37
38 ZEND_EXTERN_MODULE_GLOBALS(xdebug)
39
40 static const char* text_formats[11] = {
41 "\n",
42 "%s: %s in %s on line %d\n",
43 "\nCall Stack:\n",
44 "%10.4F %10ld %3d. %s(",
45 "'%s'",
46 ") %s:%d\n",
47 "\n\nVariables in local scope (#%d):\n",
48 "\n",
49 " $%s = %s\n",
50 " $%s = *uninitialized*\n",
51 "SCREAM: Error suppression ignored for\n"
52 };
53
54 static const char* ansi_formats[11] = {
55 "\n",
56 "[1m[31m%s[0m: %s[22m in [31m%s[0m on line [32m%d[0m[22m\n",
57 "\n[1mCall Stack:[22m\n",
58 "%10.4F %10ld %3d. %s(",
59 "'%s'",
60 ") %s:%d\n",
61 "\n\nVariables in local scope (#%d):\n",
62 "\n",
63 " $%s = %s\n",
64 " $%s = *uninitialized*\n",
65 "[1m[31mSCREAM[0m: Error suppression ignored for\n"
66 };
67
DBCS_IBM_EBCDIC_Encoder(Charset cs)68 static const char* html_formats[13] = {
69 "<br />\n<font size='1'><table class='xdebug-error xe-%s%s' dir='ltr' border='1' cellspacing='0' cellpadding='1'>\n",
70 "<tr><th align='left' bgcolor='#f57900' colspan=\"5\"><span style='background-color: #cc0000; color: #fce94f; font-size: x-large;'>( ! )</span> %s: %s in %s on line <i>%d</i></th></tr>\n",
71 "<tr><th align='left' bgcolor='#e9b96e' colspan='5'>Call Stack</th></tr>\n<tr><th align='center' bgcolor='#eeeeec'>#</th><th align='left' bgcolor='#eeeeec'>Time</th><th align='left' bgcolor='#eeeeec'>Memory</th><th align='left' bgcolor='#eeeeec'>Function</th><th align='left' bgcolor='#eeeeec'>Location</th></tr>\n",
72 "<tr><td bgcolor='#eeeeec' align='center'>%d</td><td bgcolor='#eeeeec' align='center'>%.4F</td><td bgcolor='#eeeeec' align='right'>%ld</td><td bgcolor='#eeeeec'>%s( ",
73 "<font color='#00bb00'>'%s'</font>",
74 " )</td><td title='%s' bgcolor='#eeeeec'>%s<b>:</b>%d</td></tr>\n",
75 "<tr><th align='left' colspan='5' bgcolor='#e9b96e'>Variables in local scope (#%d)</th></tr>\n",
76 "</table></font>\n",
77 "<tr><td colspan='2' align='right' bgcolor='#eeeeec' valign='top'><pre>$%s =</pre></td><td colspan='3' bgcolor='#eeeeec'>%s</td></tr>\n",
78 "<tr><td colspan='2' align='right' bgcolor='#eeeeec' valign='top'><pre>$%s =</pre></td><td colspan='3' bgcolor='#eeeeec' valign='top'><i>Undefined</i></td></tr>\n",
79 " )</td><td title='%s' bgcolor='#eeeeec'><a style='color: black' href='%s'>%s<b>:</b>%d</a></td></tr>\n",
80 "<tr><th align='left' bgcolor='#f57900' colspan=\"5\"><span style='background-color: #cc0000; color: #fce94f; font-size: x-large;'>( ! )</span> %s: %s in <a style='color: black' href='%s'>%s</a> on line <i>%d</i></th></tr>\n",
81 "<tr><th align='left' bgcolor='#f57900' colspan=\"5\"><span style='background-color: #cc0000; color: #fce94f; font-size: x-large;'>( ! )</span> SCREAM: Error suppression ignored for</th></tr>\n"
82 };
83
84 static const char** select_formats(int html)
85 {
86 if (html) {
87 return html_formats;
88 } else if ((XINI_DEV(cli_color) == 1 && xdebug_is_output_tty()) || (XINI_DEV(cli_color) == 2)) {
89 return ansi_formats;
canEncode(char ch)90 } else {
91 return text_formats;
92 }
93 }
94
95 void xdebug_log_stack(const char *error_type_str, char *buffer, const char *error_filename, const int error_lineno)
96 {
97 char *tmp_log_message;
98 int i;
99 function_stack_entry *fse;
100
101 tmp_log_message = xdebug_sprintf( "PHP %s: %s in %s on line %d", error_type_str, buffer, error_filename, error_lineno);
102 php_log_err(tmp_log_message);
103 xdfree(tmp_log_message);
104
105 if (!XG_BASE(stack) || XDEBUG_VECTOR_COUNT(XG_BASE(stack)) < 1) {
106 return;
107 }
108
encodeArrayLoop(CharBuffer src, ByteBuffer dst)109 fse = XDEBUG_VECTOR_HEAD(XG_BASE(stack));
110
111 php_log_err((char*) "PHP Stack trace:");
112
113 for (i = 0; i < XDEBUG_VECTOR_COUNT(XG_BASE(stack)); i++, fse++)
114 {
115 int c = 0; /* Comma flag */
116 unsigned int j = 0; /* Counter */
117 char *tmp_name;
118 xdebug_str log_buffer = XDEBUG_STR_INITIALIZER;
119 int variadic_opened = 0;
120 int sent_variables = fse->varc;
121
122 if (sent_variables > 0 && fse->var[sent_variables-1].is_variadic && Z_ISUNDEF(fse->var[sent_variables-1].data)) {
123 sent_variables--;
124 }
125
126 tmp_name = xdebug_show_fname(fse->function, XDEBUG_SHOW_FNAME_DEFAULT);
127 xdebug_str_add_fmt(&log_buffer, "PHP %3d. %s(", fse->level, tmp_name);
128 xdfree(tmp_name);
129
130 /* Printing vars */
131 for (j = 0; j < sent_variables; j++) {
132 xdebug_str *tmp_value;
133
134 if (c) {
135 xdebug_str_add_literal(&log_buffer, ", ");
136 } else {
137 c = 1;
138 }
139
140 if (fse->var[j].is_variadic) {
141 xdebug_str_add_literal(&log_buffer, "...");
142 variadic_opened = 1;
143 }
144
145 if (fse->var[j].name) {
146 xdebug_str_add_fmt(&log_buffer, "$%s = ", ZSTR_VAL(fse->var[j].name));
147 }
148
149 if (fse->var[j].is_variadic) {
150 xdebug_str_add_literal(&log_buffer, "variadic(");
151 c = 0;
152 continue;
153 }
154
155 if (!Z_ISUNDEF(fse->var[j].data)) {
156 tmp_value = xdebug_get_zval_value_line(&fse->var[j].data, 0, NULL);
157 xdebug_str_add_str(&log_buffer, tmp_value);
158 xdebug_str_free(tmp_value);
159 } else {
160 xdebug_str_add_literal(&log_buffer, "*uninitialized*");
161 }
162 }
163
164 if (variadic_opened) {
165 xdebug_str_add_literal(&log_buffer, ")");
166 }
167
168 xdebug_str_add_fmt(&log_buffer, ") %s:%d", ZSTR_VAL(fse->filename), fse->lineno);
169 php_log_err(log_buffer.d);
170 xdebug_str_destroy(&log_buffer);
171 }
172 }
173
174 void xdebug_append_error_head(xdebug_str *str, int html, const char *error_type_str)
175 {
176 const char **formats = select_formats(html);
177
178 if (html) {
179 xdebug_str_add_fmt(str, formats[0], error_type_str, XG_DEV(in_at) ? " xe-scream" : "");
encodeBufferLoop(CharBuffer src, ByteBuffer dst)180 if (XG_DEV(in_at)) {
181 xdebug_str_add_const(str, formats[12]);
182 }
183 } else {
184 xdebug_str_add_const(str, formats[0]);
185 if (XG_DEV(in_at)) {
186 xdebug_str_add_const(str, formats[10]);
187 }
188 }
189 }
190
191 void xdebug_append_error_description(xdebug_str *str, int html, const char *error_type_str, const char *buffer, const char *error_filename, const int error_lineno)
192 {
193 const char **formats = select_formats(html);
194 char *escaped;
195
196 if (!html) {
197 escaped = estrdup(buffer);
198 } else {
199 zend_string *tmp;
200 char *first_closing = strchr(buffer, ']');
201
202 /* We do need to escape HTML entities here, as HTML chars could be in
203 * the error message. However, PHP in some circumstances also adds an
204 * HTML link to a manual page. That bit, we don't need to escape. So
205 * this bit of code finds the portion that doesn't need escaping, adds
206 * it to a tmp string, and then adds an HTML escaped string for the
207 * rest of the original buffer. */
208 if (first_closing && strstr(buffer, "() [<a href=") != NULL) {
209 smart_string special_escaped = { 0, 0, 0 };
210
211 *first_closing = '\0';
212 first_closing++;
213
214 smart_string_appends(&special_escaped, buffer);
215 tmp = php_escape_html_entities((unsigned char *) first_closing, strlen(first_closing), 0, 0, NULL);
216 smart_string_appends(&special_escaped, tmp->val);
217 zend_string_free(tmp);
218
219 smart_string_0(&special_escaped);
220 escaped = estrdup(special_escaped.c);
221 smart_string_free(&special_escaped);
222 } else if (strncmp(buffer, "assert()", 8) == 0) {
223 /* Also don't escape if we're in an assert, as things are already
224 * escaped. It's all nice and consistent ey? */
225 escaped = estrdup(buffer);
226 } else {
227 tmp = php_escape_html_entities((unsigned char *) buffer, strlen(buffer), 0, 0, NULL);
228 escaped = estrdup(tmp->val);
229 zend_string_free(tmp);
230 }
231 }
232
233 if (strlen(XINI_LIB(file_link_format)) > 0 && html && strcmp(error_filename, "Unknown") != 0) {
234 char *file_link;
235
236 xdebug_format_file_link(&file_link, error_filename, error_lineno);
237 xdebug_str_add_fmt(str, formats[11], error_type_str, escaped, file_link, error_filename, error_lineno);
238 xdfree(file_link);
239 } else {
240 xdebug_str_add_fmt(str, formats[1], error_type_str, escaped, error_filename, error_lineno);
241 }
242
243 efree(escaped);
244 }
245
encodeLoop(CharBuffer src, ByteBuffer dst)246 static void add_single_value(xdebug_str *str, zval *zv, int html)
247 {
248 xdebug_str *tmp_value = NULL;
249 char *tmp_html_value = NULL;
250 size_t newlen;
251
252 if (html) {
253 tmp_value = xdebug_get_zval_value_line(zv, 0, NULL);
254 tmp_html_value = xdebug_xmlize(tmp_value->d, tmp_value->l, &newlen);
255
256 xdebug_str_add_literal(str, "<span>");
257 xdebug_str_add(str, tmp_html_value, 0);
258 xdebug_str_add_literal(str, "</span>");
259
260 xdebug_str_free(tmp_value);
261 efree(tmp_html_value);
262 } else {
263 tmp_value = xdebug_get_zval_value_line(zv, 0, NULL);
264
265 if (tmp_value) {
266 xdebug_str_add_str(str, tmp_value);
267 xdebug_str_free(tmp_value);
268 } else {
269 xdebug_str_add_literal(str, "???");
270 }
271 }
272 }
273
274 #define XDEBUG_VAR_FORMAT_INITIALISED 0
275 #define XDEBUG_VAR_FORMAT_UNINITIALISED 1
276
277 static const char* text_var_formats[2] = {
278 " $%s = %s\n",
279 " $%s = *uninitialized*\n",
280 };
281
282 static const char* ansi_var_formats[2] = {
283 " $%s = %s\n",
284 " $%s = *uninitialized*\n",
285 };
286
287 static const char* html_var_formats[2] = {
288 "<tr><td colspan='2' align='right' bgcolor='#eeeeec' valign='top'><pre>$%s =</pre></td><td colspan='3' bgcolor='#eeeeec'>%s</td></tr>\n",
289 "<tr><td colspan='2' align='right' bgcolor='#eeeeec' valign='top'><pre>$%s =</pre></td><td colspan='3' bgcolor='#eeeeec' valign='top'><i>Undefined</i></td></tr>\n",
290 };
291
292 static const char** get_var_format_string(int html)
293 {
294 if (html) {
295 return html_var_formats;
296 } else if ((XINI_DEV(cli_color) == 1 && xdebug_is_output_tty()) || (XINI_DEV(cli_color) == 2)) {
297 return ansi_var_formats;
298 } else {
299 return text_var_formats;
300 }
301 }
302
303
304 static void xdebug_dump_used_var_with_contents(void *htmlq, xdebug_hash_element* he, void *argument)
305 {
306 int html = *(int*) htmlq;
307 zval zvar;
308 xdebug_str *contents;
309 xdebug_str *name = (xdebug_str*) he->ptr;
310 HashTable *tmp_ht;
311 const char **formats;
312 xdebug_str *str = (xdebug_str *) argument;
313
314 if (!he->ptr) {
315 return;
316 }
317
318 /* Bail out on $this and $GLOBALS */
319 if (strcmp(name->d, "this") == 0 || strcmp(name->d, "GLOBALS") == 0) {
320 return;
321 }
322
323 if (EG(current_execute_data) && !(ZEND_CALL_INFO(EG(current_execute_data)) & ZEND_CALL_HAS_SYMBOL_TABLE)) {
324 zend_rebuild_symbol_table();
325 }
326
327 tmp_ht = xdebug_lib_get_active_symbol_table();
328 {
329 zend_execute_data *ex = EG(current_execute_data);
330 while (ex && (!ex->func || !ZEND_USER_CODE(ex->func->type))) {
331 ex = ex->prev_execute_data;
332 }
333 if (ex) {
334 xdebug_lib_set_active_data(ex);
335 xdebug_lib_set_active_symbol_table(ex->symbol_table);
336 }
337 }
338
339 xdebug_get_php_symbol(&zvar, name);
340 xdebug_lib_set_active_symbol_table(tmp_ht);
341
342 formats = get_var_format_string(PG(html_errors));
343
344 if (Z_TYPE(zvar) == IS_UNDEF) {
345 xdebug_str_add_fmt(str, formats[XDEBUG_VAR_FORMAT_UNINITIALISED], name->d);
346 return;
347 }
348
349 if (html) {
350 contents = xdebug_get_zval_value_html(NULL, &zvar, 0, NULL);
351 } else {
352 contents = xdebug_get_zval_value_line(&zvar, 0, NULL);
353 }
354
355 if (contents) {
356 xdebug_str_add_fmt(str, formats[XDEBUG_VAR_FORMAT_INITIALISED], name->d, contents->d);
357 } else {
358 xdebug_str_add_fmt(str, formats[XDEBUG_VAR_FORMAT_UNINITIALISED], name->d);
359 }
360
361 if (contents) {
362 xdebug_str_free(contents);
363 }
364 zval_ptr_dtor_nogc(&zvar);
365 }
366
367 void xdebug_append_printable_stack(xdebug_str *str, int html)
368 {
369 int printed_frames = 0;
370 const char **formats = select_formats(html);
371 int i;
372 function_stack_entry *fse;
373
374 if (!XG_BASE(stack) || XDEBUG_VECTOR_COUNT(XG_BASE(stack)) < 1) {
375 return;
376 }
377
378 fse = XDEBUG_VECTOR_HEAD(XG_BASE(stack));
379
380 xdebug_str_add_const(str, formats[2]);
381
382 for (i = 0; i < XDEBUG_VECTOR_COUNT(XG_BASE(stack)); i++, fse++)
383 {
384 int c = 0; /* Comma flag */
385 unsigned int j = 0; /* Counter */
386 char *tmp_name;
387 int variadic_opened = 0;
388 int sent_variables = fse->varc;
389
390 if (sent_variables > 0 && fse->var[sent_variables-1].is_variadic && Z_ISUNDEF(fse->var[sent_variables-1].data)) {
391 sent_variables--;
392 }
393
394 if (xdebug_is_stack_frame_filtered(XDEBUG_FILTER_STACK, fse)) {
395 continue;
396 }
397 tmp_name = xdebug_show_fname(fse->function, html ? XDEBUG_SHOW_FNAME_ALLOW_HTML : XDEBUG_SHOW_FNAME_DEFAULT);
398 if (html) {
399 xdebug_str_add_fmt(str, formats[3], fse->level, XDEBUG_SECONDS_SINCE_START(fse->nanotime), fse->memory, tmp_name);
400 } else {
401 xdebug_str_add_fmt(str, formats[3], XDEBUG_SECONDS_SINCE_START(fse->nanotime), fse->memory, fse->level, tmp_name);
402 }
403 xdfree(tmp_name);
404
405 /* Printing vars */
406 for (j = 0; j < sent_variables; j++) {
407 if (c) {
408 xdebug_str_add_literal(str, ", ");
409 } else {
410 c = 1;
411 }
412
413 if (
414 (fse->var[j].is_variadic && Z_ISUNDEF(fse->var[j].data))
415 ) {
416 xdebug_str_add_literal(str, "...");
417 }
418
419 if (fse->var[j].name) {
420 if (html) {
421 xdebug_str_add_literal(str, "<span>$");
422 xdebug_str_add_zstr(str, fse->var[j].name);
423 xdebug_str_add_literal(str, " = </span>");
424 } else {
425 xdebug_str_add_literal(str, "$");
426 xdebug_str_add_zstr(str, fse->var[j].name);
427 xdebug_str_add_literal(str, " = ");
428 }
429 }
430
431 if (!variadic_opened && fse->var[j].is_variadic && Z_ISUNDEF(fse->var[j].data)) {
432 if (html) {
433 xdebug_str_add_literal(str, "<i>variadic</i>(");
434 } else {
435 xdebug_str_add_literal(str, "variadic(");
436 }
437 c = 0;
438 variadic_opened = 1;
439 continue;
440 }
441
442 if (!Z_ISUNDEF(fse->var[j].data)) {
443 add_single_value(str, &fse->var[j].data, html);
444 } else {
445 xdebug_str_add_literal(str, "???");
446 }
447 }
448
449 if (variadic_opened) {
450 xdebug_str_add_literal(str, ")");
451 }
452
453 if (fse->include_filename) {
454 if (html) {
455 xdebug_str_add_literal(str, "<font color='#00bb00'>'");
456 xdebug_str_add_zstr(str, fse->include_filename);
457 xdebug_str_add_literal(str, "</font>");
458 } else {
459 xdebug_str_addc(str, '\'');
460 xdebug_str_add_zstr(str, fse->include_filename);
461 xdebug_str_addc(str, '\'');
462 }
463 }
464
465 if (html) {
466 char *formatted_filename;
467 xdebug_format_filename(&formatted_filename, "...%s%n", fse->filename);
468
469 if (strlen(XINI_LIB(file_link_format)) > 0 && strcmp(ZSTR_VAL(fse->filename), "Unknown") != 0) {
470 char *file_link;
471
472 xdebug_format_file_link(&file_link, ZSTR_VAL(fse->filename), fse->lineno);
473 xdebug_str_add_fmt(str, formats[10], ZSTR_VAL(fse->filename), file_link, formatted_filename, fse->lineno);
474 xdfree(file_link);
475 } else {
476 xdebug_str_add_fmt(str, formats[5], ZSTR_VAL(fse->filename), formatted_filename, fse->lineno);
477 }
478
479 xdfree(formatted_filename);
480 } else {
481 xdebug_str_add_fmt(str, formats[5], ZSTR_VAL(fse->filename), fse->lineno);
482 }
483
484 printed_frames++;
485 if (XINI_DEV(max_stack_frames) > 0 && printed_frames >= XINI_DEV(max_stack_frames)) {
486 break;
487 }
488 }
489
490 if (XINI_DEV(dump_globals) && !(XINI_DEV(dump_once) && XG_LIB(dumped))) {
491 char *tmp = xdebug_get_printable_superglobals(html);
492
493 if (tmp) {
494 xdebug_str_add(str, tmp, 1);
495 }
496 XG_LIB(dumped) = 1;
497 }
498
499 if (XINI_DEV(show_local_vars) && XG_BASE(stack) && XDEBUG_VECTOR_TAIL(XG_BASE(stack))) {
500 int scope_nr = XDEBUG_VECTOR_COUNT(XG_BASE(stack));
501
502 fse = XDEBUG_VECTOR_TAIL(XG_BASE(stack));
503 if (fse->user_defined == XDEBUG_BUILT_IN && xdebug_vector_element_is_valid(XG_BASE(stack), fse -1)) {
504 fse = fse - 1;
505 scope_nr--;
506 }
507 if (fse->declared_vars && fse->declared_vars->size) {
508 xdebug_hash *tmp_hash;
509
510 xdebug_str_add_fmt(str, formats[6], scope_nr);
511 tmp_hash = xdebug_declared_var_hash_from_llist(fse->declared_vars);
512 xdebug_hash_apply_with_argument(tmp_hash, (void*) &html, xdebug_dump_used_var_with_contents, (void *) str);
513 xdebug_hash_destroy(tmp_hash);
514 }
515 }
516 }
517
518 void xdebug_append_error_footer(xdebug_str *str, int html)
519 {
520 const char **formats = select_formats(html);
521
522 xdebug_str_add_const(str, formats[7]);
523 }
524
525 char *xdebug_get_printable_stack(int html, int error_type, const char *buffer, const char *error_filename, const int error_lineno, int include_decription)
526 {
527 char *prepend_string;
528 char *append_string;
529 char *error_type_str = xdebug_error_type(error_type);
530 char *error_type_str_simple = xdebug_error_type_simple(error_type);
531 xdebug_str str = XDEBUG_STR_INITIALIZER;
532
533 prepend_string = INI_STR((char*) "error_prepend_string");
534 append_string = INI_STR((char*) "error_append_string");
535
536 if (prepend_string) {
537 xdebug_str_add(&str, prepend_string, 0);
538 }
539 xdebug_append_error_head(&str, html, error_type_str_simple);
540 if (include_decription) {
541 xdebug_append_error_description(&str, html, error_type_str, buffer, error_filename, error_lineno);
542 }
543 xdebug_append_printable_stack(&str, html);
544 xdebug_append_error_footer(&str, html);
545 if (append_string) {
546 xdebug_str_add(&str, append_string, 0);
547 }
548
549 xdfree(error_type_str);
550 xdfree(error_type_str_simple);
551
552 return str.d;
553 }
554
555 static void php_output_error(const char *error)
556 {
557 #ifdef PHP_DISPLAY_ERRORS_STDERR
558 if (PG(display_errors) == PHP_DISPLAY_ERRORS_STDERR) {
559 fputs(error, stderr);
560 fflush(stderr);
561 return;
562 }
563 #endif
564 php_printf("%s", error);
565 }
566
567 char *xdebug_strip_php_stack_trace(char *buffer)
568 {
569 char *tmp_buf, *p;
570
571 if (strncmp(buffer, "Uncaught ", 9) != 0) {
572 return NULL;
573 }
574
575 /* find first new line */
576 p = strchr(buffer, '\n');
577 if (!p) {
578 p = buffer + strlen(buffer);
579 } else {
580 /* find the last " in ", which isn't great and might not work... but in most cases it will */
581 p = xdebug_strrstr(buffer, " in ");
582 if (!p) {
583 p = buffer + strlen(buffer);
584 }
585 }
586 /* Create new buffer */
587 tmp_buf = calloc(p - buffer + 1, 1);
588 strncpy(tmp_buf, buffer, p - buffer);
589
590 return tmp_buf;
591 }
592
593 static char *xdebug_handle_stack_trace(int type, char *error_type_str, const char *error_filename, const unsigned int error_lineno, char *buffer)
594 {
595 char *printable_stack;
596 char *tmp_buf;
597
598 /* We need to see if we have an uncaught exception fatal error now */
599 if (type == E_ERROR && ((tmp_buf = xdebug_strip_php_stack_trace(buffer)) != NULL)) {
600 xdebug_str str = XDEBUG_STR_INITIALIZER;
601
602 /* Append error */
603 xdebug_append_error_head(&str, PG(html_errors), "uncaught-exception");
604 xdebug_append_error_description(&str, PG(html_errors), error_type_str, tmp_buf, error_filename, error_lineno);
605 xdebug_append_printable_stack(&str, PG(html_errors));
606 if (XG_BASE(last_exception_trace)) {
607 xdebug_str_add(&str, XG_BASE(last_exception_trace), 0);
608 }
609 xdebug_append_error_footer(&str, PG(html_errors));
610
611 free(tmp_buf);
612 printable_stack = str.d;
613 } else {
614 printable_stack = xdebug_get_printable_stack(PG(html_errors), type, buffer, error_filename, error_lineno, 1);
615 }
616
617 return printable_stack;
618 }
619
620 #if PHP_VERSION_ID >= 80000
621 static void clear_last_error()
622 {
623 if (PG(last_error_message)) {
624 zend_string_release(PG(last_error_message));
625 PG(last_error_message) = NULL;
626 }
627 if (PG(last_error_file)) {
628 # if PHP_VERSION_ID >= 80100
629 zend_string_release(PG(last_error_file));
630 # else
631 free(PG(last_error_file));
632 # endif
633 PG(last_error_file) = NULL;
634 }
635 }
636 #else
637 static void clear_last_error()
638 {
639 if (PG(last_error_message)) {
640 char *s = PG(last_error_message);
641 PG(last_error_message) = NULL;
642 free(s);
643 }
644 if (PG(last_error_file)) {
645 char *s = PG(last_error_file);
646 PG(last_error_file) = NULL;
647 free(s);
648 }
649 }
650 #endif
651
652 /* Error callback for formatting stack traces */
653 #if PHP_VERSION_ID >= 80100
654 void xdebug_develop_error_cb(int orig_type, zend_string *error_filename, const unsigned int error_lineno, zend_string *message)
655 {
656 #elif PHP_VERSION_ID >= 80000
657 void xdebug_develop_error_cb(int orig_type, const char *error_filename, const unsigned int error_lineno, zend_string *message)
658 {
659 #else
660 void xdebug_develop_error_cb(int orig_type, const char *error_filename, const unsigned int error_lineno, const char *format, va_list args)
661 {
662 char *buffer;
663 int buffer_len;
664 #endif
665
666 char *error_type_str;
667 int display;
668 int type = orig_type & E_ALL;
669 error_handling_t error_handling;
670 zend_class_entry *exception_class;
671
672 #if PHP_VERSION_ID < 80000
673 buffer_len = vspprintf(&buffer, PG(log_errors_max_len), format, args);
674 #endif
675
676 error_type_str = xdebug_error_type(type);
677
678 /* check for repeated errors to be ignored */
679 if (PG(ignore_repeated_errors) && PG(last_error_message)) {
680 /* no check for PG(last_error_file) is needed since it cannot
681 * be NULL if PG(last_error_message) is not NULL */
682
683 #if PHP_VERSION_ID >= 80000
684 if (!zend_string_equals(PG(last_error_message), message)
685 #else
686 if (strcmp(PG(last_error_message), buffer) != 0
687 #endif
688 ||
689 (!PG(ignore_repeated_source) && (
690 (PG(last_error_lineno) != (int)error_lineno) ||
691 #if PHP_VERSION_ID >= 80100
692 !zend_string_equals(PG(last_error_file), error_filename)
693 #else
694 strcmp(PG(last_error_file), error_filename) != 0
695 #endif
696 ))
697 ) {
698 display = 1;
699 } else {
700 display = 0;
701 }
702 } else {
703 display = 1;
704 }
705
706 #if PHP_VERSION_ID < 70300
707 /* Store last error message for error_get_last() */
708 if (display) {
709 clear_last_error();
710 if (!error_filename) {
711 error_filename = "Unknown";
712 }
713 PG(last_error_type) = type;
714 PG(last_error_message) = strdup(buffer);
715 PG(last_error_file) = strdup(error_filename);
716 PG(last_error_lineno) = error_lineno;
717 }
718 #endif
719 error_handling = EG(error_handling);
720 exception_class = EG(exception_class);
721 #if PHP_VERSION_ID >= 70300
722 /* according to error handling mode, throw exception or show it */
723 if (error_handling == EH_THROW) {
724 #else
725 /* according to error handling mode, suppress error, throw exception or show it */
726 if (error_handling != EH_NORMAL) {
727 #endif
728 switch (type) {
729 case E_ERROR:
730 case E_CORE_ERROR:
731 case E_COMPILE_ERROR:
732 case E_USER_ERROR:
733 case E_PARSE:
734 /* fatal errors are real errors and cannot be made exceptions */
735 break;
736 case E_STRICT:
737 case E_DEPRECATED:
738 case E_USER_DEPRECATED:
739 /* for the sake of BC to old damaged code */
740 break;
741 case E_NOTICE:
742 case E_USER_NOTICE:
743 /* notices are no errors and are not treated as such like E_WARNINGS */
744 break;
745 default:
746 /* throw an exception if we are in EH_THROW mode
747 * but DO NOT overwrite a pending exception
748 */
749 #if PHP_VERSION_ID >= 70300
750 if (!EG(exception)) {
751 #else
752 if (error_handling == EH_THROW && !EG(exception)) {
753 #endif
754 #if PHP_VERSION_ID >= 80000
755 zend_throw_error_exception(exception_class, message, 0, type);
756 #else
757 zend_throw_error_exception(exception_class, buffer, 0, type);
758 #endif
759 }
760 #if PHP_VERSION_ID < 80000
761 efree(buffer);
762 #endif
763 xdfree(error_type_str);
764 return;
765 }
766 }
767
768 #if PHP_VERSION_ID >= 70300
769 /* Store last error message for error_get_last() */
770 if (display) {
771 clear_last_error();
772 if (!error_filename) {
773 # if PHP_VERSION_ID >= 80100
774 error_filename = zend_string_init(ZEND_STRL("Unknown"), 0);
775 # else
776 error_filename = "Unknown";
777 # endif
778 }
779 PG(last_error_type) = type;
780 # if PHP_VERSION_ID >= 80000
781 PG(last_error_message) = zend_string_copy(message);
782 # else
783 PG(last_error_message) = strdup(buffer);
784 # endif
785 # if PHP_VERSION_ID >= 80100
786 PG(last_error_file) = zend_string_copy(error_filename);
787 # else
788 PG(last_error_file) = strdup(error_filename);
789 # endif
790 PG(last_error_lineno) = error_lineno;
791 }
792 #endif
793
794 if ((EG(error_reporting) | XINI_DEV(force_error_reporting)) & type) {
795 /* Log to logger */
796 if (PG(log_errors)) {
797
798 #ifdef PHP_WIN32
799 if (type==E_CORE_ERROR || type==E_CORE_WARNING) {
800 #if PHP_VERSION_ID >= 80000
801 php_syslog(LOG_ALERT, "PHP %s: %s (%s)", error_type_str, ZSTR_VAL(message), GetCommandLine());
802 #else
803 MessageBox(NULL, buffer, error_type_str, MB_OK);
804 #endif
805 }
806 #endif
807 #if PHP_VERSION_ID >= 80100
808 xdebug_log_stack(error_type_str, ZSTR_VAL(message), ZSTR_VAL(error_filename), error_lineno);
809 #elif PHP_VERSION_ID >= 80000
810 xdebug_log_stack(error_type_str, ZSTR_VAL(message), error_filename, error_lineno);
811 #else
812 xdebug_log_stack(error_type_str, buffer, error_filename, error_lineno);
813 #endif
814 if (XINI_DEV(dump_globals) && !(XINI_DEV(dump_once) && XG_LIB(dumped))) {
815 char *printable_stack = xdebug_get_printable_superglobals(0);
816
817 if (printable_stack) {
818 int pc;
819
820 xdebug_arg *parts = xdebug_arg_ctor();
821
822 xdebug_explode("\n", printable_stack, parts, -1);
823
824 for (pc = 0; pc < parts->c; pc++) {
825 char *tmp_line = xdebug_sprintf("PHP %s", parts->args[pc]);
826 php_log_err(tmp_line);
827 xdfree(tmp_line);
828 }
829
830 xdebug_arg_dtor(parts);
831 xdfree(printable_stack);
832 php_log_err((char*) "PHP ");
833 }
834 }
835 }
836
837 /* Display errors */
838 if ((PG(display_errors) || XINI_DEV(force_display_errors)) && !PG(during_request_startup)) {
839 char *printable_stack;
840
841 #if PHP_VERSION_ID >= 80100
842 printable_stack = xdebug_handle_stack_trace(type, error_type_str, ZSTR_VAL(error_filename), error_lineno, ZSTR_VAL(message));
843 #elif PHP_VERSION_ID >= 80000
844 printable_stack = xdebug_handle_stack_trace(type, error_type_str, error_filename, error_lineno, ZSTR_VAL(message));
845 #else
846 printable_stack = xdebug_handle_stack_trace(type, error_type_str, error_filename, error_lineno, buffer);
847 #endif
848
849 if (XG_LIB(do_collect_errors) && (type != E_ERROR) && (type != E_COMPILE_ERROR) && (type != E_USER_ERROR)) {
850 xdebug_llist_insert_next(XG_DEV(collected_errors), XDEBUG_LLIST_TAIL(XG_DEV(collected_errors)), printable_stack);
851 } else {
852 php_output_error(printable_stack);
853 xdfree(printable_stack);
854 }
855 } else if (XG_LIB(do_collect_errors)) {
856 char *printable_stack;
857 #if PHP_VERSION_ID >= 80100
858 printable_stack = xdebug_get_printable_stack(PG(html_errors), type, ZSTR_VAL(message), ZSTR_VAL(error_filename), error_lineno, 1);
859 #elif PHP_VERSION_ID >= 80000
860 printable_stack = xdebug_get_printable_stack(PG(html_errors), type, ZSTR_VAL(message), error_filename, error_lineno, 1);
861 #else
862 printable_stack = xdebug_get_printable_stack(PG(html_errors), type, buffer, error_filename, error_lineno, 1);
863 #endif
864 xdebug_llist_insert_next(XG_DEV(collected_errors), XDEBUG_LLIST_TAIL(XG_DEV(collected_errors)), printable_stack);
865 }
866 }
867
868 {
869 #if PHP_VERSION_ID >= 80100
870 zend_string *tmp_error_filename = zend_string_copy(error_filename);
871 #else
872 zend_string *tmp_error_filename = zend_string_init(error_filename, strlen(error_filename), 0);
873 #endif
874 #if PHP_VERSION_ID >= 80000
875 xdebug_debugger_error_cb(tmp_error_filename, error_lineno, type, error_type_str, ZSTR_VAL(message));
876 #else
877 xdebug_debugger_error_cb(tmp_error_filename, error_lineno, type, error_type_str, buffer);
878 #endif
879 zend_string_release(tmp_error_filename);
880 }
881
882 xdfree(error_type_str);
883
884 if (type & XINI_DEV(halt_level) & XDEBUG_ALLOWED_HALT_LEVELS) {
885 type = E_USER_ERROR;
886 }
887
888 /* Bail out if we can't recover */
889 switch (type) {
890 case E_CORE_ERROR:
891 if (!php_get_module_initialized()) {
892 /* bad error in module startup - no way we can live with this */
893 exit(-2);
894 }
895 XDEBUG_BREAK_INTENTIONALLY_MISSING
896
897 case E_ERROR:
898 case E_RECOVERABLE_ERROR:
899 case E_PARSE:
900 case E_COMPILE_ERROR:
901 case E_USER_ERROR:
902 EG(exit_status) = 255;
903 if (php_get_module_initialized()) {
904 if (!PG(display_errors) &&
905 !SG(headers_sent) &&
906 SG(sapi_headers).http_response_code == 200
907 ) {
908 sapi_header_line ctr = { 0, 0, 0 };
909
910 ctr.line = (char*) "HTTP/1.0 500 Internal Server Error";
911 ctr.line_len = sizeof("HTTP/1.0 500 Internal Server Error") - 1;
912 sapi_header_op(SAPI_HEADER_REPLACE, &ctr);
913 }
914 /* the parser would return 1 (failure), we can bail out nicely */
915 #if PHP_VERSION_ID >= 80000
916 if (!(orig_type & E_DONT_BAIL)) {
917 #else
918 if (type != E_PARSE) {
919 #endif
920 /* restore memory limit */
921 zend_set_memory_limit(PG(memory_limit));
922 #if PHP_VERSION_ID < 80000
923 efree(buffer);
924 #endif
925 zend_objects_store_mark_destructed(&EG(objects_store));
926 _zend_bailout((char*) __FILE__, __LINE__);
927 return;
928 }
929 }
930 break;
931 }
932
933 #if PHP_VERSION_ID < 70400
934 /* Log if necessary */
935 if (!display) {
936 efree(buffer);
937 return;
938 }
939
940 if (PG(track_errors) && EG(active)) {
941 zval tmp;
942 ZVAL_STRINGL(&tmp, buffer, buffer_len);
943
944 if (EG(current_execute_data)) {
945 if (zend_set_local_var_str("php_errormsg", sizeof("php_errormsg")-1, &tmp, 0) == FAILURE) {
946 zval_ptr_dtor(&tmp);
947 }
948 } else {
949 zend_hash_str_update(&EG(symbol_table), "php_errormsg", sizeof("php_errormsg"), &tmp);
950 }
951 }
952 #endif
953
954 #if PHP_VERSION_ID < 80000
955 efree(buffer);
956 #endif
957 }
958
959 #if PHP_VERSION_ID >= 80000
960 void xdebug_develop_throw_exception_hook(zend_object *exception, zval *file, zval *line, zval *code, char *code_str, zval *message)
961 {
962 zend_class_entry *exception_ce = exception->ce;
963 #else
964 void xdebug_develop_throw_exception_hook(zval *exception, zval *file, zval *line, zval *code, char *code_str, zval *message)
965 {
966 zend_class_entry *exception_ce = Z_OBJCE_P(exception);
967 #endif
968 zval *xdebug_message_trace, *previous_exception;
969 char *exception_trace;
970 xdebug_str tmp_str = XDEBUG_STR_INITIALIZER;
971 zval dummy;
972
973 previous_exception = zend_read_property(exception_ce, exception, "previous", sizeof("previous")-1, 1, &dummy);
974 if (previous_exception && Z_TYPE_P(previous_exception) == IS_OBJECT) {
975 #if PHP_VERSION_ID >= 80000
976 xdebug_message_trace = zend_read_property(exception_ce, Z_OBJ_P(previous_exception), "xdebug_message", sizeof("xdebug_message")-1, 1, &dummy);
977 #else
978 xdebug_message_trace = zend_read_property(exception_ce, previous_exception, "xdebug_message", sizeof("xdebug_message")-1, 1, &dummy);
979 #endif
980 if (xdebug_message_trace && Z_TYPE_P(xdebug_message_trace) != IS_NULL) {
981 xdebug_str_add(&tmp_str, Z_STRVAL_P(xdebug_message_trace), 0);
982 }
983 }
984
985 if (!PG(html_errors)) {
986 xdebug_str_addc(&tmp_str, '\n');
987 }
988 xdebug_append_error_description(&tmp_str, PG(html_errors), STR_NAME_VAL(exception_ce->name), message ? Z_STRVAL_P(message) : "", Z_STRVAL_P(file), Z_LVAL_P(line));
989 xdebug_append_printable_stack(&tmp_str, PG(html_errors));
990 exception_trace = tmp_str.d;
991 zend_update_property_string(exception_ce, exception, "xdebug_message", sizeof("xdebug_message")-1, exception_trace);
992
993 if (XG_BASE(last_exception_trace)) {
994 xdfree(XG_BASE(last_exception_trace));
995 }
996 XG_BASE(last_exception_trace) = exception_trace;
997
998 if (XINI_DEV(show_ex_trace) || (instanceof_function(exception_ce, zend_ce_error) && XINI_DEV(show_error_trace))) {
999 if (PG(log_errors)) {
1000 xdebug_log_stack(STR_NAME_VAL(exception_ce->name), Z_STRVAL_P(message), Z_STRVAL_P(file), Z_LVAL_P(line));
1001 }
1002 if (PG(display_errors)) {
1003 xdebug_str displ_tmp_str = XDEBUG_STR_INITIALIZER;
1004 xdebug_append_error_head(&displ_tmp_str, PG(html_errors), "exception");
1005 xdebug_str_add(&displ_tmp_str, exception_trace, 0);
1006 xdebug_append_error_footer(&displ_tmp_str, PG(html_errors));
1007
1008 php_printf("%s", displ_tmp_str.d);
1009 xdebug_str_dtor(displ_tmp_str);
1010 }
1011 }
1012 }
1013
1014 /* {{{ proto int xdebug_get_stack_depth()
1015 Returns the stack depth */
1016 PHP_FUNCTION(xdebug_get_stack_depth)
1017 {
1018 /* We substract one so that the function call to xdebug_get_stack_depth()
1019 * is not part of the returned depth. */
1020 RETURN_LONG(XDEBUG_VECTOR_COUNT(XG_BASE(stack)) - 1);
1021 }
1022
1023 /* {{{ proto array xdebug_get_function_stack()
1024 Returns an array representing the current stack */
1025 PHP_FUNCTION(xdebug_get_function_stack)
1026 {
1027 function_stack_entry *fse;
1028 unsigned int i;
1029 unsigned int j;
1030 zval *frame;
1031 zval *params;
1032 int variadic_opened = 0;
1033
1034 if (!XDEBUG_MODE_IS(XDEBUG_MODE_DEVELOP)) {
1035 php_error(E_WARNING, "Function must be enabled in php.ini by setting 'xdebug.mode' to 'develop'");
1036 array_init(return_value);
1037 return;
1038 }
1039
1040 array_init(return_value);
1041
1042 fse = XDEBUG_VECTOR_HEAD(XG_BASE(stack));
1043
1044 for (i = 0; i < XDEBUG_VECTOR_COUNT(XG_BASE(stack)) - 1; i++, fse++) {
1045 int sent_variables = fse->varc;
1046
1047 if (fse->function.function) {
1048 if (strcmp(fse->function.function, "xdebug_get_function_stack") == 0) {
1049 return;
1050 }
1051 }
1052
1053 if (sent_variables > 0 && fse->var[sent_variables-1].is_variadic && Z_ISUNDEF(fse->var[sent_variables-1].data)) {
1054 sent_variables--;
1055 }
1056
1057 /* Initialize frame array */
1058 XDEBUG_MAKE_STD_ZVAL(frame);
1059 array_init(frame);
1060
1061 /* Add data */
1062 if (fse->function.function) {
1063 add_assoc_string_ex(frame, "function", HASH_KEY_SIZEOF("function"), fse->function.function);
1064 }
1065 if (fse->function.object_class) {
1066 add_assoc_string_ex(frame, "type", HASH_KEY_SIZEOF("type"), (char*) (fse->function.type == XFUNC_STATIC_MEMBER ? "static" : "dynamic"));
1067 add_assoc_str_ex(frame, "class", HASH_KEY_SIZEOF("class"), zend_string_copy(fse->function.object_class));
1068 }
1069 add_assoc_str_ex(frame, "file", HASH_KEY_SIZEOF("file"), zend_string_copy(fse->filename));
1070 add_assoc_long_ex(frame, "line", HASH_KEY_SIZEOF("line"), fse->lineno);
1071
1072 /* Add parameters */
1073 XDEBUG_MAKE_STD_ZVAL(params);
1074 array_init(params);
1075 add_assoc_zval_ex(frame, "params", HASH_KEY_SIZEOF("params"), params);
1076
1077 for (j = 0; j < sent_variables; j++) {
1078 xdebug_str *argument = NULL;
1079
1080 if (fse->var[j].is_variadic) {
1081 zval *vparams;
1082
1083 XDEBUG_MAKE_STD_ZVAL(vparams);
1084 array_init(vparams);
1085
1086 if (fse->var[j].name) {
1087 add_assoc_zval_ex(params, ZSTR_VAL(fse->var[j].name), ZSTR_LEN(fse->var[j].name), vparams);
1088 } else {
1089 add_index_zval(params, j, vparams);
1090 }
1091 efree(params);
1092 params = vparams;
1093 variadic_opened = 1;
1094 continue;
1095 }
1096 if (!Z_ISUNDEF(fse->var[j].data)) {
1097 argument = xdebug_get_zval_value_line(&fse->var[j].data, 0, NULL);
1098 } else {
1099 argument = xdebug_str_create_from_char((char*) "???");
1100 }
1101 if (fse->var[j].name && !variadic_opened && argument) {
1102 add_assoc_stringl_ex(params, ZSTR_VAL(fse->var[j].name), ZSTR_LEN(fse->var[j].name), argument->d, argument->l);
1103 } else {
1104 add_index_stringl(params, j - variadic_opened, argument->d, argument->l);
1105 }
1106 if (argument) {
1107 xdebug_str_free(argument);
1108 argument = NULL;
1109 }
1110 }
1111
1112 if (fse->include_filename) {
1113 add_assoc_str_ex(frame, "include_filename", HASH_KEY_SIZEOF("include_filename"), zend_string_copy(fse->include_filename));
1114 }
1115
1116 add_next_index_zval(return_value, frame);
1117 efree(params);
1118 efree(frame);
1119 }
1120 }
1121 /* }}} */
1122