]> granicus.if.org Git - php/commitdiff
- Add support for linking exceptions (implements TODO)
authorMarcus Boerger <helly@php.net>
Sat, 12 Jul 2008 14:56:51 +0000 (14:56 +0000)
committerMarcus Boerger <helly@php.net>
Sat, 12 Jul 2008 14:56:51 +0000 (14:56 +0000)
# Right now backtrace is broken, for internal functions I only see the
# first char and debugging reveals the length of the function name is seen
# as one when building the trace. There is something out of the scope of
# this patch broken.

Zend/zend_exceptions.c

index fa633cea698b4138bf62fdaa3736e790369ff4ea..8ec5ecdb5e03c989143995dc8b7c15ec543b8f55 100644 (file)
@@ -33,13 +33,11 @@ zend_class_entry *error_exception_ce;
 static zend_object_handlers default_exception_handlers;
 ZEND_API void (*zend_throw_exception_hook)(zval *ex TSRMLS_DC);
 
-
 void zend_throw_exception_internal(zval *exception TSRMLS_DC) /* {{{ */
 {
        if (exception != NULL) {
                if (EG(exception)) {
-                       /* FIXME:  bail out? */
-                       return;
+                       zend_update_property(default_exception_ce, exception, "previous", sizeof("previous")-1, EG(exception) TSRMLS_CC);
                }
                EG(exception) = exception;
        }
@@ -122,17 +120,17 @@ ZEND_METHOD(exception, __clone)
 }
 /* }}} */
 
-/* {{{ proto Exception::__construct(string message, int code) U
+/* {{{ proto Exception::__construct(string message, int code [, Exception previous]) U
    Exception constructor */
 ZEND_METHOD(exception, __construct)
 {
        void  *message = NULL;
        long   code = 0;
-       zval  *object;
+       zval  *object, *previous = NULL;
        int    argc = ZEND_NUM_ARGS(), message_len;
        zend_uchar message_type;
 
-       if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc TSRMLS_CC, "|tl", &message, &message_len, &message_type, &code) == FAILURE) {
+       if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc TSRMLS_CC, "|tlO", &message, &message_len, &message_type, &code, &previous, default_exception_ce) == FAILURE) {
                zend_error(E_ERROR, "Wrong parameters for Exception([string $exception [, long $code ]])");
        }
 
@@ -149,20 +147,24 @@ ZEND_METHOD(exception, __construct)
        if (code) {
                zend_update_property_long(default_exception_ce, object, "code", sizeof("code")-1, code TSRMLS_CC);
        }
+
+       if (previous) {
+               zend_update_property(default_exception_ce, object, "previous", sizeof("previous")-1, previous TSRMLS_CC);
+       }
 }
 /* }}} */
 
-/* {{{ proto ErrorException::__construct(string message, int code, int severity [, string filename [, int lineno]]) U
+/* {{{ proto ErrorException::__construct(string message, int code, int severity [, string filename [, int lineno [, Exception previous]]]) U
    ErrorException constructor */
 ZEND_METHOD(error_exception, __construct)
 {
        void  *message = NULL, *filename = NULL;
        long   code = 0, severity = E_ERROR, lineno;
-       zval  *object;
+       zval  *object, *previous = NULL;
        int    argc = ZEND_NUM_ARGS(), message_len, filename_len;
        zend_uchar message_type, file_type;
 
-       if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc TSRMLS_CC, "|tlltl", &message, &message_len, &message_type, &code, &severity, &filename, &filename_len, &file_type, &lineno) == FAILURE) {
+       if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc TSRMLS_CC, "|tlltlO", &message, &message_len, &message_type, &code, &severity, &filename, &filename_len, &file_type, &lineno, &previous, default_exception_ce) == FAILURE) {
                zend_error(E_ERROR, "Wrong parameters for ErrorException([string $exception [, long $code, [ long $severity, [ string $filename, [ long $lineno ]]]]])");
        }
 
@@ -180,6 +182,10 @@ ZEND_METHOD(error_exception, __construct)
                zend_update_property_long(default_exception_ce, object, "code", sizeof("code")-1, code TSRMLS_CC);
        }
 
+       if (previous) {
+               zend_update_property(default_exception_ce, object, "previous", sizeof("previous")-1, previous TSRMLS_CC);
+       }
+
        zend_update_property_long(default_exception_ce, object, "severity", sizeof("severity")-1, severity TSRMLS_CC);
 
        if (argc >= 4) {
@@ -302,8 +308,8 @@ ZEND_METHOD(error_exception, getSeverity)
                zval copy; \
                int use_copy; \
                zend_make_printable_zval((zv), &copy, &use_copy); \
-               TRACE_APPEND_STRL(Z_STRVAL(copy), Z_STRLEN(copy)); \
-               zval_dtor(&copy); \
+               TRACE_APPEND_STRL(Z_STRVAL_P((use_copy ? &copy : (zv))), Z_STRLEN_P((use_copy ? &copy : (zv)))); \
+               if (use_copy) zval_dtor(&copy); \
        } else { \
                TRACE_APPEND_STRL(Z_STRVAL_P((zv)), Z_STRLEN_P((zv))); \
        }
