Skip to content

Commit 08b2ab2

Browse files
authored
Include the source location in Closure names (#13550)
* Include the source location in Closure names This change makes stack traces involving Closures, especially multiple different Closures, much more useful, because it's more easily visible *which* closure was called for a given stack frame. The implementation is similar to that of anonymous classes which already include the file name and line number within their generated classname. * Update scripts/dev/bless_tests.php for closure naming * Adjust existing tests for closure naming * Adjust tests for closure naming that were not caught locally * Drop the namespace from closure names This is redundant with the included filename. * Include filename and line number as separate keys in Closure debug info * Fix test * Fix test * Include the surrounding class and function name in closure names * Fix test * Relax test expecations * Fix tests after merge * NEWS / UPGRADING
1 parent ab589e4 commit 08b2ab2

File tree

141 files changed

+945
-533
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

141 files changed

+945
-533
lines changed

NEWS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ PHP NEWS
1919
. Added sparc64 arch assembly support for zend fiber. (Claudio Jeker)
2020
. Fixed GH-13581 no space available for TLS on NetBSD. (Paul Ripke)
2121
. Added fiber Sys-V loongarch64 support. (qiangxuhui)
22+
. Adjusted closure names to include the parent function's name. (timwolla)
2223

2324
- Curl:
2425
. Deprecated the CURLOPT_BINARYTRANSFER constant. (divinity76)

UPGRADING

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,8 @@ PHP 8.4 UPGRADE NOTES
172172
RFC: https://wiki.php.net/rfc/rfc1867-non-post
173173
. Getting the debug info for WeakReference will now also output the object
174174
it references, or null if the reference is no longer valid.
175+
. The output of Closure::__debugInfo() now includes the name, file, and line
176+
of the Closure.
175177

176178
- Curl:
177179
. curl_version() returns an additional feature_list value, which is an
@@ -620,6 +622,10 @@ PHP 8.4 UPGRADE NOTES
620622
13. Other Changes
621623
========================================
622624

625+
* Closure names have been adjusted to include the parent function's name
626+
and the line of definition to make them easier to distinguish, for example
627+
within stack traces.
628+
623629
========================================
624630
14. Performance Improvements
625631
========================================

Zend/tests/arrow_functions/006.phpt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ try {
3232
--EXPECTF--
3333
int(2)
3434
int(10)
35-
{closure}(): Argument #1 ($x) must be of type int, string given, called in %s on line %d
35+
{closure:%s:%d}(): Argument #1 ($x) must be of type int, string given, called in %s on line %d
3636
array(3) {
3737
[0]=>
3838
int(20)
@@ -41,4 +41,4 @@ array(3) {
4141
[2]=>
4242
int(30)
4343
}
44-
{closure}(): Argument #2 must be of type ?int, string given, called in %s on line %d
44+
{closure:%s:%d}(): Argument #2 must be of type ?int, string given, called in %s on line %d

Zend/tests/bug26697.phpt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ spl_autoload_register(function ($name) {
1212
var_dump(class_exists('NotExistingClass'));
1313

1414
?>
15-
--EXPECT--
16-
{closure}(NotExistingClass)
15+
--EXPECTF--
16+
{closure:%s:%d}(NotExistingClass)
1717
bool(false)
18-
{closure}(NotExistingClass), done
18+
{closure:%s:%d}(NotExistingClass), done
1919
bool(false)

Zend/tests/bug31102.phpt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,11 @@ while($test++ < 5)
3737
?>
3838
===DONE===
3939
--EXPECTF--
40-
{closure}(Test1,1)
40+
{closure:%s:%d}(Test1,1)
4141
Caught: Test1::__construct
42-
{closure}(Test2,2)
43-
Caught: {closure}
44-
{closure}(Test3,3)
42+
{closure:%s:%d}(Test2,2)
43+
Caught: {closure:%s:%d}
44+
{closure:%s:%d}(Test3,3)
4545

4646
Fatal error: Uncaught Error: Class "Test3" not found in %s:%d
4747
Stack trace:

Zend/tests/bug49908.phpt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ string(3) "Bar"
2929

3030
Fatal error: Uncaught Exception: Bar in %s:%d
3131
Stack trace:
32-
#0 %s(%d): {closure}('Bar')
33-
#1 %s(%d): {closure}('Foo')
32+
#0 %s(%d): {closure:%s:%d}('Bar')
33+
#1 %s(%d): {closure:%s:%d}('Foo')
3434
#2 {main}
3535
thrown in %s on line %d

Zend/tests/bug52193.phpt

Lines changed: 49 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,43 +35,85 @@ array(0) {
3535
}
3636
array(1) {
3737
[0]=>
38-
object(Closure)#%d (0) {
38+
object(Closure)#%d (3) {
39+
["name"]=>
40+
string(%d) "{closure:%s:%d}"
41+
["file"]=>
42+
string(%d) "%s"
43+
["line"]=>
44+
int(%d)
3945
}
4046
}
4147
int(2)
4248
array(1) {
4349
[0]=>
44-
object(Closure)#%d (1) {
50+
object(Closure)#%d (4) {
51+
["name"]=>
52+
string(%d) "{closure:%s:%d}"
53+
["file"]=>
54+
string(%d) "%s"
55+
["line"]=>
56+
int(%d)
4557
["static"]=>
4658
array(1) {
4759
["h"]=>
4860
&array(1) {
4961
[0]=>
50-
object(Closure)#%d (0) {
62+
object(Closure)#%d (3) {
63+
["name"]=>
64+
string(%d) "{closure:%s:%d}"
65+
["file"]=>
66+
string(%d) "%s"
67+
["line"]=>
68+
int(%d)
5169
}
5270
}
5371
}
5472
}
5573
}
56-
object(Closure)#%d (1) {
74+
object(Closure)#%d (4) {
75+
["name"]=>
76+
string(%d) "{closure:%s:%d}"
77+
["file"]=>
78+
string(%d) "%s"
79+
["line"]=>
80+
int(%d)
5781
["static"]=>
5882
array(1) {
5983
["h"]=>
6084
&array(1) {
6185
[0]=>
62-
object(Closure)#%d (0) {
86+
object(Closure)#%d (3) {
87+
["name"]=>
88+
string(%d) "{closure:%s:%d}"
89+
["file"]=>
90+
string(%d) "%s"
91+
["line"]=>
92+
int(%d)
6393
}
6494
}
6595
}
6696
}
6797
array(1) {
6898
[0]=>
69-
object(Closure)#%d (0) {
99+
object(Closure)#%d (3) {
100+
["name"]=>
101+
string(%d) "{closure:%s:%d}"
102+
["file"]=>
103+
string(%d) "%s"
104+
["line"]=>
105+
int(%d)
70106
}
71107
}
72108
array(2) {
73109
[0]=>
74-
object(Closure)#%d (0) {
110+
object(Closure)#%d (3) {
111+
["name"]=>
112+
string(%d) "{closure:%s:%d}"
113+
["file"]=>
114+
string(%d) "%s"
115+
["line"]=>
116+
int(%d)
75117
}
76118
[1]=>
77119
int(5)

