Category
Language · php-src-strict / first-class callable + late static binding
Problem
An instance-method first-class callable $obj->m(...) in a compile unit poisons later static resolution in that same unit. Subsequent static::class and : static return types resolve to the FCC's class (FM) instead of the class that declared them.
Isolated static::class / : static (no preceding FCC) already match Zend. Distinct from closed #27835 (LSB inside an FCC) and #24431 (Closure::fromCallable LSB).
Probed 2026-08-18 @ ad82044529 — Zend 8.2.32 vs php bin/vm.php / php bin/jit.php.
| Repro |
Zend 8.2.32 |
VM/JIT |
FCC $c = (new FM())->m(...); $c(4) |
8 |
8 ✓ |
Base::who() / Child::who() after that FCC (static::class) |
Base Child |
FM FM |
(new B3())->f() with function f(): static after that FCC |
B3 |
TypeError: B3::f(): Return value must be of type FM, B3 returned |
php-src reference
PHP implementation target
lib/Compiler.php — first-class callable lowering (ZEND_AST_CALLABLE_CONVERT); called-scope / current-class for later static::class and : static return types
lib/VM.php — runtime static::class / return-type check must use the method's class, not the last FCC class
- No new
runtime/*.c
Repro
./script/docker-exec.sh -- bash -lc 'php bin/vm.php test/repro/maintainer_gap_fcc_poisons_static.php'
./script/docker-exec.sh -- bash -lc 'php bin/jit.php test/repro/maintainer_gap_fcc_poisons_static.php'
./script/docker-exec.sh -- bash -lc 'php test/repro/maintainer_gap_fcc_poisons_static.php' # Zend baseline
<?php
error_reporting(E_ALL);
class FM {
public function m($x) { return $x * 2; }
}
$c = (new FM())->m(...);
echo 'fcc=', $c(4), "\n";
class Base {
public static function who() { return static::class; }
}
class Child extends Base {}
echo 'lsb=', Base::who(), ' ', Child::who(), "\n";
class A3 {
public function f(): object { return new stdClass(); }
}
class B3 extends A3 {
public function f(): static { return $this; }
}
echo 'ret=', get_class((new B3())->f()), "\n";
Done when
Category
Language· php-src-strict / first-class callable + late static bindingProblem
An instance-method first-class callable
$obj->m(...)in a compile unit poisons laterstaticresolution in that same unit. Subsequentstatic::classand: staticreturn types resolve to the FCC's class (FM) instead of the class that declared them.Isolated
static::class/: static(no preceding FCC) already match Zend. Distinct from closed #27835 (LSB inside an FCC) and #24431 (Closure::fromCallableLSB).Probed 2026-08-18 @
ad82044529— Zend 8.2.32 vsphp bin/vm.php/php bin/jit.php.$c = (new FM())->m(...); $c(4)88✓Base::who()/Child::who()after that FCC (static::class)Base ChildFM FM(new B3())->f()withfunction f(): staticafter that FCCB3TypeError: B3::f(): Return value must be of type FM, B3 returnedphp-src reference
Zend/zend_compile.c—ZEND_AST_CALLABLE_CONVERT($obj->method(...)) must not change the compile-unit called-scope for later classesZend/zend_closures.c— FCC /zend_create_fake_closurebinds the callee class, not laterstatic::Zend/zend_execute.c—static::class/: staticuse the declaring/called classPHP implementation target
lib/Compiler.php— first-class callable lowering (ZEND_AST_CALLABLE_CONVERT); called-scope / current-class for laterstatic::classand: staticreturn typeslib/VM.php— runtimestatic::class/ return-type check must use the method's class, not the last FCC classruntime/*.cRepro
Done when
(new FM())->m(...),Base::who()isBaseandChild::who()isChild(Zend)(new B3())->f()with: staticreturns aB3(no TypeError namingFM)static::class/: staticwithout an FCC still match Zend.phptundertest/compliance/cases/language/