@@ -311,17 +317,9 @@ ZEND_METHOD(error_exception, getSeverity)
 #define TRACE_APPEND_STR(val)                                            \
        TRACE_APPEND_STRL(val, sizeof(val)-1)
 
-#define TRACE_APPEND_KEY(key)                                            \
+#define TRACE_APPEND_KEY(key,dump)                                            \
        if (zend_ascii_hash_find(ht, key, sizeof(key), (void**)&tmp) == SUCCESS) { \
-               if (Z_TYPE_PP(tmp) == IS_UNICODE) { \
-                       zval copy; \
-                       int use_copy; \
-                       zend_make_printable_zval(*tmp, &copy, &use_copy); \
-           TRACE_APPEND_STRL(Z_STRVAL(copy), Z_STRLEN(copy)); \
-           zval_dtor(&copy); \
-               } else { \
-           TRACE_APPEND_STRL(Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp));           \
-         } \
+           TRACE_APPEND_ZVAL(*tmp);           \
        }
 /* }}} */
 
@@ -495,9 +493,9 @@ static int _build_trace_string(zval **frame, int num_args, va_list args, zend_ha
        } else {
                TRACE_APPEND_STR("[internal function]: ");
        }
-       TRACE_APPEND_KEY("class");
-       TRACE_APPEND_KEY("type");
-       TRACE_APPEND_KEY("function");
+       TRACE_APPEND_KEY("class",0);
+       TRACE_APPEND_KEY("type",0);
+       TRACE_APPEND_KEY("function",1);
        TRACE_APPEND_CHR('(');
        if (zend_ascii_hash_find(ht, "args", sizeof("args"), (void**)&tmp) == SUCCESS) {
                int last_len = *len;
@@ -532,6 +530,15 @@ ZEND_METHOD(exception, getTraceAsString)
 }
 /* }}} */
 