Zend/tests/bug60738.phpt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,13 @@ trigger_error('Error!');
1616
--EXPECTF--
1717
NULL
1818
Intercepted error!
19-
object(Closure)#1 (0) {
19+
object(Closure)#%d (3) {
20+
["name"]=>
21+
string(%d) "{closure:%s:%d}"
22+
["file"]=>
23+
string(%d) "%s"
24+
["line"]=>
25+
int(%d)
2026
}
2127

2228
Notice: Error! in %s on line %d

Zend/tests/bug60738_variation.phpt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,13 @@ throw new Exception('Exception!');
1313
?>
1414
--EXPECTF--
1515
NULL
16-
object(Closure)#1 (0) {
16+
object(Closure)#%d (3) {
17+
["name"]=>
18+
string(%d) "{closure:%s:%d}"
19+
["file"]=>
20+
string(%d) "%s"
21+
["line"]=>
22+
int(%d)
1723
}
1824

1925
Fatal error: Uncaught Exception: Exception! in %s:%d

Zend/tests/bug60909_1.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ require 'notfound.php';
1414
error(require(notfound.php): Failed to open stream: %s)
1515
Fatal error: Uncaught Exception: Foo in %sbug60909_1.php:5
1616
Stack trace:
17-
#0 %sbug60909_1.php(8): {closure}(2, 'require(notfoun...', '%s', 8)
17+
#0 %s(%d): {closure:%s:%d}(2, 'require(notfoun...', '%s', 8)
1818
#1 %sbug60909_1.php(8): require()
1919
#2 {main}
2020
thrown in %sbug60909_1.php on line 5

