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)