Commit a402e3645cd for php

commit a402e3645cd9f9c9b3e4ddf4e41cc156dc9f3382
Author: lazerg <lazerg2@gmail.com>
Date:   Sat Nov 14 19:11:27 2026 +0800

    Fix GH-24743: var_export() ignores exception from lazy object init (#24746)

    Stop exporting when zend_get_properties_for() raises an exception while
    initializing a lazy object. Release the recursion guard and properties
    before returning FAILURE so var_export() does not emit partial output.

    Add regression coverage for lazy initialization failures and retain coverage
    for nested property hook exceptions, whose failure handling is already fixed.

    Closes #24746

diff --git a/NEWS b/NEWS
index 004580cf218..7b2fd52737b 100644
--- a/NEWS
+++ b/NEWS
@@ -105,6 +105,8 @@ PHP                                                                        NEWS
     DNS responses. (Marti2203)
   . Fixed bug GH-25387 (Uploaded files leak in request_parse_body()).
     (Kévin Dunglas)
+  . Fixed bug GH-24743 (var_export() ignores exceptions from lazy object
+    initialization). (lazerg)

 - Streams:
   . Fixed bug GH-22063 (Stream filter chain UAF on self-removal during
diff --git a/ext/standard/tests/general_functions/gh24743_1.phpt b/ext/standard/tests/general_functions/gh24743_1.phpt
new file mode 100644
index 00000000000..3cf841cb17f
--- /dev/null
+++ b/ext/standard/tests/general_functions/gh24743_1.phpt
@@ -0,0 +1,79 @@
+--TEST--
+GH-24743 (var_export() does not handle exception correctly when retrieving object)
+--FILE--
+<?php
+
+class Inner {
+    public static bool $fail = true;
+
+    public int $x = 1 {
+        get {
+            if (self::$fail) {
+                throw new Exception("hook failed");
+            }
+            return $this->x;
+        }
+    }
+}
+
+class Outer {
+    public Inner $a;
+    public int $b = 2;
+
+    public function __construct() {
+        $this->a = new Inner;
+    }
+}
+
+$o = new Outer;
+
+function show_export(string $label, callable $export): void
+{
+    ob_start();
+    $caught = null;
+    try {
+        $export();
+    } catch (Throwable $e) {
+        $caught = $e;
+    }
+    $output = ob_get_clean();
+
+    echo "--- ", $label, " ---\n";
+    if ($caught === null) {
+        echo "No exception\n";
+    } else {
+        echo "Caught: ", $caught->getMessage(), "\n";
+    }
+
+    if ($output !== '') {
+        echo "BUG: var_export() emitted partial output before the exception:\n";
+        echo $output, "\n";
+        echo "--- end of partial output ---\n";
+    }
+}
+
+show_export("print mode, nested object", fn() => var_export($o));
+show_export("return mode, nested object", fn() => var_export($o, true));
+show_export("print mode, object inside array", fn() => var_export([1, $o]));
+
+echo "--- recovery: recursion guards were released ---\n";
+Inner::$fail = false;
+var_export($o);
+echo "\n";
+
+?>
+--EXPECT--
+--- print mode, nested object ---
+Caught: hook failed
+--- return mode, nested object ---
+Caught: hook failed
+--- print mode, object inside array ---
+Caught: hook failed
+--- recovery: recursion guards were released ---
+\Outer::__set_state(array(
+   'a' =>
+  \Inner::__set_state(array(
+     'x' => 1,
+  )),
+   'b' => 2,
+))
diff --git a/ext/standard/tests/general_functions/gh24743_2.phpt b/ext/standard/tests/general_functions/gh24743_2.phpt
new file mode 100644
index 00000000000..fd57fe5f114
--- /dev/null
+++ b/ext/standard/tests/general_functions/gh24743_2.phpt
@@ -0,0 +1,35 @@
+--TEST--
+GH-24743 (var_export() does not handle exception correctly when retrieving object): lazy object initializer
+--FILE--
+<?php
+
+class C {
+    public int $a = 1;
+}
+
+class O {
+    public C $c;
+    public int $b = 2;
+}
+
+$o = new O;
+$o->c = (new ReflectionClass(C::class))->newLazyGhost(function ($obj) {
+    throw new Exception("init failed");
+});
+
+try {
+    var_export($o);
+} catch (Exception $e) {
+    echo $e->getMessage(), "\n";
+}
+
+try {
+    var_export($o->c);
+} catch (Exception $e) {
+    echo $e->getMessage(), "\n";
+}
+
+?>
+--EXPECT--
+init failed
+init failed
diff --git a/ext/standard/var.c b/ext/standard/var.c
index 6ce623bc2a4..dc398c5a598 100644
--- a/ext/standard/var.c
+++ b/ext/standard/var.c
@@ -635,6 +635,11 @@ PHPAPI zend_result php_var_export_ex(zval *struc, int level, smart_str *buf) /*
 			}
 			ZEND_GUARD_OR_GC_PROTECT_RECURSION(guard, EXPORT, zobj);
 			myht = zend_get_properties_for(struc, ZEND_PROP_PURPOSE_VAR_EXPORT);
+			if (UNEXPECTED(EG(exception))) {
+				ZEND_GUARD_OR_GC_UNPROTECT_RECURSION(guard, EXPORT, zobj);
+				zend_release_properties(myht);
+				return FAILURE;
+			}
 			if (level > 1) {
 				smart_str_appendc(buf, '\n');
 				buffer_append_spaces(buf, level - 1);