Skip to content

Commit 687b7c1

Browse files
committed
Ensure end handlers fire only once for fatals
1 parent 2612448 commit 687b7c1

File tree

4 files changed

+59
-0
lines changed

4 files changed

+59
-0
lines changed

Zend/zend.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1329,6 +1329,11 @@ static ZEND_COLD void zend_error_impl(
13291329
do {
13301330
if (ex->func->type != ZEND_INTERNAL_FUNCTION) {
13311331
zend_observer_fcall_end(ex, NULL);
1332+
/* If an extension catches a fatal error and raises another one, the
1333+
* observer end handlers will fire more than once. This requires us
1334+
* to "unobserve" the functions after the end handlers have fired the
1335+
* first time. */
1336+
zend_observer_fcall_unobserve(ex->func);
13321337
}
13331338
} while ((ex = ex->prev_execute_data) != NULL);
13341339
}

Zend/zend_observer.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,15 @@ ZEND_API void ZEND_FASTCALL zend_observer_fcall_end(
210210
}
211211
}
212212

213+
ZEND_API void zend_observer_fcall_unobserve(zend_function *func)
214+
{
215+
if (ZEND_OBSERVER_ENABLED
216+
&& ZEND_OBSERVABLE_FN(func->common.fn_flags)
217+
&& ZEND_OBSERVER_DATA(&func->op_array)) {
218+
ZEND_OBSERVER_DATA(&func->op_array) = ZEND_OBSERVER_NOT_OBSERVED;
219+
}
220+
}
221+
213222
ZEND_API void zend_observer_error_register(zend_observer_error_cb cb)
214223
{
215224
zend_llist_add_element(&zend_observer_error_callbacks, &cb);

Zend/zend_observer.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ ZEND_API void ZEND_FASTCALL zend_observer_fcall_end(
7070
zend_execute_data *execute_data,
7171
zval *return_value);
7272

73+
ZEND_API void zend_observer_fcall_unobserve(zend_function *func);
74+
7375
typedef void (*zend_observer_error_cb)(int type, const char *error_filename, uint32_t error_lineno, zend_string *message);
7476

7577
ZEND_API void zend_observer_error_register(zend_observer_error_cb callback);
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
--TEST--
2+
Observer: fatal errors caught with zend_try will fire end handlers once
3+
--SKIPIF--
4+
<?php if (!extension_loaded('zend-test')) die('skip: zend-test extension required'); ?>
5+
<?php if (!extension_loaded('soap')) die('skip: soap extension required'); ?>
6+
--INI--
7+
zend_test.observer.enabled=1
8+
zend_test.observer.observe_all=1
9+
zend_test.observer.show_return_value=1
10+
--FILE--
11+
<?php
12+
function foo()
13+
{
14+
try {
15+
// ext/soap catches a fatal error and then throws an exception
16+
$client = new SoapClient('foo');
17+
} catch (SoapFault $e) {
18+
echo $e->getMessage() . PHP_EOL;
19+
}
20+
}
21+
22+
function main()
23+
{
24+
foo();
25+
}
26+
27+
main();
28+
29+
echo 'Done.' . PHP_EOL;
30+
?>
31+
--EXPECTF--
32+
<!-- init '%s/observer_error_%d.php' -->
33+
<file '%s/observer_error_%d.php'>
34+
<!-- init main() -->
35+
<main>
36+
<!-- init foo() -->
37+
<foo>
38+
</foo:NULL>
39+
</main:NULL>
40+
</file '%s/observer_error_%d.php'>
41+
SOAP-ERROR: Parsing WSDL: Couldn't load from 'foo' : failed to load external entity "foo"
42+
43+
Done.

0 commit comments

Comments
 (0)