Commit 614f935fe0c for php

commit 614f935fe0cb6a696f7d1f5728a73f52d5d66f1b
Author: David Carlier <devnexen@gmail.com>
Date:   Thu Oct 22 16:56:01 2026 +0100

    Fix GH-24534: inheritance cache records unlinked child dependencies

    Replaying such an entry autoloaded the child before its parent was
    linked. Unlinked dependencies now make the class non-cacheable.

    Close GH-24556

diff --git a/NEWS b/NEWS
index 63621c3ee7f..3d05c595e6f 100644
--- a/NEWS
+++ b/NEWS
@@ -36,6 +36,8 @@ PHP                                                                        NEWS
     (Marti2203).
   . Fixed bug GH-24989 (Use-after-free in GC when an exception is thrown).
     (David Carlier)
+  . Fixed bug GH-24534 (Segfault in zend_try_early_bind when a parent class
+    autoloads its cached child during linking). (David Carlier)

 - CLI:
   . Fixed bug GH-24697 (PHP_HTTP_PARSER_STRICT build option doesn't work).
diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c
index a4dd5f1893d..4d73dd003a3 100644
--- a/Zend/zend_inheritance.c
+++ b/Zend/zend_inheritance.c
@@ -404,7 +404,7 @@ static void track_class_dependency(zend_class_entry *ce, zend_string *class_name

 	ht = (HashTable*)CG(current_linking_class)->inheritance_cache;

-	if (!(ce->ce_flags & ZEND_ACC_IMMUTABLE)) {
+	if (!(ce->ce_flags & ZEND_ACC_IMMUTABLE) || !(ce->ce_flags & ZEND_ACC_LINKED)) {
 		// TODO: dependency on not-immutable class ???
 		if (ht) {
 			zend_hash_destroy(ht);
diff --git a/ext/opcache/tests/gh24534.phpt b/ext/opcache/tests/gh24534.phpt
new file mode 100644
index 00000000000..99dad338662
--- /dev/null
+++ b/ext/opcache/tests/gh24534.phpt
@@ -0,0 +1,95 @@
+--TEST--
+GH-24534: Inheritance cache must not record an unlinked child as a dependency
+--CREDITS--
+Wim Winterberg
+--EXTENSIONS--
+opcache
+--CONFLICTS--
+server
+--FILE--
+<?php
+$dir = __DIR__ . '/gh24534';
+@mkdir($dir . '/src', 0777, true);
+
+file_put_contents($dir . '/autoload.php', <<<'PHP'
+<?php
+spl_autoload_register(static function (string $class): void {
+    $file = __DIR__ . '/src/' . $class . '.php';
+    if (is_file($file)) {
+        require $file;
+    }
+});
+PHP);
+
+file_put_contents($dir . '/src/EntityInterface.php', <<<'PHP'
+<?php
+interface EntityInterface
+{
+    public static function getById(int $id): ?self;
+}
+PHP);
+
+file_put_contents($dir . '/src/Entity.php', <<<'PHP'
+<?php
+abstract class Entity implements EntityInterface
+{
+    protected array $data = [];
+}
+PHP);
+
+file_put_contents($dir . '/src/UserBase.php', <<<'PHP'
+<?php
+abstract class UserBase extends Entity
+{
+    public const TABLE = 'user';
+
+    protected ?int $userId = null;
+
+    public static function getById(int $id): ?User
+    {
+        return null;
+    }
+}
+PHP);
+
+file_put_contents($dir . '/src/User.php', <<<'PHP'
+<?php
+class User extends UserBase
+{
+}
+PHP);
+
+file_put_contents($dir . '/base-first.php', <<<'PHP'
+<?php
+require __DIR__ . '/autoload.php';
+echo 'base-first: ', UserBase::TABLE, PHP_EOL;
+PHP);
+
+file_put_contents($dir . '/child-first.php', <<<'PHP'
+<?php
+require __DIR__ . '/autoload.php';
+echo 'child-first: ', User::TABLE, PHP_EOL;
+PHP);
+
+include 'php_cli_server.inc';
+$ini = trim((string) getenv('TEST_PHP_EXTRA_ARGS'));
+$ini .= ($ini !== '' ? ' ' : '') . '-d opcache.enable=1 -d opcache.enable_cli=1 -d opcache.file_update_protection=0';
+php_cli_server_start($ini);
+
+foreach (['base-first', 'child-first', 'base-first'] as $page) {
+    echo file_get_contents('http://' . PHP_CLI_SERVER_ADDRESS . '/gh24534/' . $page . '.php');
+}
+?>
+--CLEAN--
+<?php
+$dir = __DIR__ . '/gh24534';
+foreach (['src/EntityInterface.php', 'src/Entity.php', 'src/UserBase.php', 'src/User.php', 'autoload.php', 'base-first.php', 'child-first.php'] as $file) {
+    @unlink($dir . '/' . $file);
+}
+@rmdir($dir . '/src');
+@rmdir($dir);
+?>
+--EXPECT--
+base-first: user
+child-first: user
+base-first: user