Skip to content

Fix array_map() optimization not resolving the function name of an FCC - #25286

Merged
arnaud-lb merged 1 commit into
php:PHP-8.6from
ChrisAbner:fix-array-map-fcc-function-name
Nov 9, 2026
Merged

arnaud-lb merged 1 commit into
php:PHP-8.6from
ChrisAbner:fix-array-map-fcc-function-name

Conversation

@ChrisAbner

Copy link
Copy Markdown
Contributor

In a namespace, or with a use function import, array_map() with a first-class callable called the wrong function. The foreach optimization (added in #20934, extended in #23254) compiles the function name as an expression before the loop. That yields the name as written, with no namespace resolution. The loop then calls it as a global function, ignoring the current namespace, the use function imports and the fallback to the global function. In namespace App with App\strtoupper defined, \array_map(strtoupper(...), ['a']) returned A from the internal function instead of calling App\strtoupper. With use function Lib\fmt, array_map(fmt(...), ...) failed with "undefined function fmt()".

For a literal function name in a ZEND_AST_CALL, this keeps the name AST and lets zend_compile_call() resolve it in the loop body, the same way a normal call is resolved, with the namespace fallback. Non-literal names keep the pre-loop evaluation, because they can have side effects and must run once. This mirrors #24244, which did the same for the class name of a static FCC. Resolving the name eagerly with zend_resolve_function_name() was the other option, but it does not express the runtime App\f then \f fallback, so I kept the lazy resolution in the call.

Introduced by 40a8468 (#23254) in 8.6.0RC3. Confirmed by bisect: the parent 31db39c returns the correct value, 40a8468 returns the wrong one. It affects 8.6 (RC3 to RC5) and master, not 8.5 or 8.4.

Verification

Case (in namespace App) Before After
\array_map(strtoupper(...), ['a']) with App\strtoupper A APP:a
\array_map(label(...), [1]) with App\label global label App\label
use function Lib\fmt; array_map(fmt(...), [1]) undefined function Lib\fmt
\array_map(namespace\label(...), [4]), \label(...), PFA pad(?, '!') already correct still correct

The new test Zend/tests/array_map_foreach_optimization_function_name.phpt passes, and the 14 existing array_map_foreach_optimization_* tests still pass, without OPcache, with OPcache and with the tracing JIT. On PHP-8.6, Zend/tests, ext/opcache/tests and ext/standard/tests/array run with 0 failures (7360 tests), without OPcache and with it. On master, Zend/tests and ext/opcache/tests run with 0 failures (6572 tests).

Not covered: non-literal function names are unchanged. Static method FCCs already resolved correctly (the class name since #24244, the method name is a plain literal). I found this with a deterministic differential testing tool I am developing.

@TimWolla
TimWolla requested a review from arnaud-lb November 8, 2026 15:16
@arnaud-lb
arnaud-lb merged commit 365d450 into php:PHP-8.6 Nov 9, 2026
18 checks passed
arnaud-lb added a commit that referenced this pull request Nov 9, 2026
* PHP-8.6:
  [ci skip] NEWS
  Fix array_map() optimization not resolving the function name of an FCC (#25286)
@arnaud-lb

Copy link
Copy Markdown
Member

Thank you!

DanielEScherzer pushed a commit that referenced this pull request Nov 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants