Commit 63d183e9c1a for php
commit 63d183e9c1a12b5caa359c6af5885dea671d2f4a
Author: David Carlier <devnexen@gmail.com>
Date: Sat Oct 31 06:45:08 2026 +0000
ext/opcache: Fix JIT ignoring exceptions from typed property set hooks
zend_may_throw_ex() didn't account for hooks on typed properties.
Close GH-25075
diff --git a/NEWS b/NEWS
index 24fd78630de..a2550c672e1 100644
--- a/NEWS
+++ b/NEWS
@@ -81,6 +81,7 @@ PHP NEWS
loop-invariant addition). (Ilia Alshanetsky)
. Fixed OSS-Fuzz #545352966 (default value AST of an SHM-persisted partial).
(ndossche)
+ . Fixed JIT ignoring exceptions from typed property set hooks. (David Carlier)
- PDO:
. Fixed PDOStatement::getColumnMeta() reading out of bounds for an invalid
diff --git a/Zend/Optimizer/zend_inference.c b/Zend/Optimizer/zend_inference.c
index 04742707238..8d01ac050cf 100644
--- a/Zend/Optimizer/zend_inference.c
+++ b/Zend/Optimizer/zend_inference.c
@@ -5264,6 +5264,7 @@ ZEND_API bool zend_may_throw_ex(const zend_op *opline, const zend_ssa_op *ssa_op
* and the value already has this type: the old value has no destructor.
* If the property holds a reference, all its type sources accept this type because they accept the current value. */
if ((prop_info->flags & (ZEND_ACC_READONLY|ZEND_ACC_PPP_SET_MASK))
+ || prop_info->hooks
|| ZEND_TYPE_IS_COMPLEX(prop_info->type)
|| (type_mask & ~(MAY_BE_NULL|MAY_BE_BOOL|MAY_BE_LONG|MAY_BE_DOUBLE|MAY_BE_STRING))
/* single type, this excludes bool too as a reference can exist to true or false standalone types. */
diff --git a/ext/opcache/tests/jit/assign_obj_007.phpt b/ext/opcache/tests/jit/assign_obj_007.phpt
new file mode 100644
index 00000000000..3843b07e859
--- /dev/null
+++ b/ext/opcache/tests/jit/assign_obj_007.phpt
@@ -0,0 +1,44 @@
+--TEST--
+JIT ASSIGN_OBJ: exception thrown by a set hook on a typed property
+--INI--
+opcache.enable=1
+opcache.enable_cli=1
+opcache.file_update_protection=0
+opcache.jit_buffer_size=1M
+opcache.jit=tracing
+opcache.jit_hot_loop=1
+opcache.jit_hot_func=1
+opcache.jit_hot_return=1
+opcache.jit_hot_side_exit=1
+--FILE--
+<?php
+class Foo {
+ public int $test = 1 {
+ set(int $v) {
+ if ($v === 3) {
+ throw new Exception('hook threw');
+ }
+ $this->test = $v;
+ }
+ }
+}
+
+function test() {
+ $foo = new Foo;
+ for ($i = 0; $i < 10; $i++) {
+ $foo->test = $i;
+ echo $i, PHP_EOL;
+ }
+}
+
+try {
+ test();
+} catch (Exception $e) {
+ echo $e::class, ': ', $e->getMessage(), PHP_EOL;
+}
+?>
+--EXPECT--
+0
+1
+2
+Exception: hook threw