php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Request #80517 Retain parent method name of Closure in call stack
Submitted: 2020-12-15 02:55 UTC Modified: 2021-05-12 09:18 UTC
From: ttijhof at wikimedia dot org Assigned:
Status: Wont fix Package: Scripting Engine problem
PHP Version: Irrelevant OS: Debian stable
Private report: No CVE-ID: None
 [2020-12-15 02:55 UTC] ttijhof at wikimedia dot org
Description:
------------
When an anonymous function is encountered on the stack, it is currently not feasible (afaik) to obtain the name of the class/method or function where the closure was declared.

My use case is a sampling profiler observing EG(current_execute_data), in which I'd like to report something more useful and stable to the end-user than file path and line number.

This information would be useful to expose via debug_backtrace() as well, although maybe that should could be a separate ticket.


Downstream code: https://github.com/wikimedia/php-excimer/
Downstream task: https://phabricator.wikimedia.org/T231335
Downstream use case: Flame graphs, such as https://performance.wikimedia.org/php-profiling/


Test script:
---------------
class ResourceLoader {
 public function getHashes() {
  return array_map( function ( $module ) {
    return $this->getModule( $module )->getVersionHash();
  }, $moduleNames );
 }
}




Expected result:
----------------
Able to construct a string like "Closure$ResourceLoader::getHashes".


Before Wikimedia upgraded to PHP 7.2, we used HHVM and its Xenon extension, which provided "faux" function name for closures that were unique over a given code base and added signifant value for developers as part of stack traces.

Example:
https://github.com/facebook/hhvm/blob/HHVM-3.23.4/hphp/tools/gdb/README.md#stacktraces
https://github.com/facebook/hhvm/blob/HHVM-3.23.4/hphp/test/slow/closure/name.php.expect

Note that a method could define multiple closures, in which we could append a number to them. HHVM did this by appending "#2" as needed. XHProf does something similar for nested functions, by appending "@2" to the faux function name.

Actual result:
--------------
function_name:
class_name:
filename: /tmp/foo.php
closure_line: 3

For closures, we currently format this as {closure:/tmp/foo.php(3)}

https://github.com/wikimedia/php-excimer/blob/d82eaf7b3b/excimer_log.c#L311

Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2020-12-15 02:58 UTC] ttijhof at wikimedia dot org
The formatted outcome for this bug, may be of use in bug 62325 as the identifier or token for the Reflection API.

Mentions:
- https://bugs.php.net/bug.php?id=62325 Backtrace gives no handle to anonymous functions
 [2020-12-15 11:45 UTC] danack@php.net
imo, this would be useful for all closures, including those created through Closure::fromCallable().
 [2021-05-12 09:04 UTC] krakjoe@php.net
-Status: Open +Status: Wont fix
 [2021-05-12 09:04 UTC] krakjoe@php.net
This is outside of the scope of normal operation for PHP, we store no information about relationships between closures and the declaring op array, and we probably shouldn't.

One of the problems with this is avoiding collisions in naming, the internal solution used to be to append the opcode address to the end of the closure ... but this is obviously meaningless information that should not leak into userland, an implementation detail that today we are without, and we wouldn't want to introduce another detail like it.

So decide for yourself how you want to name closure, below is a demonstration of how to achieve that.

https://github.com/krakjoe/cloname

While I'm aware that there are other reports complaining about unidentifiable closures, I'm going to close this one because you've got a pretty good answer here for your particular use case.

If we do decide to append some information to a closures name, it is not likely to be enough for your use case whatever.
 [2021-05-12 09:18 UTC] krakjoe@php.net
Just to note, in 8.1, closures are stored as a member of the op array, dynamic func defs, however the information about relationships is encoded within the instructions of that op array - we still don't store that information in an accessible way, because for zend, it's not important ...
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Thu Nov 21 17:01:32 2024 UTC