Skip to content
This repository was archived by the owner on Jul 7, 2018. It is now read-only.

Commit f39ccbb

Browse files
authored
Merge pull request #12 from pinepain/fix_notifiers_calling
Fix notifiers calling behavior
2 parents 17f5094 + 913e537 commit f39ccbb

21 files changed

+455
-469
lines changed

README.md

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,15 @@ $obj = null; // outputs "Object destroyed"
2727

2828
This extension adds `Weak` namespace and all entities are created inside it.
2929

30-
There are no INI setting, constants or exceptions provided by this extension.
30+
There are no INI setting or constants provided by this extension.
3131

3232
Brief docs about [`Weak\Reference` class](./stubs/weak/Reference.php) and [functions](./stubs/weak/functions.php)
3333
may be seen in [stub files](./stubs/weak).
3434

3535
Short list if what provided by this extension is:
3636

3737
- `class Weak\Reference`
38+
- `class Weak\NotifierException extend Exception`
3839
- `function Weak\refcounted()`
3940
- `function Weak\refcount()`
4041
- `function Weak\weakrefcounted()`
@@ -51,8 +52,9 @@ Note that notification happens *after* referent object destruction, so at the ti
5152
will return `null` (unless rare case when object refcount get incremented in destructor, e.g. by storing destructing value
5253
somewhere else).
5354

54-
Callback notifier will not be called if referent object destructor or previous notifier callback throws exception, whether
55-
array notifier get executed even in such cases.
55+
If object destructor or one or more notifiers throw exception, all further notifier callbacks will be called as if
56+
that exception was thrown inside `try-catch` block. In case one or more exception was thrown, `Weak\NotifierException`
57+
will be thrown and all thrown exceptions will be available via `Weak\NotifierException::getExceptions()` method.
5658

5759

