Skip to content

Commit 2a771bf

Browse files
committed
1 parent baa2858 commit 2a771bf

9 files changed

+269
-155
lines changed

Zend/zend_builtin_functions.c

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include "zend_extensions.h"
2828
#include "zend_closures.h"
2929
#include "zend_generators.h"
30+
#include "zend_smart_str.h"
3031
#include "zend_builtin_functions_arginfo.h"
3132

3233
/* }}} */
@@ -2150,6 +2151,164 @@ ZEND_API void zend_fetch_debug_backtrace(zval *return_value, int skip_last, int
21502151
}
21512152
/* }}} */
21522153

2154+
/* {{{ */
2155+
#define TRACE_APPEND_KEY(key) do { \
2156+
tmp = zend_hash_find(ht, key); \
2157+
if (tmp) { \
2158+
if (Z_TYPE_P(tmp) != IS_STRING) { \
2159+
zend_error(E_WARNING, "Value for %s is not a string", \
2160+
ZSTR_VAL(key)); \
2161+
smart_str_appends(str, "[unknown]"); \
2162+
} else { \
2163+
smart_str_appends(str, Z_STRVAL_P(tmp)); \
2164+
} \
2165+
} \
2166+
} while (0)
2167+
2168+
static void _build_trace_args(zval *arg, smart_str *str) /* {{{ */
2169+
{
2170+
/* the trivial way would be to do
2171+
* convert_to_string_ex(arg);
2172+
* append it and kill the now tmp arg.
2173+
* but that could cause some E_NOTICE and also damn long lines.
2174+
*/
2175+
2176+
ZVAL_DEREF(arg);
2177+
switch (Z_TYPE_P(arg)) {
2178+
case IS_NULL:
2179+
smart_str_appends(str, "NULL, ");
2180+
break;
2181+
case IS_STRING:
2182+
smart_str_appendc(str, '\'');
2183+
smart_str_append_escaped(str, Z_STRVAL_P(arg), MIN(Z_STRLEN_P(arg), 15));
2184+
if (Z_STRLEN_P(arg) > 15) {
2185+
smart_str_appends(str, "...', ");
2186+
} else {
2187+
smart_str_appends(str, "', ");
2188+
}
2189+
break;
2190+
case IS_FALSE:
2191+
smart_str_appends(str, "false, ");
2192+
break;
2193+
case IS_TRUE:
2194+
smart_str_appends(str, "true, ");
2195+
break;
2196+
case IS_RESOURCE:
2197+
smart_str_appends(str, "Resource id #");
2198+
smart_str_append_long(str, Z_RES_HANDLE_P(arg));
2199+
smart_str_appends(str, ", ");
2200+
break;
2201+
case IS_LONG:
2202+
smart_str_append_long(str, Z_LVAL_P(arg));
2203+
smart_str_appends(str, ", ");
2204+
break;
2205+
case IS_DOUBLE: {
2206+
smart_str_append_printf(str, "%.*G", (int) EG(precision), Z_DVAL_P(arg));
2207+
smart_str_appends(str, ", ");
2208+
break;
2209+
}
2210+
case IS_ARRAY:
2211+
smart_str_appends(str, "Array, ");
2212+
break;
2213+
case IS_OBJECT: {
2214+
zend_string *class_name = Z_OBJ_HANDLER_P(arg, get_class_name)(Z_OBJ_P(arg));
2215+
smart_str_appends(str, "Object(");
2216+
smart_str_appends(str, ZSTR_VAL(class_name));
2217+
smart_str_appends(str, "), ");
2218+
zend_string_release_ex(class_name, 0);
2219+
break;
2220+
}
2221+
}
2222+
}
2223+
2224+
static void _build_trace_string(smart_str *str, HashTable *ht, uint32_t num)
2225+
{
2226+
zval *file, *tmp;
2227+
2228+
smart_str_appendc(str, '#');
2229+
smart_str_append_long(str, num);
2230+
smart_str_appendc(str, ' ');
2231+
2232+
file = zend_hash_find_ex(ht, ZSTR_KNOWN(ZEND_STR_FILE), 1);
2233+
if (file) {
2234+
if (Z_TYPE_P(file) != IS_STRING) {
2235+
zend_error(E_WARNING, "Function name is not a string");
2236+
smart_str_appends(str, "[unknown function]");
2237+
} else{
2238+
zend_long line;
2239+
tmp = zend_hash_find_ex(ht, ZSTR_KNOWN(ZEND_STR_LINE), 1);
2240+
if (tmp) {
2241+
if (Z_TYPE_P(tmp) == IS_LONG) {
2242+
line = Z_LVAL_P(tmp);
2243+
} else {
2244+
zend_error(E_WARNING, "Line is not an int");
2245+
line = 0;
2246+
}
2247+
} else {
2248+
line = 0;
2249+
}
2250+
smart_str_append(str, Z_STR_P(file));
2251+
smart_str_appendc(str, '(');
2252+
smart_str_append_long(str, line);
2253+
smart_str_appends(str, "): ");
2254+
}
2255+
} else {
2256+
smart_str_appends(str, "[internal function]: ");
2257+
}
2258+
TRACE_APPEND_KEY(ZSTR_KNOWN(ZEND_STR_CLASS));
2259+
TRACE_APPEND_KEY(ZSTR_KNOWN(ZEND_STR_TYPE));
2260+
TRACE_APPEND_KEY(ZSTR_KNOWN(ZEND_STR_FUNCTION));
2261+
smart_str_appendc(str, '(');
2262+
tmp = zend_hash_find_ex(ht, ZSTR_KNOWN(ZEND_STR_ARGS), 1);
2263+
if (tmp) {
2264+
if (Z_TYPE_P(tmp) == IS_ARRAY) {
2265+
size_t last_len = ZSTR_LEN(str->s);
2266+
zval *arg;
2267+
2268+
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(tmp), arg) {
2269+
_build_trace_args(arg, str);
2270+
} ZEND_HASH_FOREACH_END();
2271+
2272+
if (last_len != ZSTR_LEN(str->s)) {
2273+
ZSTR_LEN(str->s) -= 2; /* remove last ', ' */
2274+
}
2275+
} else {
2276+
zend_error(E_WARNING, "args element is not an array");
2277+
}
2278+
}
2279+
smart_str_appends(str, ")\n");
2280+
}
2281+
/* }}} */
2282+
2283+
/* {{{ */
2284+
ZEND_API zend_string *zend_build_backtrace_string(zval *trace)
2285+
{
2286+
zval *frame;
2287+
zend_ulong index;
2288+
smart_str str = {0};
2289+
uint32_t num = 0;
2290+
2291+
if (Z_TYPE_P(trace) != IS_ARRAY) {
2292+
zend_type_error("Trace is not an array");
2293+
return NULL;
2294+
}
2295+
ZEND_HASH_FOREACH_NUM_KEY_VAL(Z_ARRVAL_P(trace), index, frame) {
2296+
if (Z_TYPE_P(frame) != IS_ARRAY) {
2297+
zend_error(E_WARNING, "Expected array for frame " ZEND_ULONG_FMT, index);
2298+
continue;
2299+
}
2300+
2301+
_build_trace_string(&str, Z_ARRVAL_P(frame), num++);
2302+
} ZEND_HASH_FOREACH_END();
2303+
2304+
smart_str_appendc(&str, '#');
2305+
smart_str_append_long(&str, num);
2306+
smart_str_appends(&str, " {main}");
2307+
smart_str_0(&str);
2308+
2309+
return str.s;
2310+
}
2311+
21532312
/* {{{ proto array debug_backtrace([int options[, int limit]])
21542313
Return backtrace as array */
21552314
ZEND_FUNCTION(debug_backtrace)

