diff --git a/README.md b/README.md index 3ef8cf3..72f2261 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ $obj = null; // outputs "Object destroyed" This extension adds `Weak` namespace and all entities are created inside it. -There are no INI setting, constants or exceptions provided by this extension. +There are no INI setting or constants provided by this extension. Brief docs about [`Weak\Reference` class](./stubs/weak/Reference.php) and [functions](./stubs/weak/functions.php) may be seen in [stub files](./stubs/weak). @@ -35,6 +35,7 @@ may be seen in [stub files](./stubs/weak). Short list if what provided by this extension is: - `class Weak\Reference` + - `class Weak\NotifierException extend Exception` - `function Weak\refcounted()` - `function Weak\refcount()` - `function Weak\weakrefcounted()` @@ -51,8 +52,9 @@ Note that notification happens *after* referent object destruction, so at the ti will return `null` (unless rare case when object refcount get incremented in destructor, e.g. by storing destructing value somewhere else). -Callback notifier will not be called if referent object destructor or previous notifier callback throws exception, whether -array notifier get executed even in such cases. +If object destructor or one or more notifiers throw exception, all further notifier callbacks will be called as if +that exception was thrown inside `try-catch` block. In case one or more exception was thrown, `Weak\NotifierException` +will be thrown and all thrown exceptions will be available via `Weak\NotifierException::getExceptions()` method. ### Cloning @@ -157,16 +159,28 @@ You may also want to add php-weak extension as a [composer.json dependency](http with custom one, which meta-code is: ```php -run_original_dtor_obj($object); +$exceptions = []; + +try { + run_original_dtor_obj($object); +} catch(Throwable $e) { + $exceptions[] = $e; +} foreach($weak_references as $weak_ref_object_handle => $weak_reference) { if (is_array($weak_reference->notifier)) { $weak_reference->notifier[] = $weak_reference; - } elseif (is_callable($weak_reference->notifier) && $no_exception_thrown) { - $weak_reference->notifier($weak_reference); + } elseif (is_callable($weak_reference->notifier)) { + try { + $weak_reference->notifier($weak_reference); + } catch(Throwable $e) { + $exceptions[] = $e; + } } - - unset($weak_references[$weak_ref_object_handle]); +} + +if ($exceptions) { + throw new Weak\NotifierException('One or more exceptions thrown during notifiers calling', $exceptions); } ``` diff --git a/config.m4 b/config.m4 index 93cc9c2..8b77cda 100644 --- a/config.m4 +++ b/config.m4 @@ -27,9 +27,10 @@ if test "$PHP_WEAK" != "no"; then fi fi - PHP_NEW_EXTENSION(weak, [ \ - weak.c \ - php_weak_reference.c \ - php_weak_functions.c \ + PHP_NEW_EXTENSION(weak, [ \ + weak.c \ + php_weak_notifier_exception.c \ + php_weak_reference.c \ + php_weak_functions.c \ ], $ext_shared,, -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1) fi diff --git a/config.w32 b/config.w32 index 563b40c..c09ddcb 100644 --- a/config.w32 +++ b/config.w32 @@ -4,5 +4,5 @@ ARG_ENABLE("weak", "enable weak support", "no"); if (PHP_WEAK != "no") { - EXTENSION("weak", "weak.c php_weak_reference.c php_weak_functions.c", PHP_WEAK_SHARED, "/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1"); + EXTENSION("weak", "weak.c php_weak_notifier_exception.c php_weak_reference.c php_weak_functions.c", PHP_WEAK_SHARED, "/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1"); } diff --git a/php_weak_notifier_exception.c b/php_weak_notifier_exception.c new file mode 100644 index 0000000..f91b288 --- /dev/null +++ b/php_weak_notifier_exception.c @@ -0,0 +1,131 @@ +/* + +----------------------------------------------------------------------+ + | This file is part of the pinepain/php-weak PHP extension. | + | | + | Copyright (c) 2016 Bogdan Padalko | + | | + | Licensed under the MIT license: http://opensource.org/licenses/MIT | + | | + | For the full copyright and license information, please view the | + | LICENSE file that was distributed with this source or visit | + | http://opensource.org/licenses/MIT | + +----------------------------------------------------------------------+ +*/ + +#include "php_weak_notifier_exception.h" +#include "php_weak.h" +#include "zend_exceptions.h" + + +zend_class_entry *php_weak_notifier_exception_class_entry; +#define this_ce php_weak_notifier_exception_class_entry + + +static zend_object *php_weak_notifier_exception_ctor(zend_class_entry *ce) /* {{{ */ +{ + zval obj, thrown; + zend_object *object; + + Z_OBJ(obj) = object = ce->parent->create_object(ce); + + array_init_size(&thrown, 0); + zend_update_property(php_weak_notifier_exception_class_entry, &obj, ZEND_STRL("exceptions"), &thrown); + + return object; +} /* }}} */ + + +void php_weak_create_notifier_exception(zval *exception, const char *message, zval *thrown) /* {{{ */ +{ + object_init_ex(exception, this_ce); + zend_update_property_string(zend_ce_exception, exception, ZEND_STRL("message"), message); + zend_update_property(php_weak_notifier_exception_class_entry, exception, ZEND_STRL("exceptions"), thrown); +} /* }}} */ + +static PHP_METHOD(NotifierException, __construct) /* {{{ */ +{ + zend_string *message = NULL; + zend_long code = 0; + + zval tmp; + zval *exceptions = NULL; + zval *previous = NULL; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "|SalO!", &message, &exceptions, &code, &previous, zend_ce_throwable) == FAILURE) { + return; + } + + if (message) { + zend_update_property_str(zend_ce_exception, getThis(), ZEND_STRL("message"), message); + } + + if (exceptions) { + zend_update_property(this_ce, getThis(), ZEND_STRL("exceptions"), exceptions); + } else { + array_init_size(&tmp, 0); + zend_update_property(this_ce, getThis(), ZEND_STRL("exceptions"), &tmp); + } + + if (code) { + zend_update_property_long(zend_ce_exception, getThis(), ZEND_STRL("code"), code); + } + + if (previous) { + zend_update_property(zend_ce_exception, getThis(), ZEND_STRL("previous"), previous); + } +} + +static PHP_METHOD(NotifierException, getExceptions) /* {{{ */ +{ + zval rv; + + if (zend_parse_parameters_none() == FAILURE) { + return; + } + + RETVAL_ZVAL(zend_read_property(php_weak_notifier_exception_class_entry, getThis(), ZEND_STRL("exceptions"), 0, &rv), 1, 0); +} /* }}} */ + + +ZEND_BEGIN_ARG_INFO_EX(arginfo_notifier_exception___construct, ZEND_SEND_BY_VAL, ZEND_RETURN_VALUE, 0) + ZEND_ARG_INFO(0, message) + ZEND_ARG_INFO(0, exceptions) + ZEND_ARG_INFO(0, code) + ZEND_ARG_INFO(0, previous) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_notifier_exception_getExceptions, ZEND_SEND_BY_VAL, ZEND_RETURN_VALUE, 0) +ZEND_END_ARG_INFO() + + +static const zend_function_entry php_weak_notifier_exception_methods[] = { /* {{{ */ + PHP_ME(NotifierException, __construct, arginfo_notifier_exception___construct, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR) + PHP_ME(NotifierException, getExceptions, arginfo_notifier_exception_getExceptions, ZEND_ACC_PUBLIC) + + PHP_FE_END +}; /* }}} */ + + +PHP_MINIT_FUNCTION (php_weak_notifier_exception) /* {{{ */ +{ + zend_class_entry ce; + + INIT_NS_CLASS_ENTRY(ce, PHP_WEAK_NS, "NotifierException", php_weak_notifier_exception_methods); + this_ce = zend_register_internal_class_ex(&ce, zend_ce_exception); + /*this_ce->create_object = php_weak_notifier_exception_ctor;*/ + + zend_declare_property_null(this_ce, ZEND_STRL("exceptions"), ZEND_ACC_PRIVATE); + + + return SUCCESS; +} /* }}} */ + + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/php_weak_notifier_exception.h b/php_weak_notifier_exception.h new file mode 100644 index 0000000..2d1b675 --- /dev/null +++ b/php_weak_notifier_exception.h @@ -0,0 +1,41 @@ +/* + +----------------------------------------------------------------------+ + | This file is part of the pinepain/php-weak PHP extension. | + | | + | Copyright (c) 2016 Bogdan Padalko | + | | + | Licensed under the MIT license: http://opensource.org/licenses/MIT | + | | + | For the full copyright and license information, please view the | + | LICENSE file that was distributed with this source or visit | + | http://opensource.org/licenses/MIT | + +----------------------------------------------------------------------+ +*/ + +#ifndef PHP_WEAK_NOTIFIER_EXCEPTION_H +#define PHP_WEAK_NOTIFIER_EXCEPTION_H + +#include "php.h" + +#ifdef ZTS +#include "TSRM.h" +#endif + +extern zend_class_entry *php_weak_notifier_exception_class_entry; + +void php_weak_create_notifier_exception(zval *exception, const char *message, zval *thrown); + +PHP_MINIT_FUNCTION(php_weak_notifier_exception); + + +#endif /* PHP_WEAK_NOTIFIER_EXCEPTION_H */ + + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/php_weak_reference.c b/php_weak_reference.c index ba16177..8ad9ef9 100644 --- a/php_weak_reference.c +++ b/php_weak_reference.c @@ -13,6 +13,7 @@ */ #include "php_weak_reference.h" +#include "php_weak_notifier_exception.h" #include "php_weak.h" #include "zend_exceptions.h" #include "zend_interfaces.h" @@ -32,6 +33,7 @@ static zend_object_get_debug_info_t spl_object_storage_get_debug_info_orig_handl php_weak_reference_t *php_weak_reference_init(zval *this_ptr, zval *referent_zv, zval *notifier_zv); +static inline void php_weak_store_exceptions(zval *exceptions, zval *tmp); static int php_weak_reference_check_notifier(zval *notifier, zval *this); @@ -197,17 +199,29 @@ void php_weak_reference_call_notifier(zval *reference, zval *notifier) /* {{{ */ } /* }}} */ + void php_weak_referent_object_dtor_obj(zend_object *object) /* {{{ */ { php_weak_referent_t *referent = php_weak_referent_find_ptr(object->handle); + zval exceptions; + zval tmp; + + ZVAL_UNDEF(&exceptions); + assert(NULL != referent); assert(NULL != PHP_WEAK_G(referents)); zend_ulong handle; php_weak_reference_t *reference; - referent->original_handlers->dtor_obj(object); + if (referent->original_handlers->dtor_obj) { + referent->original_handlers->dtor_obj(object); + + if (EG(exception)) { + php_weak_store_exceptions(&exceptions, &tmp); + } + } ZEND_HASH_REVERSE_FOREACH_PTR(&referent->weak_references, reference) { handle = _p->h; @@ -221,11 +235,12 @@ void php_weak_referent_object_dtor_obj(zend_object *object) /* {{{ */ break; case PHP_WEAK_NOTIFIER_CALLBACK: /* callback notifier */ - if (!EG(exception)) { - php_weak_reference_call_notifier(&reference->this_ptr, &reference->notifier); + php_weak_reference_call_notifier(&reference->this_ptr, &reference->notifier); + + if (EG(exception)) { + php_weak_store_exceptions(&exceptions, &tmp); } break; - default: break; } @@ -234,6 +249,13 @@ void php_weak_referent_object_dtor_obj(zend_object *object) /* {{{ */ } ZEND_HASH_FOREACH_END(); zend_hash_index_del(PHP_WEAK_G(referents), referent->handle); + + if (!Z_ISUNDEF(exceptions)) { + zval exception; + php_weak_create_notifier_exception(&exception, "One or more exceptions thrown during notifiers calling", &exceptions); + + zend_throw_exception_object(&exception); + } } /* }}} */ void php_weak_globals_referents_ht_dtor(zval *zv) /* {{{ */ @@ -340,6 +362,18 @@ php_weak_reference_t *php_weak_reference_init(zval *this_ptr, zval *referent_zv, return reference; } /* }}} */ +static inline void php_weak_store_exceptions(zval *exceptions, zval *tmp) +{ + if (Z_ISUNDEF_P(exceptions)) { + array_init(exceptions); + } + + ZVAL_OBJ(tmp, EG(exception)); + Z_ADDREF_P(tmp); + add_next_index_zval(exceptions, tmp); + + zend_clear_exception(); +} static int php_weak_reference_check_notifier(zval *notifier, zval *this) /* {{{ */ { @@ -628,10 +662,10 @@ PHP_MINIT_FUNCTION (php_weak_reference) /* {{{ */ zend_class_entry ce; INIT_NS_CLASS_ENTRY(ce, PHP_WEAK_NS, "Reference", php_weak_reference_methods); - ce.serialize = zend_class_serialize_deny; - ce.unserialize = zend_class_unserialize_deny; this_ce = zend_register_internal_class(&ce); this_ce->create_object = php_weak_reference_ctor; + this_ce->serialize = zend_class_serialize_deny; + this_ce->unserialize = zend_class_unserialize_deny; memcpy(&php_weak_reference_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); diff --git a/stubs/weak/NotifierException.php b/stubs/weak/NotifierException.php new file mode 100644 index 0000000..8afedce --- /dev/null +++ b/stubs/weak/NotifierException.php @@ -0,0 +1,23 @@ +exceptions; + } +} diff --git a/stubs/weak/functions.php b/stubs/weak/functions.php index 65be179..a813af1 100644 --- a/stubs/weak/functions.php +++ b/stubs/weak/functions.php @@ -60,7 +60,7 @@ function weakrefcount(object $value) : int {} function weakrefs(object $value) : array {} /** - * Get object's weak references + * Get object's handle id * * @param object $value * diff --git a/tests/.testsuite.php b/tests/.testsuite.php index ebae93b..7d9fef9 100644 --- a/tests/.testsuite.php +++ b/tests/.testsuite.php @@ -32,6 +32,21 @@ public function exception_export(\Throwable $e) echo get_class($e), ': ', $e->getMessage(), PHP_EOL; } + public function weak_exception_export(\Weak\NotifierException $e) + { + $this->exception_export($e); + + if ($e->getPrevious()) { + echo 'previous: '; + $this->exception_export($e->getPrevious()); + } + + foreach ($e->getExceptions() as $thrown) { + echo ' '; + $this->exception_export($thrown); + } + } + public function export($value) { echo gettype($value), ': ', var_export($value, true), PHP_EOL; diff --git a/tests/002-notifier-exception-basic.phpt b/tests/002-notifier-exception-basic.phpt new file mode 100644 index 0000000..518b05d --- /dev/null +++ b/tests/002-notifier-exception-basic.phpt @@ -0,0 +1,37 @@ +--TEST-- +Weak\NotifierException - basic +--SKIPIF-- + +--FILE-- + +EOF +--EXPECTF-- +object(Weak\NotifierException)#1 (8) { + ["message":protected]=> + string(4) "Test" + ["string":"Exception":private]=> + string(0) "" + ["code":protected]=> + int(0) + ["file":protected]=> + string(%d) "%s" + ["line":protected]=> + int(5) + ["trace":"Exception":private]=> + array(0) { + } + ["previous":"Exception":private]=> + NULL + ["exceptions":"Weak\NotifierException":private]=> + array(0) { + } +} +EOF diff --git a/tests/002-reference-clone_extended.phpt b/tests/002-reference-clone_extended.phpt index 4510703..ecbb108 100644 --- a/tests/002-reference-clone_extended.phpt +++ b/tests/002-reference-clone_extended.phpt @@ -20,71 +20,59 @@ $helper = require '.testsuite.php'; $obj = new \stdClass(); -$notifier = function (Weak\Reference $ref) use ($helper) { +$notifier = function (Weak\Reference $ref) { echo 'Notified: '; - $helper->dump($ref); + var_dump($ref); }; $wr = new ExtendedReference($obj, $notifier, [42]); -$helper->dump($wr); +var_dump($wr); $helper->line(); $wr2 = clone $wr; -$helper->dump($wr2); +var_dump($wr2); $helper->line(); ?> EOF --EXPECT-- -object(WeakTests\ExtendedReference)#4 (3) refcount(3){ +object(WeakTests\ExtendedReference)#4 (3) { ["test":"WeakTests\ExtendedReference":private]=> - array(1) refcount(2){ + array(1) { [0]=> int(42) } ["referent":"Weak\Reference":private]=> - object(stdClass)#2 (0) refcount(2){ + object(stdClass)#2 (0) { } ["notifier":"Weak\Reference":private]=> - object(Closure)#3 (2) refcount(3){ - ["static"]=> - array(1) refcount(1){ - ["helper"]=> - object(Testsuite)#1 (0) refcount(4){ - } - } + object(Closure)#3 (1) { ["parameter"]=> - array(1) refcount(1){ + array(1) { ["$ref"]=> - string(10) "" refcount(1) + string(10) "" } } } -object(WeakTests\ExtendedReference)#5 (3) refcount(3){ +object(WeakTests\ExtendedReference)#5 (3) { ["test":"WeakTests\ExtendedReference":private]=> - array(1) refcount(3){ + array(1) { [0]=> int(42) } ["referent":"Weak\Reference":private]=> - object(stdClass)#2 (0) refcount(2){ + object(stdClass)#2 (0) { } ["notifier":"Weak\Reference":private]=> - object(Closure)#3 (2) refcount(4){ - ["static"]=> - array(1) refcount(1){ - ["helper"]=> - object(Testsuite)#1 (0) refcount(4){ - } - } + object(Closure)#3 (1) { ["parameter"]=> - array(1) refcount(1){ + array(1) { ["$ref"]=> - string(10) "" refcount(1) + string(10) "" } } } diff --git a/tests/002-reference-dump_extended.phpt b/tests/002-reference-dump_extended.phpt index b7a90e4..a79bacb 100644 --- a/tests/002-reference-dump_extended.phpt +++ b/tests/002-reference-dump_extended.phpt @@ -16,49 +16,49 @@ $obj = new stdClass(); $wr = new ExtendedReference($obj, function (Weak\Reference $reference) {}, [42]); -$helper->dump($wr); +var_dump($wr); $helper->line(); $obj = null; -$helper->dump($wr); +var_dump($wr); $helper->line(); ?> EOF --EXPECT-- -object(WeakTests\ExtendedReference)#3 (3) refcount(3){ +object(WeakTests\ExtendedReference)#3 (3) { ["test":"WeakTests\ExtendedReference":private]=> - array(1) refcount(2){ + array(1) { [0]=> int(42) } ["referent":"Weak\Reference":private]=> - object(stdClass)#2 (0) refcount(2){ + object(stdClass)#2 (0) { } ["notifier":"Weak\Reference":private]=> - object(Closure)#4 (1) refcount(2){ + object(Closure)#4 (1) { ["parameter"]=> - array(1) refcount(1){ + array(1) { ["$reference"]=> - string(10) "" refcount(1) + string(10) "" } } } -object(WeakTests\ExtendedReference)#3 (3) refcount(3){ +object(WeakTests\ExtendedReference)#3 (3) { ["test":"WeakTests\ExtendedReference":private]=> - array(1) refcount(2){ + array(1) { [0]=> int(42) } ["referent":"Weak\Reference":private]=> NULL ["notifier":"Weak\Reference":private]=> - object(Closure)#4 (1) refcount(2){ + object(Closure)#4 (1) { ["parameter"]=> - array(1) refcount(1){ + array(1) { ["$reference"]=> - string(10) "" refcount(1) + string(10) "" } } } diff --git a/tests/002-reference-exception_in_callback.phpt b/tests/002-reference-exception_in_callback.phpt index 617dd2f..46d7fb6 100644 --- a/tests/002-reference-exception_in_callback.phpt +++ b/tests/002-reference-exception_in_callback.phpt @@ -21,18 +21,14 @@ $wr = new Weak\Reference($obj, $callback); try { $obj = null; -} catch(\Exception $e) { - $helper->exception_export($e); - - if ($e->getPrevious()) { - echo 'previous:'; - $helper->exception_export($e->getPrevious()); - } +} catch(\Weak\NotifierException $e) { + $helper->weak_exception_export($e); } ?> EOF --EXPECT-- WeakTests\TrackingDtor's destructor called -Exception: Test exception from callback +Weak\NotifierException: One or more exceptions thrown during notifiers calling + Exception: Test exception from callback EOF diff --git a/tests/002-reference-exception_in_multiple_callbacks.phpt b/tests/002-reference-exception_in_multiple_callbacks.phpt index e3a2cb9..769228b 100644 --- a/tests/002-reference-exception_in_multiple_callbacks.phpt +++ b/tests/002-reference-exception_in_multiple_callbacks.phpt @@ -15,6 +15,7 @@ $obj = new \WeakTests\TrackingDtor(0); function callback_throws($id) { return function (Weak\Reference $reference) use ($id) { + echo 'Callback #' . $id, ' called', PHP_EOL; throw new \Exception('Test exception from callback #' . $id); }; } @@ -22,34 +23,38 @@ function callback_throws($id) function callback_ok($id) { return function (Weak\Reference $reference) use ($id) { - echo 'Callback #' . $id, ' called', PHP_EOL; }; } -$wr3 = new Weak\Reference($obj, callback_ok(3)); -$wr2 = new Weak\Reference($obj, callback_throws(2)); +$wr5 = new Weak\Reference($obj, callback_ok(5)); +$wr4 = new Weak\Reference($obj, callback_ok(4)); +$wr3 = new Weak\Reference($obj, callback_throws(3)); +$wr2 = new Weak\Reference($obj, callback_ok(2)); $wr1 = new Weak\Reference($obj, callback_throws(1)); $wr0 = new Weak\Reference($obj, callback_ok(0)); try { $obj = null; -} catch(\Exception $e) { - $helper->exception_export($e); - - if ($e->getPrevious()) { - echo 'previous:'; - $helper->exception_export($e->getPrevious()); - } +} catch(\Weak\NotifierException $e) { + $helper->weak_exception_export($e); } + $helper->line(); ?> EOF --EXPECT-- WeakTests\TrackingDtor's destructor called Callback #0 called -Exception: Test exception from callback #1 +Callback #1 called +Callback #2 called +Callback #3 called +Callback #4 called +Callback #5 called +Weak\NotifierException: One or more exceptions thrown during notifiers calling + Exception: Test exception from callback #1 + Exception: Test exception from callback #3 EOF diff --git a/tests/002-reference-exception_in_orig_dtor.phpt b/tests/002-reference-exception_in_orig_dtor.phpt index d924c35..093305b 100644 --- a/tests/002-reference-exception_in_orig_dtor.phpt +++ b/tests/002-reference-exception_in_orig_dtor.phpt @@ -11,6 +11,8 @@ $helper = require '.testsuite.php'; class BadDtor { function __destruct() { + echo 'Dtor called', PHP_EOL; + throw new Exception('Test exception from dtor'); } } @@ -26,17 +28,15 @@ $wr = new Weak\Reference($obj, $callback); try { $obj = null; -} catch(\Exception $e) { - $helper->exception_export($e); - - if ($e->getPrevious()) { - echo 'previous:'; - $helper->exception_export($e->getPrevious()); - } +} catch(\Weak\NotifierException $e) { + $helper->weak_exception_export($e); } ?> EOF --EXPECT-- -Exception: Test exception from dtor +Dtor called +Weak callback called +Weak\NotifierException: One or more exceptions thrown during notifiers calling + Exception: Test exception from dtor EOF diff --git a/tests/002-reference-exception_in_orig_dtor_and_callback.phpt b/tests/002-reference-exception_in_orig_dtor_and_callback.phpt index bf64bc5..639e175 100644 --- a/tests/002-reference-exception_in_orig_dtor_and_callback.phpt +++ b/tests/002-reference-exception_in_orig_dtor_and_callback.phpt @@ -11,6 +11,8 @@ $helper = require '.testsuite.php'; class BadDtor { function __destruct() { + echo 'Dtor called', PHP_EOL; + throw new Exception('Test exception from dtor'); } } @@ -18,6 +20,8 @@ class BadDtor { $obj = new BadDtor(); $callback = function (Weak\Reference $reference) { + echo 'Callback called', PHP_EOL; + throw new \Exception('Test exception from callback'); }; @@ -27,17 +31,16 @@ $wr = new Weak\Reference($obj, $callback); try { $obj = null; -} catch(\Exception $e) { - $helper->exception_export($e); - - if ($e->getPrevious()) { - echo 'previous:'; - $helper->exception_export($e->getPrevious()); - } +} catch(\Weak\NotifierException $e) { + $helper->weak_exception_export($e); } ?> EOF --EXPECT-- -Exception: Test exception from dtor +Dtor called +Callback called +Weak\NotifierException: One or more exceptions thrown during notifiers calling + Exception: Test exception from dtor + Exception: Test exception from callback EOF diff --git a/tests/002-reference-notifier_array.phpt b/tests/002-reference-notifier_array.phpt index d691b94..d8bbde1 100644 --- a/tests/002-reference-notifier_array.phpt +++ b/tests/002-reference-notifier_array.phpt @@ -14,69 +14,43 @@ $notifier = []; $wr = new Weak\Reference($obj, $notifier); -$helper->dump($notifier); +var_dump($notifier); $obj = null; -$helper->dump($notifier); +var_dump($notifier); $wr = null; -$helper->dump($notifier); +var_dump($notifier); $helper->line(); ?> EOF --EXPECT-- -array(0) refcount(4){ +array(0) { } -array(1) refcount(4){ +array(1) { [0]=> - object(Weak\Reference)#3 (2) refcount(2){ + object(Weak\Reference)#3 (2) { ["referent":"Weak\Reference":private]=> NULL ["notifier":"Weak\Reference":private]=> - array(1) refcount(5){ + array(1) { [0]=> - object(Weak\Reference)#3 (2) refcount(2){ - ["referent":"Weak\Reference":private]=> - NULL - ["notifier":"Weak\Reference":private]=> - array(1) refcount(6){ - [0]=> - object(Weak\Reference)#3 (2) refcount(2){ - ["referent":"Weak\Reference":private]=> - NULL - ["notifier":"Weak\Reference":private]=> - *RECURSION* - } - } - } + *RECURSION* } } } -array(1) refcount(4){ +array(1) { [0]=> - object(Weak\Reference)#3 (2) refcount(1){ + object(Weak\Reference)#3 (2) { ["referent":"Weak\Reference":private]=> NULL ["notifier":"Weak\Reference":private]=> - array(1) refcount(5){ + array(1) { [0]=> - object(Weak\Reference)#3 (2) refcount(1){ - ["referent":"Weak\Reference":private]=> - NULL - ["notifier":"Weak\Reference":private]=> - array(1) refcount(6){ - [0]=> - object(Weak\Reference)#3 (2) refcount(1){ - ["referent":"Weak\Reference":private]=> - NULL - ["notifier":"Weak\Reference":private]=> - *RECURSION* - } - } - } + *RECURSION* } } } diff --git a/tests/002-reference-notifier_array_clone.phpt b/tests/002-reference-notifier_array_clone.phpt index 6359f77..8749c2e 100644 --- a/tests/002-reference-notifier_array_clone.phpt +++ b/tests/002-reference-notifier_array_clone.phpt @@ -15,231 +15,91 @@ $wr = new Weak\Reference($obj, $notifier); $wr2 = clone $wr; -$helper->dump($notifier); +var_dump($notifier); $obj = null; -$helper->dump($notifier); +var_dump($notifier); $wr = null; -$helper->dump($notifier); +var_dump($notifier); $helper->line(); ?> EOF --EXPECT-- -array(0) refcount(5){ +array(0) { } -array(2) refcount(5){ +array(2) { [0]=> - object(Weak\Reference)#4 (2) refcount(2){ + object(Weak\Reference)#4 (2) { ["referent":"Weak\Reference":private]=> NULL ["notifier":"Weak\Reference":private]=> - array(2) refcount(6){ + array(2) { [0]=> - object(Weak\Reference)#4 (2) refcount(2){ - ["referent":"Weak\Reference":private]=> - NULL - ["notifier":"Weak\Reference":private]=> - array(2) refcount(7){ - [0]=> - object(Weak\Reference)#4 (2) refcount(2){ - ["referent":"Weak\Reference":private]=> - NULL - ["notifier":"Weak\Reference":private]=> - *RECURSION* - } - [1]=> - object(Weak\Reference)#3 (2) refcount(2){ - ["referent":"Weak\Reference":private]=> - NULL - ["notifier":"Weak\Reference":private]=> - *RECURSION* - } - } - } + *RECURSION* [1]=> - object(Weak\Reference)#3 (2) refcount(2){ + object(Weak\Reference)#3 (2) { ["referent":"Weak\Reference":private]=> NULL ["notifier":"Weak\Reference":private]=> - array(2) refcount(7){ - [0]=> - object(Weak\Reference)#4 (2) refcount(2){ - ["referent":"Weak\Reference":private]=> - NULL - ["notifier":"Weak\Reference":private]=> - *RECURSION* - } - [1]=> - object(Weak\Reference)#3 (2) refcount(2){ - ["referent":"Weak\Reference":private]=> - NULL - ["notifier":"Weak\Reference":private]=> - *RECURSION* - } - } + *RECURSION* } } } [1]=> - object(Weak\Reference)#3 (2) refcount(2){ + object(Weak\Reference)#3 (2) { ["referent":"Weak\Reference":private]=> NULL ["notifier":"Weak\Reference":private]=> - array(2) refcount(6){ + array(2) { [0]=> - object(Weak\Reference)#4 (2) refcount(2){ + object(Weak\Reference)#4 (2) { ["referent":"Weak\Reference":private]=> NULL ["notifier":"Weak\Reference":private]=> - array(2) refcount(7){ - [0]=> - object(Weak\Reference)#4 (2) refcount(2){ - ["referent":"Weak\Reference":private]=> - NULL - ["notifier":"Weak\Reference":private]=> - *RECURSION* - } - [1]=> - object(Weak\Reference)#3 (2) refcount(2){ - ["referent":"Weak\Reference":private]=> - NULL - ["notifier":"Weak\Reference":private]=> - *RECURSION* - } - } + *RECURSION* } [1]=> - object(Weak\Reference)#3 (2) refcount(2){ - ["referent":"Weak\Reference":private]=> - NULL - ["notifier":"Weak\Reference":private]=> - array(2) refcount(7){ - [0]=> - object(Weak\Reference)#4 (2) refcount(2){ - ["referent":"Weak\Reference":private]=> - NULL - ["notifier":"Weak\Reference":private]=> - *RECURSION* - } - [1]=> - object(Weak\Reference)#3 (2) refcount(2){ - ["referent":"Weak\Reference":private]=> - NULL - ["notifier":"Weak\Reference":private]=> - *RECURSION* - } - } - } + *RECURSION* } } } -array(2) refcount(5){ +array(2) { [0]=> - object(Weak\Reference)#4 (2) refcount(2){ + object(Weak\Reference)#4 (2) { ["referent":"Weak\Reference":private]=> NULL ["notifier":"Weak\Reference":private]=> - array(2) refcount(6){ + array(2) { [0]=> - object(Weak\Reference)#4 (2) refcount(2){ - ["referent":"Weak\Reference":private]=> - NULL - ["notifier":"Weak\Reference":private]=> - array(2) refcount(7){ - [0]=> - object(Weak\Reference)#4 (2) refcount(2){ - ["referent":"Weak\Reference":private]=> - NULL - ["notifier":"Weak\Reference":private]=> - *RECURSION* - } - [1]=> - object(Weak\Reference)#3 (2) refcount(1){ - ["referent":"Weak\Reference":private]=> - NULL - ["notifier":"Weak\Reference":private]=> - *RECURSION* - } - } - } + *RECURSION* [1]=> - object(Weak\Reference)#3 (2) refcount(1){ + object(Weak\Reference)#3 (2) { ["referent":"Weak\Reference":private]=> NULL ["notifier":"Weak\Reference":private]=> - array(2) refcount(7){ - [0]=> - object(Weak\Reference)#4 (2) refcount(2){ - ["referent":"Weak\Reference":private]=> - NULL - ["notifier":"Weak\Reference":private]=> - *RECURSION* - } - [1]=> - object(Weak\Reference)#3 (2) refcount(1){ - ["referent":"Weak\Reference":private]=> - NULL - ["notifier":"Weak\Reference":private]=> - *RECURSION* - } - } + *RECURSION* } } } [1]=> - object(Weak\Reference)#3 (2) refcount(1){ + object(Weak\Reference)#3 (2) { ["referent":"Weak\Reference":private]=> NULL ["notifier":"Weak\Reference":private]=> - array(2) refcount(6){ + array(2) { [0]=> - object(Weak\Reference)#4 (2) refcount(2){ + object(Weak\Reference)#4 (2) { ["referent":"Weak\Reference":private]=> NULL ["notifier":"Weak\Reference":private]=> - array(2) refcount(7){ - [0]=> - object(Weak\Reference)#4 (2) refcount(2){ - ["referent":"Weak\Reference":private]=> - NULL - ["notifier":"Weak\Reference":private]=> - *RECURSION* - } - [1]=> - object(Weak\Reference)#3 (2) refcount(1){ - ["referent":"Weak\Reference":private]=> - NULL - ["notifier":"Weak\Reference":private]=> - *RECURSION* - } - } + *RECURSION* } [1]=> - object(Weak\Reference)#3 (2) refcount(1){ - ["referent":"Weak\Reference":private]=> - NULL - ["notifier":"Weak\Reference":private]=> - array(2) refcount(7){ - [0]=> - object(Weak\Reference)#4 (2) refcount(2){ - ["referent":"Weak\Reference":private]=> - NULL - ["notifier":"Weak\Reference":private]=> - *RECURSION* - } - [1]=> - object(Weak\Reference)#3 (2) refcount(1){ - ["referent":"Weak\Reference":private]=> - NULL - ["notifier":"Weak\Reference":private]=> - *RECURSION* - } - } - } + *RECURSION* } } } diff --git a/tests/002-reference-notifier_array_reliability.phpt b/tests/002-reference-notifier_array_reliability.phpt index 292df29..727031e 100644 --- a/tests/002-reference-notifier_array_reliability.phpt +++ b/tests/002-reference-notifier_array_reliability.phpt @@ -20,7 +20,7 @@ $wr2 = new Weak\Reference($obj, function () { $wr2 = new Weak\Reference($obj, $notifier); -$helper->dump($notifier); +var_dump($notifier); try { $obj = null; @@ -28,227 +28,87 @@ try { $helper->exception_export($e); } -$helper->dump($notifier); +var_dump($notifier); $wr1 = null; -$helper->dump($notifier); +var_dump($notifier); $helper->line(); ?> EOF --EXPECT-- -array(0) refcount(5){ +array(0) { } -array(2) refcount(5){ +array(2) { [0]=> - object(Weak\Reference)#6 (2) refcount(2){ + object(Weak\Reference)#6 (2) { ["referent":"Weak\Reference":private]=> NULL ["notifier":"Weak\Reference":private]=> - array(2) refcount(6){ + array(2) { [0]=> - object(Weak\Reference)#6 (2) refcount(2){ - ["referent":"Weak\Reference":private]=> - NULL - ["notifier":"Weak\Reference":private]=> - array(2) refcount(7){ - [0]=> - object(Weak\Reference)#6 (2) refcount(2){ - ["referent":"Weak\Reference":private]=> - NULL - ["notifier":"Weak\Reference":private]=> - *RECURSION* - } - [1]=> - object(Weak\Reference)#3 (2) refcount(2){ - ["referent":"Weak\Reference":private]=> - NULL - ["notifier":"Weak\Reference":private]=> - *RECURSION* - } - } - } + *RECURSION* [1]=> - object(Weak\Reference)#3 (2) refcount(2){ + object(Weak\Reference)#3 (2) { ["referent":"Weak\Reference":private]=> NULL ["notifier":"Weak\Reference":private]=> - array(2) refcount(7){ - [0]=> - object(Weak\Reference)#6 (2) refcount(2){ - ["referent":"Weak\Reference":private]=> - NULL - ["notifier":"Weak\Reference":private]=> - *RECURSION* - } - [1]=> - object(Weak\Reference)#3 (2) refcount(2){ - ["referent":"Weak\Reference":private]=> - NULL - ["notifier":"Weak\Reference":private]=> - *RECURSION* - } - } + *RECURSION* } } } [1]=> - object(Weak\Reference)#3 (2) refcount(2){ + object(Weak\Reference)#3 (2) { ["referent":"Weak\Reference":private]=> NULL ["notifier":"Weak\Reference":private]=> - array(2) refcount(6){ + array(2) { [0]=> - object(Weak\Reference)#6 (2) refcount(2){ + object(Weak\Reference)#6 (2) { ["referent":"Weak\Reference":private]=> NULL ["notifier":"Weak\Reference":private]=> - array(2) refcount(7){ - [0]=> - object(Weak\Reference)#6 (2) refcount(2){ - ["referent":"Weak\Reference":private]=> - NULL - ["notifier":"Weak\Reference":private]=> - *RECURSION* - } - [1]=> - object(Weak\Reference)#3 (2) refcount(2){ - ["referent":"Weak\Reference":private]=> - NULL - ["notifier":"Weak\Reference":private]=> - *RECURSION* - } - } + *RECURSION* } [1]=> - object(Weak\Reference)#3 (2) refcount(2){ - ["referent":"Weak\Reference":private]=> - NULL - ["notifier":"Weak\Reference":private]=> - array(2) refcount(7){ - [0]=> - object(Weak\Reference)#6 (2) refcount(2){ - ["referent":"Weak\Reference":private]=> - NULL - ["notifier":"Weak\Reference":private]=> - *RECURSION* - } - [1]=> - object(Weak\Reference)#3 (2) refcount(2){ - ["referent":"Weak\Reference":private]=> - NULL - ["notifier":"Weak\Reference":private]=> - *RECURSION* - } - } - } + *RECURSION* } } } -array(2) refcount(5){ +array(2) { [0]=> - object(Weak\Reference)#6 (2) refcount(2){ + object(Weak\Reference)#6 (2) { ["referent":"Weak\Reference":private]=> NULL ["notifier":"Weak\Reference":private]=> - array(2) refcount(6){ + array(2) { [0]=> - object(Weak\Reference)#6 (2) refcount(2){ - ["referent":"Weak\Reference":private]=> - NULL - ["notifier":"Weak\Reference":private]=> - array(2) refcount(7){ - [0]=> - object(Weak\Reference)#6 (2) refcount(2){ - ["referent":"Weak\Reference":private]=> - NULL - ["notifier":"Weak\Reference":private]=> - *RECURSION* - } - [1]=> - object(Weak\Reference)#3 (2) refcount(1){ - ["referent":"Weak\Reference":private]=> - NULL - ["notifier":"Weak\Reference":private]=> - *RECURSION* - } - } - } + *RECURSION* [1]=> - object(Weak\Reference)#3 (2) refcount(1){ + object(Weak\Reference)#3 (2) { ["referent":"Weak\Reference":private]=> NULL ["notifier":"Weak\Reference":private]=> - array(2) refcount(7){ - [0]=> - object(Weak\Reference)#6 (2) refcount(2){ - ["referent":"Weak\Reference":private]=> - NULL - ["notifier":"Weak\Reference":private]=> - *RECURSION* - } - [1]=> - object(Weak\Reference)#3 (2) refcount(1){ - ["referent":"Weak\Reference":private]=> - NULL - ["notifier":"Weak\Reference":private]=> - *RECURSION* - } - } + *RECURSION* } } } [1]=> - object(Weak\Reference)#3 (2) refcount(1){ + object(Weak\Reference)#3 (2) { ["referent":"Weak\Reference":private]=> NULL ["notifier":"Weak\Reference":private]=> - array(2) refcount(6){ + array(2) { [0]=> - object(Weak\Reference)#6 (2) refcount(2){ + object(Weak\Reference)#6 (2) { ["referent":"Weak\Reference":private]=> NULL ["notifier":"Weak\Reference":private]=> - array(2) refcount(7){ - [0]=> - object(Weak\Reference)#6 (2) refcount(2){ - ["referent":"Weak\Reference":private]=> - NULL - ["notifier":"Weak\Reference":private]=> - *RECURSION* - } - [1]=> - object(Weak\Reference)#3 (2) refcount(1){ - ["referent":"Weak\Reference":private]=> - NULL - ["notifier":"Weak\Reference":private]=> - *RECURSION* - } - } + *RECURSION* } [1]=> - object(Weak\Reference)#3 (2) refcount(1){ - ["referent":"Weak\Reference":private]=> - NULL - ["notifier":"Weak\Reference":private]=> - array(2) refcount(7){ - [0]=> - object(Weak\Reference)#6 (2) refcount(2){ - ["referent":"Weak\Reference":private]=> - NULL - ["notifier":"Weak\Reference":private]=> - *RECURSION* - } - [1]=> - object(Weak\Reference)#3 (2) refcount(1){ - ["referent":"Weak\Reference":private]=> - NULL - ["notifier":"Weak\Reference":private]=> - *RECURSION* - } - } - } + *RECURSION* } } } diff --git a/tests/002-reference-notifier_not_called_after_wr_dies_first.phpt b/tests/002-reference-notifier_not_called_after_wr_dies_first.phpt index 9023924..13f4f58 100644 --- a/tests/002-reference-notifier_not_called_after_wr_dies_first.phpt +++ b/tests/002-reference-notifier_not_called_after_wr_dies_first.phpt @@ -25,8 +25,8 @@ $wr = new Weak\Reference($obj, $callback); try { $obj = null; -} catch(Exception $e) { - $helper->exception_export($e); +} catch(\Weak\NotifierException $e) { + $helper->weak_exception_export($e); $helper->line(); } @@ -37,7 +37,9 @@ $helper->line(); ?> EOF --EXPECT-- -Exception: Destructor throws exception +Weak notifier called +Weak\NotifierException: One or more exceptions thrown during notifiers calling + Exception: Destructor throws exception Referent object dead: ok Referent object invalid: ok diff --git a/weak.c b/weak.c index beef3eb..42c74f7 100644 --- a/weak.c +++ b/weak.c @@ -18,6 +18,7 @@ #include "php_weak_functions.h" #include "php_weak_reference.h" +#include "php_weak_notifier_exception.h" #include "php_weak.h" #include "ext/standard/info.h" @@ -29,6 +30,7 @@ static int le_weak; PHP_MINIT_FUNCTION(weak) /* {{{ */ { + PHP_MINIT(php_weak_notifier_exception)(INIT_FUNC_ARGS_PASSTHRU); PHP_MINIT(php_weak_reference)(INIT_FUNC_ARGS_PASSTHRU); return SUCCESS;