5860
### Cloning
@@ -157,16 +159,28 @@ You may also want to add php-weak extension as a [composer.json dependency](http
157159
with custom one, which meta-code is:
158160

159161
```php
160-
run_original_dtor_obj($object);
162+
$exceptions = [];
163+
164+
try {
165+
run_original_dtor_obj($object);
166+
} catch(Throwable $e) {
167+
$exceptions[] = $e;
168+
}
161169

162170
foreach($weak_references as $weak_ref_object_handle => $weak_reference) {
163171
if (is_array($weak_reference->notifier)) {
164172
$weak_reference->notifier[] = $weak_reference;
165-
} elseif (is_callable($weak_reference->notifier) && $no_exception_thrown) {
166-
$weak_reference->notifier($weak_reference);
173+
} elseif (is_callable($weak_reference->notifier)) {
174+
try {
175+
$weak_reference->notifier($weak_reference);
176+
} catch(Throwable $e) {
177+
$exceptions[] = $e;
178+
}
167179
}
168-
169-
unset($weak_references[$weak_ref_object_handle]);
180+
}
181+
182+
if ($exceptions) {
183+
throw new Weak\NotifierException('One or more exceptions thrown during notifiers calling', $exceptions);
170184
}
171185
```
172186

config.m4

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,10 @@ if test "$PHP_WEAK" != "no"; then
2727
fi
2828
fi
2929

30-
PHP_NEW_EXTENSION(weak, [ \
31-
weak.c \
32-
php_weak_reference.c \
33-
php_weak_functions.c \
30+
PHP_NEW_EXTENSION(weak, [ \
31+
weak.c \
32+
php_weak_notifier_exception.c \
33+
php_weak_reference.c \
34+
php_weak_functions.c \
3435
], $ext_shared,, -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1)
3536
fi

config.w32

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,5 @@
44
ARG_ENABLE("weak", "enable weak support", "no");
55

66
if (PHP_WEAK != "no") {
7-
EXTENSION("weak", "weak.c php_weak_reference.c php_weak_functions.c", PHP_WEAK_SHARED, "/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1");
7+
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");
88
}

php_weak_notifier_exception.c

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
/*
2+
+----------------------------------------------------------------------+
3+
| This file is part of the pinepain/php-weak PHP extension. |
4+
| |
5+
| Copyright (c) 2016 Bogdan Padalko <zaq178miami@gmail.com> |
6+
| |
7+
| Licensed under the MIT license: http://opensource.org/licenses/MIT |
8+
| |
9+
| For the full copyright and license information, please view the |
10+
| LICENSE file that was distributed with this source or visit |
11+
| http://opensource.org/licenses/MIT |
12+
+----------------------------------------------------------------------+
13+
*/
14+
15+
#include "php_weak_notifier_exception.h"
16+
#include "php_weak.h"
17+
#include "zend_exceptions.h"
18+
19+
20+
zend_class_entry *php_weak_notifier_exception_class_entry;
21+
#define this_ce php_weak_notifier_exception_class_entry
22+
23+
24+
static zend_object *php_weak_notifier_exception_ctor(zend_class_entry *ce) /* {{{ */
25+
{
26+
zval obj, thrown;
27+
zend_object *object;
28+
29+
Z_OBJ(obj) = object = ce->parent->create_object(ce);
30+
31+
array_init_size(&thrown, 0);
32+
zend_update_property(php_weak_notifier_exception_class_entry, &obj, ZEND_STRL("exceptions"), &thrown);
33+
34+
return object;
35+
} /* }}} */
36+
37+
38+
void php_weak_create_notifier_exception(zval *exception, const char *message, zval *thrown) /* {{{ */
39+
{
40+
object_init_ex(exception, this_ce);
41+
zend_update_property_string(zend_ce_exception, exception, ZEND_STRL("message"), message);
42+
zend_update_property(php_weak_notifier_exception_class_entry, exception, ZEND_STRL("exceptions"), thrown);
43+
} /* }}} */
44+
45+
static PHP_METHOD(NotifierException, __construct) /* {{{ */
46+
{
47+
zend_string *message = NULL;
48+
zend_long code = 0;
49+
50+
zval tmp;
51+
zval *exceptions = NULL;
52+
zval *previous = NULL;
53+
54+
if (zend_parse_parameters(ZEND_NUM_ARGS(), "|SalO!", &message, &exceptions, &code, &previous, zend_ce_throwable) == FAILURE) {
55+
return;
56+
}
57+
58+
if (message) {
59+
zend_update_property_str(zend_ce_exception, getThis(), ZEND_STRL("message"), message);
60+
}
61+
62+
if (exceptions) {
63+
zend_update_property(this_ce, getThis(), ZEND_STRL("exceptions"), exceptions);
64+
} else {
65+
array_init_size(&tmp, 0);
66+
zend_update_property(this_ce, getThis(), ZEND_STRL("exceptions"), &tmp);
67+
}
68+
69+
if (code) {
70+
zend_update_property_long(zend_ce_exception, getThis(), ZEND_STRL("code"), code);
71+
}
72+
73+
if (previous) {
74+
zend_update_property(zend_ce_exception, getThis(), ZEND_STRL("previous"), previous);
75+
}
76+
}
77+
78+
static PHP_METHOD(NotifierException, getExceptions) /* {{{ */
79+
{
80+
zval rv;
81+
82+
if (zend_parse_parameters_none() == FAILURE) {
83+
return;
84+
}
85+
86+
RETVAL_ZVAL(zend_read_property(php_weak_notifier_exception_class_entry, getThis(), ZEND_STRL("exceptions"), 0, &rv), 1, 0);
87+
} /* }}} */
88+
89+
90+
ZEND_BEGIN_ARG_INFO_EX(arginfo_notifier_exception___construct, ZEND_SEND_BY_VAL, ZEND_RETURN_VALUE, 0)
91+
ZEND_ARG_INFO(0, message)
92+
ZEND_ARG_INFO(0, exceptions)
93+
ZEND_ARG_INFO(0, code)
94+
ZEND_ARG_INFO(0, previous)
95+
ZEND_END_ARG_INFO()
96+
97+
ZEND_BEGIN_ARG_INFO_EX(arginfo_notifier_exception_getExceptions, ZEND_SEND_BY_VAL, ZEND_RETURN_VALUE, 0)
98+
ZEND_END_ARG_INFO()
99+
100+
101+
static const zend_function_entry php_weak_notifier_exception_methods[] = { /* {{{ */
102+
PHP_ME(NotifierException, __construct, arginfo_notifier_exception___construct, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR)
103+
PHP_ME(NotifierException, getExceptions, arginfo_notifier_exception_getExceptions, ZEND_ACC_PUBLIC)
104+
105+
PHP_FE_END
106+
}; /* }}} */
107+
108+
109+
PHP_MINIT_FUNCTION (php_weak_notifier_exception) /* {{{ */
110+
{
111+
zend_class_entry ce;
112+
113+
INIT_NS_CLASS_ENTRY(ce, PHP_WEAK_NS, "NotifierException", php_weak_notifier_exception_methods);
114+
this_ce = zend_register_internal_class_ex(&ce, zend_ce_exception);
115+
/*this_ce->create_object = php_weak_notifier_exception_ctor;*/
116+
117+
zend_declare_property_null(this_ce, ZEND_STRL("exceptions"), ZEND_ACC_PRIVATE);
118+
119+
120+
return SUCCESS;
121+
} /* }}} */
122+
123+
124+
/*
125+
* Local variables:
126+
* tab-width: 4
127+
* c-basic-offset: 4
128+
* End:
129+
* vim600: noet sw=4 ts=4 fdm=marker
130+
* vim<600: noet sw=4 ts=4
131+
*/

php_weak_notifier_exception.h

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
+----------------------------------------------------------------------+
3+
| This file is part of the pinepain/php-weak PHP extension. |
4+
| |
5+
| Copyright (c) 2016 Bogdan Padalko <zaq178miami@gmail.com> |
6+
| |
7+
| Licensed under the MIT license: http://opensource.org/licenses/MIT |
8+
| |
9+
| For the full copyright and license information, please view the |
10+
| LICENSE file that was distributed with this source or visit |
11+
| http://opensource.org/licenses/MIT |
12+
+----------------------------------------------------------------------+
13+
*/
14+
15+
#ifndef PHP_WEAK_NOTIFIER_EXCEPTION_H
16+
#define PHP_WEAK_NOTIFIER_EXCEPTION_H
17+
18+
#include "php.h"
19+
20+
#ifdef ZTS
21+
#include "TSRM.h"
22+
#endif
23+
24+
extern zend_class_entry *php_weak_notifier_exception_class_entry;
25+
26+
void php_weak_create_notifier_exception(zval *exception, const char *message, zval *thrown);
27+
28+
PHP_MINIT_FUNCTION(php_weak_notifier_exception);
29+
30+
31+
#endif /* PHP_WEAK_NOTIFIER_EXCEPTION_H */
32+
33+
34+
/*
35+
* Local variables:
36+
* tab-width: 4
37+
* c-basic-offset: 4
38+
* End:
39+
* vim600: noet sw=4 ts=4 fdm=marker
40+
* vim<600: noet sw=4 ts=4
41+
*/

php_weak_reference.c

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
*/
1414

1515
#include "php_weak_reference.h"
16+
#include "php_weak_notifier_exception.h"
1617
#include "php_weak.h"
1718
#include "zend_exceptions.h"
1819
#include "zend_interfaces.h"
@@ -32,6 +33,7 @@ static zend_object_get_debug_info_t spl_object_storage_get_debug_info_orig_handl
3233

3334
php_weak_reference_t *php_weak_reference_init(zval *this_ptr, zval *referent_zv, zval *notifier_zv);
3435

36+
static inline void php_weak_store_exceptions(zval *exceptions, zval *tmp);
3537
static int php_weak_reference_check_notifier(zval *notifier, zval *this);
3638

3739

@@ -197,17 +199,29 @@ void php_weak_reference_call_notifier(zval *reference, zval *notifier) /* {{{ */
197199

198200
} /* }}} */
199201

202+
200203
void php_weak_referent_object_dtor_obj(zend_object *object) /* {{{ */
201204
{
202205
php_weak_referent_t *referent = php_weak_referent_find_ptr(object->handle);
203206

207+
zval exceptions;
208+
zval tmp;
209+
210+
ZVAL_UNDEF(&exceptions);
211+
204212
assert(NULL != referent);
205213
assert(NULL != PHP_WEAK_G(referents));
206214

207215
zend_ulong handle;
208216
php_weak_reference_t *reference;
209217

210-
referent->original_handlers->dtor_obj(object);
218+
if (referent->original_handlers->dtor_obj) {
219+
referent->original_handlers->dtor_obj(object);
220+
221+
if (EG(exception)) {
222+
php_weak_store_exceptions(&exceptions, &tmp);
223+
}
224+
}
211225

212226
ZEND_HASH_REVERSE_FOREACH_PTR(&referent->weak_references, reference) {
213227
handle = _p->h;
@@ -221,11 +235,12 @@ void php_weak_referent_object_dtor_obj(zend_object *object) /* {{{ */
221235
break;
222236
case PHP_WEAK_NOTIFIER_CALLBACK:
223237
/* callback notifier */
224-
if (!EG(exception)) {
225-
php_weak_reference_call_notifier(&reference->this_ptr, &reference->notifier);
238+
php_weak_reference_call_notifier(&reference->this_ptr, &reference->notifier);
239+
240+
if (EG(exception)) {
241+
php_weak_store_exceptions(&exceptions, &tmp);
226242
}
227243
break;
228-
229244
default:
230245
break;
231246
}
@@ -234,6 +249,13 @@ void php_weak_referent_object_dtor_obj(zend_object *object) /* {{{ */
234249
} ZEND_HASH_FOREACH_END();
235250

236251
zend_hash_index_del(PHP_WEAK_G(referents), referent->handle);
252+
253+
if (!Z_ISUNDEF(exceptions)) {
254+
zval exception;
255+
php_weak_create_notifier_exception(&exception, "One or more exceptions thrown during notifiers calling", &exceptions);
256+
257+
zend_throw_exception_object(&exception);
258+
}
237259
} /* }}} */
238260

239261
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,
340362
return reference;
341363
} /* }}} */
342364

365+
static inline void php_weak_store_exceptions(zval *exceptions, zval *tmp)
366+
{
367+
if (Z_ISUNDEF_P(exceptions)) {
368+
array_init(exceptions);
369+
}
370+
371+
ZVAL_OBJ(tmp, EG(exception));
372+
Z_ADDREF_P(tmp);
373+
add_next_index_zval(exceptions, tmp);
374+
375+
zend_clear_exception();
376+
}
343377

344378
static int php_weak_reference_check_notifier(zval *notifier, zval *this) /* {{{ */
345379
{
@@ -628,10 +662,10 @@ PHP_MINIT_FUNCTION (php_weak_reference) /* {{{ */
628662
zend_class_entry ce;
629663

630664
INIT_NS_CLASS_ENTRY(ce, PHP_WEAK_NS, "Reference", php_weak_reference_methods);
631-
ce.serialize = zend_class_serialize_deny;
632-
ce.unserialize = zend_class_unserialize_deny;
633665
this_ce = zend_register_internal_class(&ce);
634666
this_ce->create_object = php_weak_reference_ctor;
667+
this_ce->serialize = zend_class_serialize_deny;
668+
this_ce->unserialize = zend_class_unserialize_deny;
635669

636670
memcpy(&php_weak_reference_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
637671

stubs/weak/NotifierException.php

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
3+
4+
namespace Weak;
5+
6+
7+
use Exception;
8+
9+
10+
class NotifierException extends Exception
11+
{
12+
private $exceptions = [];
13+
14+
/**
15+
* Get exceptions thrown from notifiers
16+
*
17+
* @return array
18+
*/
19+
public function getExceptions() : array
20+
{
21+
return $this->exceptions;
22+
}
23+
}

0 commit comments

Comments
 (0)