Zend/zend_builtin_functions.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ int zend_startup_builtin_functions(void);
2424

2525
BEGIN_EXTERN_C()
2626
ZEND_API void zend_fetch_debug_backtrace(zval *return_value, int skip_last, int options, int limit);
27+
ZEND_API zend_string *zend_build_backtrace_string(zval *trace);
2728
END_EXTERN_C()
2829

2930
#endif /* ZEND_BUILTIN_FUNCTIONS_H */

Zend/zend_errors.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@
3939
/* Indicates that this usually fatal error should not result in a bailout */
4040
#define E_DONT_BAIL (1<<15L)
4141

42+
/* Indicates that this error was caused by an uncaught exception */
43+
#define E_UNCAUGHT_EXCEPTION (1<<16L)
44+
4245
#define E_ALL (E_ERROR | E_WARNING | E_PARSE | E_NOTICE | E_CORE_ERROR | E_CORE_WARNING | E_COMPILE_ERROR | E_COMPILE_WARNING | E_USER_ERROR | E_USER_WARNING | E_USER_NOTICE | E_RECOVERABLE_ERROR | E_DEPRECATED | E_USER_DEPRECATED | E_STRICT)
4346
#define E_CORE (E_CORE_ERROR | E_CORE_WARNING)
4447

Zend/zend_exceptions.c

