Skip to content

Commit c0b1bdc

Browse files
committed
Fixed bug #80929
The function name should be kept if Closure was created from the function which is marked as ZEND_ACC_CALL_VIA_TRAMPOLINE, because it is not a one-time thing and it may be called multiple times. Closes GH-6867.
1 parent 7c6cf09 commit c0b1bdc

File tree

3 files changed

+62
-1
lines changed

3 files changed

+62
-1
lines changed

NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@ PHP NEWS
22
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
33
?? ??? ????, PHP 7.4.19
44

5+
- Core:
6+
. Fixed bug #80929 (Method name corruption related to repeated calls to call_user_func_array).
7+
(twosee)
8+
59
- SPL:
610
. Fixed bug #80933 (SplFileObject::DROP_NEW_LINE is broken for NUL and CR).
711
(cmb, Nikita)

Zend/tests/closures/bug80929.phpt

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
--TEST--
2+
Bug #80929: Method name corruption related to zend_closure_call_magic
3+
--FILE--
4+
<?php
5+
class DefaultListener
6+
{
7+
public function handleDefaultEvent($event) { }
8+
}
9+
10+
class SubscriberProxy
11+
{
12+
private array $subscribedEvents;
13+
private object $subscriber;
14+
private Closure $listener;
15+
16+
public function __construct(array $subscribedEvents, object $subscriber)
17+
{
18+
$this->subscribedEvents = $subscribedEvents;
19+
$this->subscriber = $subscriber;
20+
foreach ($this->subscribedEvents as $eventName => $params) {
21+
$this->listener = Closure::fromCallable([$this, $params]);
22+
}
23+
}
24+
25+
public function __call(string $name, array $arguments)
26+
{
27+
return $this->subscriber->$name(...$arguments);
28+
}
29+
30+
public function dispatch($event, string $eventName)
31+
{
32+
($this->listener)($event, $eventName, null);
33+
}
34+
}
35+
36+
$proxy = new SubscriberProxy(
37+
['defaultEvent' => 'handleDefaultEvent'],
38+
new DefaultListener()
39+
);
40+
41+
for ($i = 0; $i < 10; $i++) {
42+
echo $i . PHP_EOL;
43+
$proxy->dispatch(null, 'defaultEvent');
44+
}
45+
?>
46+
--EXPECT--
47+
0
48+
1
49+
2
50+
3
51+
4
52+
5
53+
6
54+
7
55+
8
56+
9

Zend/zend_closures.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,6 @@ static ZEND_NAMED_FUNCTION(zend_closure_call_magic) /* {{{ */ {
270270

271271
zend_call_function(&fci, &fcc);
272272

273-
zval_ptr_dtor(&fci.params[0]);
274273
zval_ptr_dtor(&fci.params[1]);
275274
}
276275
/* }}} */
@@ -473,6 +472,8 @@ static void zend_closure_free_storage(zend_object *object) /* {{{ */
473472

474473
if (closure->func.type == ZEND_USER_FUNCTION) {
475474
destroy_op_array(&closure->func.op_array);
475+
} else if (closure->orig_internal_handler == zend_closure_call_magic) {
476+
zend_string_release(closure->func.common.function_name);
476477
}
477478

478479
if (Z_TYPE(closure->this_ptr) != IS_UNDEF) {

0 commit comments

Comments
 (0)