From 9636ca49daf3fa1c0af9b215a5d81804bc157da8 Mon Sep 17 00:00:00 2001 From: lazerg Date: Sat, 31 Oct 2026 11:09:54 +0500 Subject: [PATCH 1/2] Fix GH-25061: $x = $x - 1 on null is optimized to PRE_DEC --- Zend/Optimizer/dfa_pass.c | 4 +-- ext/opcache/tests/opt/gh25061.phpt | 43 ++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 ext/opcache/tests/opt/gh25061.phpt diff --git a/Zend/Optimizer/dfa_pass.c b/Zend/Optimizer/dfa_pass.c index 2c3aaae06599..e7a92a302fe2 100644 --- a/Zend/Optimizer/dfa_pass.c +++ b/Zend/Optimizer/dfa_pass.c @@ -1359,7 +1359,7 @@ 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))) { + && !(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 +1513,7 @@ 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))) { + && !(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 000000000000..7a784c8a28c8 --- /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-- + +--EXPECTF-- +int(-1) +int(4) + +Warning: Undefined variable $x in %s on line %d +int(-1) +int(-2) From 1727b55c4fbf3f479ae481f5b41d16af6ca427af Mon Sep 17 00:00:00 2001 From: lazerg Date: Sat, 31 Oct 2026 20:42:09 +0500 Subject: [PATCH 2/2] Add comment on the null exclusion in SUB to PRE_DEC rewrite --- Zend/Optimizer/dfa_pass.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Zend/Optimizer/dfa_pass.c b/Zend/Optimizer/dfa_pass.c index e7a92a302fe2..6dd35cb9f379 100644 --- a/Zend/Optimizer/dfa_pass.c +++ b/Zend/Optimizer/dfa_pass.c @@ -1359,6 +1359,7 @@ 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 + /* 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; @@ -1513,6 +1514,7 @@ 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 + /* 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;