]> granicus.if.org Git - php/commitdiff
- MFH Exception handling
authorMarcus Boerger <helly@php.net>
Sun, 13 Jul 2008 21:42:49 +0000 (21:42 +0000)
committerMarcus Boerger <helly@php.net>
Sun, 13 Jul 2008 21:42:49 +0000 (21:42 +0000)
[DOC]
- Exceptions can be thrown while exceptions are pending, they are linked
- Exceptions can be handled in __destruct
- Add optional Exception $previous parameter to
  . Exception::__construct
  . ErrorException::__construct

Zend/tests/exception_007.phpt [new file with mode: 0755]
Zend/tests/exception_008.phpt [new file with mode: 0755]
Zend/tests/gc_030.phpt
Zend/zend_exceptions.c
Zend/zend_exceptions.h
Zend/zend_objects.c

diff --git a/Zend/tests/exception_007.phpt b/Zend/tests/exception_007.phpt
new file mode 100755 (executable)
index 0000000..953e765
--- /dev/null
@@ -0,0 +1,36 @@
+--TEST--
+Setting previous exception
+--FILE--
+<?php
+
+try {
+       try {
+               throw new Exception("First", 1, new Exception("Another", 0, NULL));
+       }
+       catch (Exception $e) {
+               throw new Exception("Second", 2, $e);
+       }
+}
+catch (Exception $e) {
+       throw new Exception("Third", 3, $e);
+}
+
+?>
+===DONE===
+--EXPECTF--
+Fatal error: Uncaught exception 'Exception' with message 'Another' in %sexception_007.php:%d
+Stack trace:
+#0 {main}
+
+Next exception 'Exception' with message 'First' in %sexception_007.php:%d
+Stack trace:
+#0 {main}
+
+Next exception 'Exception' with message 'Second' in %sexception_007.php:%d
+Stack trace:
+#0 {main}
+
+Next exception 'Exception' with message 'Third' in %sexception_007.php:%d
+Stack trace:
+#0 {main}
+  thrown in %sexception_007.php on line %d
diff --git a/Zend/tests/exception_008.phpt b/Zend/tests/exception_008.phpt
new file mode 100755 (executable)
index 0000000..0d40541
--- /dev/null
@@ -0,0 +1,36 @@
+--TEST--
+Exception in __destruct while exception is pending
+--FILE--
+<?php
+
+class TestFirst
+{
+       function __destruct() {
+               throw new Exception("First");
+       }
+}
+
+class TestSecond
+{
+       function __destruct() {
+               throw new Exception("Second");          
+       }
+}
+
+$ar = array(new TestFirst, new TestSecond);
+
+unset($ar);
+
+?>
+===DONE===
+--EXPECTF--
+Fatal error: Uncaught exception 'Exception' with message 'First' in %sexception_008.php:%d
+Stack trace:
+#0 %sexception_008.php(%d): TestFirst->__destruct()
+#1 {main}
+
+Next exception 'Exception' with message 'Second' in %sexception_008.php:%d
+Stack trace:
+#0 %sexception_008.php(%d): TestSecond->__destruct()
+#1 {main}
+  thrown in %sexception_008.php on line %d
index 2c5e197bf8e2dfc10e3bbec29ce5daf7fbc49e86..27db29d981b10ea708fc6e98b85266d1a1f8b03f 100644 (file)
@@ -18,4 +18,14 @@ unset($f1, $f2);
 gc_collect_cycles();
 ?>
 --EXPECTF--
