Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ PHP NEWS
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
?? ??? ????, PHP 8.6.0beta1

- Core:
. Fixed bug GH-17773 (Initializing array elements with references returned by
functions was not possible). (Matthias Görgens)

- GMP:
. Added optional $definitely_prime output parameter to gmp_prevprime().
(Weilin Du)
Expand Down
6 changes: 6 additions & 0 deletions Zend/Optimizer/zend_inference.c
Original file line number Diff line number Diff line change
Expand Up @@ -4923,6 +4923,12 @@ ZEND_API zend_result zend_ssa_inference(zend_arena **arena, const zend_op_array

ZEND_API bool zend_may_throw_ex(const zend_op *opline, const zend_ssa_op *ssa_op, const zend_op_array *op_array, const zend_ssa *ssa, uint32_t t1, uint32_t t2)
{
if (opline->opcode == ZEND_MAKE_REF && opline->extended_value == ZEND_RETURNS_FUNCTION) {
/* A by-value function result raises an E_NOTICE, which a userland
* error handler can turn into an exception. */
return 1;
}

if (opline->op1_type == IS_CV) {
if (t1 & MAY_BE_UNDEF) {
switch (opline->opcode) {
Expand Down
52 changes: 52 additions & 0 deletions Zend/tests/gh17773.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
--TEST--
GH-17773 (Initializing array element with reference returned by function)
--FILE--
<?php
function &get_reference() {
static $value = 0;
return $value;
}

$array = [&get_reference()];
$array[0] = 42;
var_dump(get_reference());

class Test {
public int $value = 0;
public static int $staticValue = 0;

public function &getReference() {
return $this->value;
}

public static function &getStaticReference() {
return self::$staticValue;
}
}

$test = new Test();
$array = [&$test->getReference(), &Test::getStaticReference(), [&get_reference()]];
$unpacked = [...$array];
$unpacked[0] = 43;
$unpacked[1] = 44;
$unpacked[2][0] = 45;
var_dump($test->value, Test::$staticValue, get_reference());

function get_value() {
return 42;
}

$array = [&get_value()];
var_dump($array);
?>
--EXPECTF--
int(42)
int(43)
int(44)
int(45)

Notice: Only variables should be assigned by reference in %s on line %d
array(1) {
[0]=>
int(42)
}
8 changes: 8 additions & 0 deletions Zend/tests/gh17773_builtin.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
--TEST--
GH-17773 (Cannot initialize array element with reference returned by built-in function)
--FILE--
<?php
$array = [&strlen('value')];
?>
--EXPECTF--
Fatal error: Cannot use result of built-in function in write context in %s on line %d
23 changes: 23 additions & 0 deletions Zend/tests/gh17773_exception.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
--TEST--
GH-17773 (Exception while reporting by-value function result used by reference)
--FILE--
<?php
function get_value() {
return 42;
}

set_error_handler(function (int $type, string $message) {
throw new Exception($message);
});

try {
$array = [&get_value()];
} catch (Exception $exception) {
echo $exception->getMessage(), "\n";
}

echo "Done\n";
?>
--EXPECT--
Only variables should be assigned by reference
Done
8 changes: 8 additions & 0 deletions Zend/tests/gh17773_globals.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
--TEST--
GH-17773 (Cannot initialize array element with reference to $GLOBALS)
--FILE--
<?php
$array = [&$GLOBALS];
?>
--EXPECTF--
Fatal error: Cannot acquire reference to $GLOBALS in %s on line %d
16 changes: 16 additions & 0 deletions Zend/tests/gh17773_nullsafe.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
--TEST--
GH-17773 (Cannot initialize array element with reference to nullsafe chain)
--FILE--
<?php
class Test {
public function &getReference() {
static $value = 42;
return $value;
}
}

$test = null;
$array = [&$test?->getReference()];
?>
--EXPECTF--
Fatal error: Cannot take reference of a nullsafe chain in %s on line %d
19 changes: 19 additions & 0 deletions Zend/tests/gh17773_uncaught.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
--TEST--
GH-17773 (Uncaught exception from by-value function result used by reference)
--FILE--
<?php
function get_value() {
return 42;
}
set_error_handler(function (int $type, string $message) {
throw new Exception($message);
});
$array = [&get_value()];
echo "Done\n";
?>
--EXPECTF--
Fatal error: Uncaught Exception: Only variables should be assigned by reference in %s:%d
Stack trace:
#0 %s(%d): {closure:%s:%d}(8, 'Only variables ...', '%s', 8)
#1 {main}
thrown in %s on line %d
13 changes: 12 additions & 1 deletion Zend/zend_compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -11537,8 +11537,19 @@ static void zend_compile_array(znode *result, zend_ast *ast) /* {{{ */
}

if (by_ref) {
zend_ensure_writable_variable(value_ast);
zend_assert_not_short_circuited(value_ast);
if (is_globals_fetch(value_ast)) {
zend_error_noreturn(E_COMPILE_ERROR, "Cannot acquire reference to $GLOBALS");
}
zend_compile_var(&value_node, value_ast, BP_VAR_W, true);
if (value_node.op_type != IS_VAR && zend_is_call(value_ast)) {
zend_error_noreturn(E_COMPILE_ERROR,
"Cannot use result of built-in function in write context");
}
if (zend_is_call(value_ast)) {
opline = zend_emit_op(&value_node, ZEND_MAKE_REF, &value_node, NULL);
opline->extended_value = ZEND_RETURNS_FUNCTION;
}
} else {
zend_compile_expr(&value_node, value_ast);
}
Expand Down
9 changes: 9 additions & 0 deletions Zend/zend_vm_def.h
Original file line number Diff line number Diff line change
Expand Up @@ -9393,6 +9393,15 @@ ZEND_VM_HANDLER(140, ZEND_MAKE_REF, VAR|CV, UNUSED)
}
ZVAL_REF(EX_VAR(opline->result.var), Z_REF_P(op1));
} else {
if (UNEXPECTED(opline->extended_value == ZEND_RETURNS_FUNCTION && !Z_ISREF_P(op1))) {
SAVE_OPLINE();
zend_error(E_NOTICE, "Only variables should be assigned by reference");
if (UNEXPECTED(EG(exception))) {
FREE_OP1();
ZVAL_UNDEF(EX_VAR(opline->result.var));
HANDLE_EXCEPTION();
}
}
ZVAL_COPY_VALUE(EX_VAR(opline->result.var), op1);
}
ZEND_VM_NEXT_OPCODE();
Expand Down
38 changes: 38 additions & 0 deletions Zend/zend_vm_execute.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading