Commit a7a49e039d7 for php
commit a7a49e039d756d9d336b770316c282d02db08980
Author: ndossche <7771979+ndossche@users.noreply.github.com>
Date: Thu Oct 29 20:55:30 2026 +0100
Fix JIT losing the called scope of self::/parent:: calls in included files
File/eval code take This from the existing frame's scope,
so This may be a class instead of an object.
Closes GH-25041.
diff --git a/NEWS b/NEWS
index cb05e526f7f..4b16461b571 100644
--- a/NEWS
+++ b/NEWS
@@ -179,6 +179,8 @@ PHP NEWS
(Pavel Gushcha)
. Fixed potential infinite recursion/hang (HYBRID/TAILCALL respectively)
when zend_interrupt_function resets EG(vm_interrupt). (Levi Morrison)
+ . Fix JIT losing the called scope of self::/parent:: calls in included files.
+ (ndossche)
- OpenSSL:
. Fixed bug GH-24784 (openssl_pkcs7_read() and openssl_cms_read() overwrite
diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c
index cc3b867b2f2..be3c7ad9bcb 100644
--- a/ext/opcache/jit/zend_jit_ir.c
+++ b/ext/opcache/jit/zend_jit_ir.c
@@ -9389,7 +9389,8 @@ static int zend_jit_init_static_method_call(zend_jit_ctx *jit,
(opline->op1.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_SELF)) {
if (op_array->fn_flags & ZEND_ACC_STATIC) {
scope_ref = ir_LOAD_A(jit_EX(This.value.ref));
- } else if (op_array->fn_flags & ZEND_ACC_CLOSURE) {
+ } else if ((op_array->fn_flags & ZEND_ACC_CLOSURE) || !op_array->function_name) {
+ /* Closures can be unbound, file/eval inherits This from the existing frame's scope. */
ir_ref if_object, values = IR_UNUSED;
if_object = ir_IF(ir_EQ(jit_Z_TYPE_ref(jit, jit_EX(This)), ir_CONST_U8(IS_OBJECT)));
diff --git a/ext/opcache/tests/jit/init_static_method_call_002.phpt b/ext/opcache/tests/jit/init_static_method_call_002.phpt
new file mode 100644
index 00000000000..897c0377d6c
--- /dev/null
+++ b/ext/opcache/tests/jit/init_static_method_call_002.phpt
@@ -0,0 +1,32 @@
+--TEST--
+JIT INIT_STATIC_METHOD_CALL: self:: in an included file keeps the called scope of a static caller
+--INI--
+opcache.enable=1
+opcache.enable_cli=1
+opcache.file_update_protection=0
+opcache.jit=tracing
+opcache.jit_hot_loop=1
+--EXTENSIONS--
+opcache
+--FILE--
+<?php
+
+class A {
+ public static function who() {
+ return static::class;
+ }
+
+ public static function test() {
+ include __DIR__ . '/init_static_method_call_002_include.inc';
+ }
+}
+
+class B extends A {}
+
+B::test();
+
+?>
+--EXPECT--
+B
+B
+B
diff --git a/ext/opcache/tests/jit/init_static_method_call_002_include.inc b/ext/opcache/tests/jit/init_static_method_call_002_include.inc
new file mode 100644
index 00000000000..66ea277d2da
--- /dev/null
+++ b/ext/opcache/tests/jit/init_static_method_call_002_include.inc
@@ -0,0 +1,4 @@
+<?php
+for ($i = 0; $i < 3; $i++) {
+ echo self::who(), "\n";
+}