-Fatal error: Ignoring exception from foo::__destruct() while an exception is already active (Uncaught Exception in %sgc_030.php on line %d) in %sgc_030.php on line %d
+Fatal error: Uncaught exception 'Exception' with message 'foobar' in %sgc_030.php:%d
+Stack trace:
+#0 [internal function]: foo->__destruct()
+#1 %sgc_030.php(%d): gc_collect_cycles()
+#2 {main}
+
+Next exception 'Exception' with message 'foobar' in %sgc_030.php:%d
+Stack trace:
+#0 %sgc_030.php(%d): foo->__destruct()
+#1 {main}
+  thrown in %sgc_030.php on line %d
\ No newline at end of file
index 3c5f9c67e2de674964bb47cb3e0948ad06dd0b30..f71b53db70aa20ae71d5478c8d89478a1b9ed4da 100644 (file)
@@ -33,13 +33,37 @@ 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_exception_set_previous(zval *add_previous TSRMLS_DC)
+{
+       zval *exception = EG(exception), *previous;
+
+       if (exception == add_previous || !add_previous) {
+               return;
+       }
+       if (Z_TYPE_P(add_previous) != IS_OBJECT && !instanceof_function(Z_OBJCE_P(add_previous), default_exception_ce TSRMLS_CC)) {
+               zend_error(E_ERROR, "Cannot set non exception as previous exception");
+               return;
+       }
+       if (!exception) {
+               EG(exception) = add_previous;
+               return;
+       }
+       while (exception && exception != add_previous && Z_OBJ_HANDLE_P(exception) != Z_OBJ_HANDLE_P(add_previous)) {
+               previous = zend_read_property(default_exception_ce, exception, "previous", sizeof("previous")-1, 1 TSRMLS_CC);
+               if (Z_TYPE_P(previous) == IS_NULL) {
+                       zend_update_property(default_exception_ce, exception, "previous", sizeof("previous")-1, add_previous TSRMLS_CC);
+                       return;
+               }
+               exception = previous;
+       }
+}
+
 void zend_throw_exception_internal(zval *exception TSRMLS_DC) /* {{{ */
 {
        if (exception != NULL) {
-               if (EG(exception)) {
-                       zend_update_property(default_exception_ce, exception, "previous", sizeof("previous")-1, EG(exception) TSRMLS_CC);
-               }
+               zval *previous = EG(exception);
                EG(exception) = exception;
+               zend_exception_set_previous(previous TSRMLS_CC);
        }
        if (!EG(current_execute_data)) {
                zend_error(E_ERROR, "Exception thrown without a stack frame");
@@ -129,8 +153,8 @@ ZEND_METHOD(exception, __construct)
        zval  *object, *previous = NULL;
        int    argc = ZEND_NUM_ARGS(), message_len;
 
-       if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc TSRMLS_CC, "|slO", &message, &message_len, &code, &previous, default_exception_ce) == FAILURE) {
-               zend_error(E_ERROR, "Wrong parameters for Exception([string $exception [, long $code ]])");
+       if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc TSRMLS_CC, "|slO!", &message, &message_len, &code, &previous, default_exception_ce) == FAILURE) {
+               zend_error(E_ERROR, "Wrong parameters for Exception([string $exception [, long $code [, Exception $previous = NULL]]])");
        }
 
        object = getThis();
@@ -158,8 +182,8 @@ ZEND_METHOD(error_exception, __construct)
        zval  *object, *previous = NULL;
        int    argc = ZEND_NUM_ARGS(), message_len, filename_len;
 
-       if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc TSRMLS_CC, "|sllslO", &message, &message_len, &code, &severity, &filename, &filename_len, &lineno, &previous, default_exception_ce) == FAILURE) {
-               zend_error(E_ERROR, "Wrong parameters for ErrorException([string $exception [, long $code, [ long $severity, [ string $filename, [ long $lineno ]]]]])");
+       if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc TSRMLS_CC, "|sllslO!", &message, &message_len, &code, &severity, &filename, &filename_len, &lineno, &previous, default_exception_ce) == FAILURE) {
+               zend_error(E_ERROR, "Wrong parameters for ErrorException([string $exception [, long $code, [ long $severity, [ string $filename, [ long $lineno  [, Exception $previous = NULL]]]]]])");
        }
 
        object = getThis();
index 094ea62b6ed623cbf375221751a49aff141714c3..0b057c697900c35e6c1d99f46c1fc13a772d84ec 100644 (file)
@@ -26,6 +26,8 @@
 
 BEGIN_EXTERN_C()
 
+ZEND_API void zend_exception_set_previous(zval *add_previous TSRMLS_DC);
+
 void zend_throw_exception_internal(zval *exception TSRMLS_DC);
 
 void zend_register_default_exception(TSRMLS_D);
index 3dcc15eaaf6c9af0b6b18b9c397dab4f3a14523a..48140056935a5b8575a3c77014a4a9accf4b4301 100644 (file)
@@ -99,20 +99,7 @@ ZEND_API void zend_objects_destroy_object(zend_object *object, zend_object_handl
                old_exception = EG(exception);
                EG(exception) = NULL;
                zend_call_method_with_0_params(&obj, object->ce, &destructor, ZEND_DESTRUCTOR_FUNC_NAME, NULL);
-               if (old_exception) {
-                       if (EG(exception)) {
-                               zend_class_entry *default_exception_ce = zend_exception_get_default(TSRMLS_C);
-                               zval *file = zend_read_property(default_exception_ce, old_exception, "file", sizeof("file")-1, 1 TSRMLS_CC);
-                               zval *line = zend_read_property(default_exception_ce, old_exception, "line", sizeof("line")-1, 1 TSRMLS_CC);
-
-                               zval_ptr_dtor(&obj);
-                               zval_ptr_dtor(&EG(exception));
-                               EG(exception) = old_exception;
-                               zend_error(E_ERROR, "Ignoring exception from %s::__destruct() while an exception is already active (Uncaught %s in %s on line %ld)", 
-                                       object->ce->name, Z_OBJCE_P(old_exception)->name, Z_STRVAL_P(file), Z_LVAL_P(line));
-                       }
-                       EG(exception) = old_exception;
-               }
+               zend_exception_set_previous(old_exception TSRMLS_CC);
                zval_ptr_dtor(&obj);
        }
 }