+/* {{{ proto string Exception::getPrevious()
+   Return previous Exception or NULL. */
+ZEND_METHOD(exception, getPrevious)
+{
+       zval *previous;
+       previous = zend_read_property(default_exception_ce, getThis(), "previous", sizeof("previous")-1, 1 TSRMLS_CC);
+       RETURN_ZVAL(previous, 1, 0);
+}
+
 int zend_spprintf(char **message, int max_len, char *format, ...) /* {{{ */
 {
        va_list arg;
@@ -548,46 +555,58 @@ int zend_spprintf(char **message, int max_len, char *format, ...) /* {{{ */
    Obtain the string representation of the Exception object */
 ZEND_METHOD(exception, __toString)
 {
-       zval message, file, line, *trace;
-       char *str;
-       int len;
+       zval message, file, line, *trace, *exception;
+       char *str = estrndup("", 0), *prev_str;
+       int len = 0;
        zend_fcall_info fci;
        zval fname;
 
-       _default_exception_get_entry(getThis(), "message", sizeof("message")-1, &message TSRMLS_CC);
-       _default_exception_get_entry(getThis(), "file", sizeof("file")-1, &file TSRMLS_CC);
-       _default_exception_get_entry(getThis(), "line", sizeof("line")-1, &line TSRMLS_CC);
+       exception = getThis();
+       ZVAL_ASCII_STRINGL(&fname, "gettraceasstring", sizeof("gettraceasstring")-1, 1);
 
-       convert_to_long(&line);
+       while (exception && Z_TYPE_P(exception) == IS_OBJECT) {
+               prev_str = str;
+               _default_exception_get_entry(exception, "message", sizeof("message")-1, &message TSRMLS_CC);
+               _default_exception_get_entry(exception, "file", sizeof("file")-1, &file TSRMLS_CC);
+               _default_exception_get_entry(exception, "line", sizeof("line")-1, &line TSRMLS_CC);
 
-       ZVAL_ASCII_STRINGL(&fname, "gettraceasstring", sizeof("gettraceasstring")-1, 1);
+               convert_to_long(&line);
 
-       fci.size = sizeof(fci);
-       fci.function_table = &Z_OBJCE_P(getThis())->function_table;
-       fci.function_name = &fname;
-       fci.symbol_table = NULL;
-       fci.object_pp = &getThis();
-       fci.retval_ptr_ptr = &trace;
-       fci.param_count = 0;
-       fci.params = NULL;
-       fci.no_separation = 1;
-
-       zend_call_function(&fci, NULL TSRMLS_CC);
-       zval_dtor(&fname);
+               fci.size = sizeof(fci);
+               fci.function_table = &Z_OBJCE_P(getThis())->function_table;
+               fci.function_name = &fname;
+               fci.symbol_table = NULL;
+               fci.object_pp = &exception;
+               fci.retval_ptr_ptr = &trace;
+               fci.param_count = 0;
+               fci.params = NULL;
+               fci.no_separation = 1;
 
-       if (Z_TYPE_P(trace) != IS_STRING && Z_TYPE_P(trace) != IS_UNICODE) {
-               trace = NULL;
-       }
+               zend_call_function(&fci, NULL TSRMLS_CC);
 
-       if (Z_UNILEN(message) > 0) {
-               len = zend_spprintf(&str, 0, "exception '%v' with message '%R' in %R:%ld\nStack trace:\n%s",
-                                                       Z_OBJCE_P(getThis())->name, Z_TYPE(message), Z_UNIVAL(message), Z_TYPE(file), Z_UNIVAL(file), Z_LVAL(line),
-                                                       (trace && Z_STRLEN_P(trace)) ? Z_STRVAL_P(trace) : "#0 {main}\n");
-       } else {
-               len = zend_spprintf(&str, 0, "exception '%v' in %R:%ld\nStack trace:\n%s",
-                                                       Z_OBJCE_P(getThis())->name, Z_TYPE(file), Z_UNIVAL(file), Z_LVAL(line),
-                                                       (trace && Z_STRLEN_P(trace)) ? Z_STRVAL_P(trace) : "#0 {main}\n");
+               if (Z_TYPE_P(trace) != IS_STRING && Z_TYPE_P(trace) != IS_UNICODE) {
+                       trace = NULL;
+               }
+
+               if (Z_UNILEN(message) > 0) {
+                       len = zend_spprintf(&str, 0, "exception '%v' with message '%R' in %R:%ld\nStack trace:\n%s%s%s",
+                                                               Z_OBJCE_P(exception)->name, Z_TYPE(message), Z_UNIVAL(message), Z_TYPE(file), Z_UNIVAL(file), Z_LVAL(line),
+                                                               (trace && Z_STRLEN_P(trace)) ? Z_STRVAL_P(trace) : "#0 {main}\n",
+                                                               len ? "\n\nNext " : "", prev_str);
+               } else {
+                       len = zend_spprintf(&str, 0, "exception '%v' in %R:%ld\nStack trace:\n%s%s%s",
+                                                               Z_OBJCE_P(exception)->name, Z_TYPE(file), Z_UNIVAL(file), Z_LVAL(line),
+                                                               (trace && Z_STRLEN_P(trace)) ? Z_STRVAL_P(trace) : "#0 {main}\n",
+                                                               len ? "\n\nNext " : "", prev_str);
+               }
+               efree(prev_str);
+               zval_dtor(&message);
+               zval_dtor(&file);
+               zval_dtor(&line);
+
+               exception = zend_read_property(default_exception_ce, exception, "previous", sizeof("previous")-1, 0 TSRMLS_CC);
        }
+       zval_dtor(&fname);
 
        /* We store the result in the private property string so we can access
         * the result in uncaught exception handlers without memleaks. */
@@ -597,10 +616,6 @@ ZEND_METHOD(exception, __toString)
                zval_ptr_dtor(&trace);
        }
 
-       zval_dtor(&message);
-       zval_dtor(&file);
-       zval_dtor(&line);
-
        RETURN_STRINGL(str, len, 0);
 }
 /* }}} */
@@ -619,6 +634,7 @@ static
 ZEND_BEGIN_ARG_INFO_EX(arginfo_exception___construct, 0, 0, 0)
        ZEND_ARG_INFO(0, message)
        ZEND_ARG_INFO(0, code)
+       ZEND_ARG_INFO(0, previous)
 ZEND_END_ARG_INFO()
 
 static const zend_function_entry default_exception_functions[] = {
@@ -629,6 +645,7 @@ static const zend_function_entry default_exception_functions[] = {
        ZEND_ME(exception, getFile, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
        ZEND_ME(exception, getLine, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
        ZEND_ME(exception, getTrace, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
+       ZEND_ME(exception, getPrevious, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
        ZEND_ME(exception, getTraceAsString, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
        ZEND_ME(exception, __toString, NULL, 0)
        {NULL, NULL, NULL}
@@ -641,6 +658,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_error_exception___construct, 0, 0, 0)
        ZEND_ARG_INFO(0, severity)
        ZEND_ARG_INFO(0, filename)
        ZEND_ARG_INFO(0, lineno)
+       ZEND_ARG_INFO(0, previous)
 ZEND_END_ARG_INFO()
 
 static const zend_function_entry error_exception_functions[] = {
@@ -666,6 +684,7 @@ void zend_register_default_exception(TSRMLS_D) /* {{{ */
        zend_declare_property_null(default_exception_ce, "file", sizeof("file")-1, ZEND_ACC_PROTECTED TSRMLS_CC);
        zend_declare_property_null(default_exception_ce, "line", sizeof("line")-1, ZEND_ACC_PROTECTED TSRMLS_CC);
        zend_declare_property_null(default_exception_ce, "trace", sizeof("trace")-1, ZEND_ACC_PRIVATE TSRMLS_CC);
+       zend_declare_property_null(default_exception_ce, "previous", sizeof("previous")-1, ZEND_ACC_PRIVATE TSRMLS_CC);
 
        INIT_CLASS_ENTRY(ce, "ErrorException", error_exception_functions);
        error_exception_ce = zend_register_internal_class_ex(&ce, default_exception_ce, NULL TSRMLS_CC);