[php-src] PR #24244: Fix array_map() optimization not resolving the class name of a static FCC
| From: | claudiodekker | Date: | Sat, 10 Oct 2026 16:55:38 +0000 |
| Subject: | [php-src] PR #24244: Fix array_map() optimization not resolving the class name of a static FCC | ||
| Groups: | php.git-pulls | ||
| Request: | Send a blank email to git-pulls+get-39319@lists.php.net to get a copy of this message | ||
Pull Request: https://github.com/php/php-src/pull/24244
Author: claudiodekker
When
array_map() is compiled into a loop, the class name of a static first-class
callable is compiled as an expression. That gives the name as written, without resolving it against
the current namespace or use imports, so the call fails with "Class not
found" at runtime:
```php
<?php
namespace Foo {
class Bar {
public static function double(int $x): int { return $x * 2; }
}
}
namespace {
use Foo\Bar;
var_dump(array_map(Bar::double(...), [1]));
// PHP 8.5: array(1) { [0]=> int(2) }
// PHP 8.6.0RC3: Uncaught Error: Class "Bar" not found
}
```
Only fully qualified names work. Inside a namespace the bug shows only with
\array_map(), because an unqualified array_map() there isn't
optimized. self, parent and static are not affected.
This was introduced in 40a8468559 (GH-23254), which started evaluating the class name once before
the loop. Bisected between RC3 and the point where PHP-8.6 branched from PHP-8.5.
The fix resolves a literal class name with zend_resolve_class_name_ast() before the
loop body is compiled, the same way zend_compile_class_ref() does for a regular static
call. self, parent, static and dynamic class expressions
still take the existing path.
The new test covers imported, aliased, namespace\-relative and fully qualified names in
the global namespace, a namespace-relative name under \array_map(), and
self, parent and static inside a class.
Tested with make test TESTS="Zend/tests ext/opcache/tests
ext/standard/tests/array" on --enable-debug and --enable-debug
--enable-zts builds, with opcache off and on. The new test fails without the fix and passes
with it.