Skip to content

Commit a69021d

Browse files
committed
1 parent 4fa3687 commit a69021d

9 files changed

+270
-152
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: 10 additions & 148 deletions
Original file line numberDiff line numberDiff line change
@@ -452,145 +452,14 @@ ZEND_METHOD(ErrorException, getSeverity)
452452
}
453453
/* }}} */
454454

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

595464
ZEND_PARSE_PARAMETERS_NONE();
596465

@@ -604,21 +473,12 @@ ZEND_METHOD(Exception, getTraceAsString)
604473

605474
/* Type should be guaranteed by property type. */
606475
ZEND_ASSERT(Z_TYPE_P(trace) == IS_ARRAY);
607-
ZEND_HASH_FOREACH_NUM_KEY_VAL(Z_ARRVAL_P(trace), index, frame) {
608-
if (Z_TYPE_P(frame) != IS_ARRAY) {
609-
zend_error(E_WARNING, "Expected array for frame " ZEND_ULONG_FMT, index);
610-
continue;
611-
}
612-
613-
_build_trace_string(&str, Z_ARRVAL_P(frame), num++);
614-
} ZEND_HASH_FOREACH_END();
615-
616-
smart_str_appendc(&str, '#');
617-
smart_str_append_long(&str, num);
618-
smart_str_appends(&str, " {main}");
619-
smart_str_0(&str);
476+
str = zend_build_backtrace_string(trace);
477+
if (!str) {
478+
RETURN_THROWS();
479+
}
620480

621-
RETURN_NEW_STR(str.s);
481+
RETURN_NEW_STR(str);
622482
}
623483
/* }}} */
624484

@@ -902,6 +762,8 @@ ZEND_API ZEND_COLD void zend_exception_error(zend_object *ex, int severity) /* {
902762
zval exception, rv;
903763
zend_class_entry *ce_exception;
904764

765+
severity |= E_UNCAUGHT_EXCEPTION;
766+
905767
ZVAL_OBJ(&exception, ex);
906768
ce_exception = ex->ce;
907769
EG(exception) = NULL;
@@ -941,7 +803,7 @@ ZEND_API ZEND_COLD void zend_exception_error(zend_object *ex, int severity) /* {
941803
line = zval_get_long(GET_PROPERTY_SILENT(&zv, ZEND_STR_LINE));
942804
}
943805

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

0 commit comments

Comments
 (0)