Lines changed: 9 additions & 151 deletions
Original file line numberDiff line numberDiff line change
@@ -461,171 +461,27 @@ ZEND_METHOD(ErrorException, getSeverity)
461461
}
462462
/* }}} */
463463

464-
#define TRACE_APPEND_KEY(key) do { \
465-
tmp = zend_hash_find(ht, key); \
466-
if (tmp) { \
467-
if (Z_TYPE_P(tmp) != IS_STRING) { \
468-
zend_error(E_WARNING, "Value for %s is not a string", \
469-
ZSTR_VAL(key)); \
470-
smart_str_appends(str, "[unknown]"); \
471-
} else { \
472-
smart_str_appends(str, Z_STRVAL_P(tmp)); \
473-
} \
474-
} \
475-
} while (0)
476-
477-
static void _build_trace_args(zval *arg, smart_str *str) /* {{{ */
478-
{
479-
/* the trivial way would be to do
480-
* convert_to_string_ex(arg);
481-
* append it and kill the now tmp arg.
482-
* but that could cause some E_NOTICE and also damn long lines.
483-
*/
484-
485-
ZVAL_DEREF(arg);
486-
switch (Z_TYPE_P(arg)) {
487-
case IS_NULL:
488-
smart_str_appends(str, "NULL, ");
489-
break;
490-
case IS_STRING:
491-
smart_str_appendc(str, '\'');
492-
smart_str_append_escaped(str, Z_STRVAL_P(arg), MIN(Z_STRLEN_P(arg), 15));
493-
if (Z_STRLEN_P(arg) > 15) {
494-
smart_str_appends(str, "...', ");
495-
} else {
496-
smart_str_appends(str, "', ");
497-
}
498-
break;
499-
case IS_FALSE:
500-
smart_str_appends(str, "false, ");
501-
break;
502-
case IS_TRUE:
503-
smart_str_appends(str, "true, ");
504-
break;
505-
case IS_RESOURCE:
506-
smart_str_appends(str, "Resource id #");
507-
smart_str_append_long(str, Z_RES_HANDLE_P(arg));
508-
smart_str_appends(str, ", ");
509-
break;
510-
case IS_LONG:
511-
smart_str_append_long(str, Z_LVAL_P(arg));
512-
smart_str_appends(str, ", ");
513-
break;
514-
case IS_DOUBLE: {
515-
smart_str_append_printf(str, "%.*G", (int) EG(precision), Z_DVAL_P(arg));
516-
smart_str_appends(str, ", ");
517-
break;
518-
}
519-
case IS_ARRAY:
520-
smart_str_appends(str, "Array, ");
521-
break;
522-
case IS_OBJECT: {
523-
zend_string *class_name = Z_OBJ_HANDLER_P(arg, get_class_name)(Z_OBJ_P(arg));
524-
smart_str_appends(str, "Object(");
525-
smart_str_appends(str, ZSTR_VAL(class_name));
526-
smart_str_appends(str, "), ");
527-
zend_string_release_ex(class_name, 0);
528-
break;
529-
}
530-
}
531-
}
532-
/* }}} */
533-
534-
static void _build_trace_string(smart_str *str, HashTable *ht, uint32_t num) /* {{{ */
535-
{
536-
zval *file, *tmp;
537-
538-
smart_str_appendc(str, '#');
539-
smart_str_append_long(str, num);
540-
smart_str_appendc(str, ' ');
541-
542-
file = zend_hash_find_ex(ht, ZSTR_KNOWN(ZEND_STR_FILE), 1);
543-
if (file) {
544-
if (Z_TYPE_P(file) != IS_STRING) {
545-
zend_error(E_WARNING, "Function name is not a string");
546-
smart_str_appends(str, "[unknown function]");
547-
} else{
548-
zend_long line;
549-
tmp = zend_hash_find_ex(ht, ZSTR_KNOWN(ZEND_STR_LINE), 1);
550-
if (tmp) {
551-
if (Z_TYPE_P(tmp) == IS_LONG) {
552-
line = Z_LVAL_P(tmp);
553-
} else {
554-
zend_error(E_WARNING, "Line is not an int");
555-
line = 0;
556-
}
557-
} else {
558-
line = 0;
559-
}
560-
smart_str_append(str, Z_STR_P(file));
561-
smart_str_appendc(str, '(');
562-
smart_str_append_long(str, line);
563-
smart_str_appends(str, "): ");
564-
}
565-
} else {
566-
smart_str_appends(str, "[internal function]: ");
567-
}
568-
TRACE_APPEND_KEY(ZSTR_KNOWN(ZEND_STR_CLASS));
569-
TRACE_APPEND_KEY(ZSTR_KNOWN(ZEND_STR_TYPE));
570-
TRACE_APPEND_KEY(ZSTR_KNOWN(ZEND_STR_FUNCTION));
571-
smart_str_appendc(str, '(');
572-
tmp = zend_hash_find_ex(ht, ZSTR_KNOWN(ZEND_STR_ARGS), 1);
573-
if (tmp) {
574-
if (Z_TYPE_P(tmp) == IS_ARRAY) {
575-
size_t last_len = ZSTR_LEN(str->s);
576-
zval *arg;
577-
578-
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(tmp), arg) {
579-
_build_trace_args(arg, str);
580-
} ZEND_HASH_FOREACH_END();
581-
582-
if (last_len != ZSTR_LEN(str->s)) {
583-
ZSTR_LEN(str->s) -= 2; /* remove last ', ' */
584-
}
585-
} else {
586-
zend_error(E_WARNING, "args element is not an array");
587-
}
588-
}
589-
smart_str_appends(str, ")\n");
590-
}
591-
/* }}} */
592-
593464
/* {{{ proto string Exception|Error::getTraceAsString()
594465
Obtain the backtrace for the exception as a string (instead of an array) */
595466
ZEND_METHOD(Exception, getTraceAsString)
596467
{
597-
zval *trace, *frame, rv;
598-
zend_ulong index;
468+
zval *trace, rv;
599469
zval *object;
600470
zend_class_entry *base_ce;
601-
smart_str str = {0};
602-
uint32_t num = 0;
471+
zend_string *str;
603472

604473
ZEND_PARSE_PARAMETERS_NONE();
605474

606475
object = ZEND_THIS;
607476
base_ce = i_get_exception_base(object);
608477

609478
trace = zend_read_property_ex(base_ce, object, ZSTR_KNOWN(ZEND_STR_TRACE), 1, &rv);
610-
if (Z_TYPE_P(trace) != IS_ARRAY) {
611-
zend_type_error("Trace is not an array");
612-
return;
479+
str = zend_build_backtrace_string(trace);
480+
if (!str) {
481+
RETURN_THROWS();
613482
}
614-
ZEND_HASH_FOREACH_NUM_KEY_VAL(Z_ARRVAL_P(trace), index, frame) {
615-
if (Z_TYPE_P(frame) != IS_ARRAY) {
616-
zend_error(E_WARNING, "Expected array for frame " ZEND_ULONG_FMT, index);
617-
continue;
618-
}
619-
620-
_build_trace_string(&str, Z_ARRVAL_P(frame), num++);
621-
} ZEND_HASH_FOREACH_END();
622483

623-
smart_str_appendc(&str, '#');
624-
smart_str_append_long(&str, num);
625-
smart_str_appends(&str, " {main}");
626-
smart_str_0(&str);
627-
628-
RETURN_NEW_STR(str.s);
484+
RETURN_NEW_STR(str);
629485
}
630486
/* }}} */
631487

@@ -902,6 +758,8 @@ ZEND_API ZEND_COLD void zend_exception_error(zend_object *ex, int severity) /* {
902758
zval exception, rv;
903759
zend_class_entry *ce_exception;
904760

761+
severity |= E_UNCAUGHT_EXCEPTION;
762+
905763
ZVAL_OBJ(&exception, ex);
906764
ce_exception = ex->ce;
907765
EG(exception) = NULL;
@@ -941,7 +799,7 @@ ZEND_API ZEND_COLD void zend_exception_error(zend_object *ex, int severity) /* {
941799
line = zval_get_long(GET_PROPERTY_SILENT(&zv, ZEND_STR_LINE));
942800
}
943801

944-
zend_error_va(E_WARNING, (file && ZSTR_LEN(file) > 0) ? ZSTR_VAL(file) : NULL, line,
802+
zend_error_va(E_WARNING | E_UNCAUGHT_EXCEPTION, (file && ZSTR_LEN(file) > 0) ? ZSTR_VAL(file) : NULL, line,
945803
"Uncaught %s in exception handling during call to %s::__tostring()",
946804
ZSTR_VAL(Z_OBJCE(zv)->name), ZSTR_VAL(ce_exception->name));
947805

0 commit comments

Comments
 (0)