Commit 0c406ea1723 for php

commit 0c406ea1723d30fa64395a0e32db40f44a82036c
Author: lazerg <lazerg2@gmail.com>
Date:   Sat Oct 31 11:09:54 2026 +0500

    Fix GH-25061: $x = $x - 1 on null is optimized to PRE_DEC

    Closes GH-25074.

diff --git a/NEWS b/NEWS
index c8cec45a97e..14ce817e8ca 100644
--- a/NEWS
+++ b/NEWS
@@ -44,6 +44,8 @@ PHP                                                                        NEWS
     modifier. (Jorg Sowa)
   . Fixed memory leak of a duplicate named argument in new in initializer.
     (Jorg Sowa)
+  . Fixed bug GH-25061 ($x = $x - 1 on null is optimized to PRE_DEC).
+    (Lazizbek Ergashev)

 - CLI:
   . Fixed bug GH-24697 (PHP_HTTP_PARSER_STRICT build option doesn't work).
diff --git a/Zend/Optimizer/dfa_pass.c b/Zend/Optimizer/dfa_pass.c
index 2c3aaae0659..6dd35cb9f37 100644
--- a/Zend/Optimizer/dfa_pass.c
+++ b/Zend/Optimizer/dfa_pass.c
@@ -1359,7 +1359,8 @@ void zend_dfa_optimize_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx
 					 && Z_TYPE_P(CT_CONSTANT_EX(op_array, op_array->opcodes[op_2].op2.constant)) == IS_LONG
 					 && Z_LVAL_P(CT_CONSTANT_EX(op_array, op_array->opcodes[op_2].op2.constant)) == 1
 					 && ssa->ops[op_2].op1_use >= 0
-					 && !(ssa->var_info[ssa->ops[op_2].op1_use].type & (MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) {
+					 /* MAY_BE_UNDEF|MAY_BE_NULL is excluded because of the currently inconsistent behaviour, revisit in PHP 9 once it is fixed */
+					 && !(ssa->var_info[ssa->ops[op_2].op1_use].type & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) {

 						op_array->opcodes[op_2].opcode = ZEND_PRE_DEC;
 						SET_UNUSED(op_array->opcodes[op_2].op2);
@@ -1513,7 +1514,8 @@ void zend_dfa_optimize_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx
 						 && Z_TYPE_P(CT_CONSTANT_EX(op_array, op_array->opcodes[op_2].op2.constant)) == IS_LONG
 						 && Z_LVAL_P(CT_CONSTANT_EX(op_array, op_array->opcodes[op_2].op2.constant)) == 1
 						 && ssa->ops[op_2].op1_use >= 0
-						 && !(ssa->var_info[ssa->ops[op_2].op1_use].type & (MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) {
+						 /* MAY_BE_UNDEF|MAY_BE_NULL is excluded because of the currently inconsistent behaviour, revisit in PHP 9 once it is fixed */
+						 && !(ssa->var_info[ssa->ops[op_2].op1_use].type & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) {

 							op_array->opcodes[op_2].opcode = ZEND_PRE_DEC;
 							SET_UNUSED(op_array->opcodes[op_2].op2);
diff --git a/ext/opcache/tests/opt/gh25061.phpt b/ext/opcache/tests/opt/gh25061.phpt
new file mode 100644
index 00000000000..7a784c8a28c
--- /dev/null
+++ b/ext/opcache/tests/opt/gh25061.phpt
@@ -0,0 +1,43 @@
+--TEST--
+GH-25061 ($x = $x - 1 on null is optimized to PRE_DEC)
+--EXTENSIONS--
+opcache
+--INI--
+opcache.enable=1
+opcache.enable_cli=1
+opcache.optimization_level=-1
+--FILE--
+<?php
+
+function nullable(?int $x)
+{
+    $x = $x - 1;
+    return $x;
+}
+
+function undef()
+{
+    $x = $x - 1;
+    return $x;
+}
+
+function twice(?int $x)
+{
+    $x = $x - 1;
+    $x = $x - 1;
+    return $x;
+}
+
+var_dump(nullable(null));
+var_dump(nullable(5));
+var_dump(undef());
+var_dump(twice(null));
+
+?>
+--EXPECTF--
+int(-1)
+int(4)
+
+Warning: Undefined variable $x in %s on line %d
+int(-1)
+int(-2)