Zend/tests/bug61273.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,5 @@ call_user_func_array(function(&$a) {}, $args);
1212
echo strval("okey");
1313
?>
1414
--EXPECTF--
15-
Warning: {closure}(): Argument #1 ($a) must be passed by reference, value given in %s on line %d
15+
Warning: {closure:%s:%d}(): Argument #1 ($a) must be passed by reference, value given in %s on line %d
1616
okey

Zend/tests/bug61767.phpt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,18 @@ Error handler called (Undefined variable $undefined)
2020

2121
Fatal error: Uncaught ErrorException: Undefined variable $undefined in %sbug61767.php:%d
2222
Stack trace:
23-
#0 %sbug61767.php(%d): {closure}(%s, 'Undefined varia...', '%s', %d)
23+
#0 %s(%d): {closure:%s:%d}(2, 'Undefined varia...', '%s', 13)
2424
#1 {main}
2525
thrown in %sbug61767.php on line %d
2626
Shutting down
2727
Array
2828
(
2929
[type] => 1
30-
[message] => %a
30+
[message] => Uncaught ErrorException: Undefined variable $undefined in %s:%d
31+
Stack trace:
32+
#0 %s(%d): {closure:%s:%d}(2, 'Undefined varia...', '%s', 13)
33+
#1 {main}
34+
thrown
3135
[file] => %sbug61767.php
3236
[line] => %d
3337
)

Zend/tests/bug64960.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ Deprecated: Creation of dynamic property Exception::$_trace is deprecated in %s
3737

3838
Fatal error: Uncaught Exception in %sbug64960.php:19
3939
Stack trace:
40-
#0 [internal function]: {closure}(8, 'ob_end_clean():...', '%s', 9)
40+
#0 [internal function]: {closure:%s:%d}(8, 'ob_end_clean():...', '%s', 9)
4141
#1 %sbug64960.php(9): ob_end_clean()
4242
#2 [internal function]: ExceptionHandler->__invoke(Object(Exception))
4343
#3 {main}

Zend/tests/bug67856.phpt

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,15 @@ var_dump(array_reduce($array, function(&$a, &$b) {
88
}, 0));
99
?>
1010
--EXPECTF--
11-
Warning: {closure}(): Argument #1 ($a) must be passed by reference, value given in %s on line %d
11+
Warning: {closure:%s:%d}(): Argument #1 ($a) must be passed by reference, value given in %s on line %d
1212

13-
Warning: {closure}(): Argument #2 ($b) must be passed by reference, value given in %s on line %d
13+
Warning: {closure:%s:%d}(): Argument #2 ($b) must be passed by reference, value given in %s on line %d
1414

15-
Warning: {closure}(): Argument #1 ($a) must be passed by reference, value given in %s on line %d
15+
Warning: {closure:%s:%d}(): Argument #1 ($a) must be passed by reference, value given in %s on line %d
1616

17-
Warning: {closure}(): Argument #2 ($b) must be passed by reference, value given in %s on line %d
17+
Warning: {closure:%s:%d}(): Argument #2 ($b) must be passed by reference, value given in %s on line %d
1818

19-
Warning: {closure}(): Argument #1 ($a) must be passed by reference, value given in %s on line %d
19+
Warning: {closure:%s:%d}(): Argument #1 ($a) must be passed by reference, value given in %s on line %d
2020

21-
Warning: {closure}(): Argument #2 ($b) must be passed by reference, value given in %s on line %d
21+
Warning: {closure:%s:%d}(): Argument #2 ($b) must be passed by reference, value given in %s on line %d
2222
int(6)

Zend/tests/bug70321.phpt

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,23 @@ var_dump($foo->bar->onBaz);
3939
--EXPECTF--
4040
array(1) {
4141
[0]=>
42-
object(Closure)#%d (0) {
42+
object(Closure)#%d (3) {
43+
["name"]=>
44+
string(%d) "{closure:%s:%d}"
45+
["file"]=>
46+
string(%d) "%s"
47+
["line"]=>
48+
int(%d)
4349
}
4450
}
4551
array(1) {
4652
[0]=>
47-
object(Closure)#%d (0) {
53+
object(Closure)#%d (3) {
54+
["name"]=>
55+
string(%d) "{closure:%s:%d}"
56+
["file"]=>
57+
string(%d) "%s"
58+
["line"]=>
59+
int(%d)
4860
}
4961
}

