Browse Source

- MFH Exception handling

[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
PECL
Marcus Boerger 18 years ago
parent
commit
2bf3bfc746
  1. 36
      Zend/tests/exception_007.phpt
  2. 36
      Zend/tests/exception_008.phpt
  3. 12
      Zend/tests/gc_030.phpt
  4. 38
      Zend/zend_exceptions.c
  5. 2
      Zend/zend_exceptions.h
  6. 15
      Zend/zend_objects.c

36
Zend/tests/exception_007.phpt

@ -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

36
Zend/tests/exception_008.phpt

@ -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

12
Zend/tests/gc_030.phpt

@ -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

38
Zend/zend_exceptions.c

@ -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();

2
Zend/zend_exceptions.h

@ -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);

15
Zend/zend_objects.c

@ -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);
}
}

Loading…
Cancel
Save