Zend/tests/bug72101.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ $foo->bar($a, $b, $c);
7878
--EXPECTF--
7979
Fatal error: Uncaught Error: Class "DoesNotExists" not found in %s:%d
8080
Stack trace:
81-
#0 %sbug72101.php(%d): {closure}(2, 'MethodCallbackB...', '%s', 8)
81+
#0 %s(%d): {closure:%s:%d}(2, 'MethodCallbackB...', '%s', 8)
8282
#1 %sbug72101.php(%d): PHPUnit_Framework_MockObject_Stub_ReturnCallback->invoke(Object(PHPUnit_Framework_MockObject_Invocation_Static))
8383
#2 %sbug72101.php(%d): PHPUnit_Framework_MockObject_Matcher->invoked(Object(PHPUnit_Framework_MockObject_Invocation_Static))
8484
#3 %sbug72101.php(%d): PHPUnit_Framework_MockObject_InvocationMocker->invoke(Object(PHPUnit_Framework_MockObject_Invocation_Static))

Zend/tests/bug74164.phpt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@ set_error_handler(function ($type, $msg) {
1212
call_user_func(function (array &$ref) {var_dump("xxx");}, 'not_an_array_variable');
1313
?>
1414
--EXPECTF--
15-
Fatal error: Uncaught Exception: Foo\{closure}(): Argument #1 ($ref) must be passed by reference, value given in %s:%d
15+
Fatal error: Uncaught Exception: {closure:%s:%d}(): Argument #1 ($ref) must be passed by reference, value given in %s:%d
1616
Stack trace:
17-
#0 [internal function]: Foo\{closure}(%s)
17+
#0 [internal function]: {closure:%s:%d}(2, '%s', '%s', 9)
1818
#1 %sbug74164.php(%d): call_user_func(%s)
1919
#2 {main}
2020
thrown in %sbug74164.php on line %d

Zend/tests/bug75079_2.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,6 @@ int(123)
3131

3232
Fatal error: Uncaught Error: Cannot access private property Foo::$bar in %s:%d
3333
Stack trace:
34-
#0 %s(%d): A->{closure}()
34+
#0 %s(%d): A->{closure:%s:%d}()
3535
#1 {main}
3636
thrown in %s on line %d

Zend/tests/bug75290.phpt

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ var_dump((new ReflectionFunction('sin'))->getClosure());
88
var_dump(function ($someThing) {});
99

1010
?>
11-
--EXPECT--
12-
object(Closure)#2 (2) {
11+
--EXPECTF--
12+
object(Closure)#%d (2) {
1313
["function"]=>
1414
string(3) "sin"
1515
["parameter"]=>
@@ -18,7 +18,13 @@ object(Closure)#2 (2) {
1818
string(10) "<required>"
1919
}
2020
}
21-
object(Closure)#2 (1) {
21+
object(Closure)#%d (4) {
22+
["name"]=>
23+
string(%d) "{closure:%s:%d}"
24+
["file"]=>
25+
string(%d) "%s"
26+
["line"]=>
27+
int(%d)
2228
["parameter"]=>
2329
array(1) {
2430
["$someThing"]=>

Zend/tests/bug76534.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,6 @@ $y = &$x["2bar"];
1212
--EXPECTF--
1313
Fatal error: Uncaught Exception: Illegal string offset "2bar" in %s:%d
1414
Stack trace:
15-
#0 %sbug76534.php(%d): {closure}(2, 'Illegal string ...', '%s', %d)
15+
#0 %s(%d): {closure:%s:%d}(2, 'Illegal string ...', '%s', 7)
1616
#1 {main}
1717
thrown in %sbug76534.php on line %d

Zend/tests/bug79657.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ get(...loop());
3636
Fatal error: Uncaught Exception in %s:%d
3737
Stack trace:
3838
#0 %s(%d): throwException()
39-
#1 %s(%d): {closure}()
39+
#1 %s(%d): {closure:%s:%d}()
4040
#2 %s(%d): loop()
4141
#3 {main}
4242
thrown in %s on line %d

0 commit comments

Comments
 (0)