Skip to content

Commit

Permalink
Deprecate autovivification on false
Browse files Browse the repository at this point in the history
Deprecate automatically converting "false" into an empty array
on write operands. Autovivification continues to be supported
for "null" values, as well as undefined/uninitialized values.

RFC: https://wiki.php.net/rfc/autovivification_false

Closes GH-7131.

Co-authored-by: Tyson Andre <tysonandre775@hotmail.com>
Co-authored-by: Nikita Popov <nikita.ppv@gmail.com>
  • Loading branch information
3 people committed Jul 19, 2021
1 parent c2a58ab commit 052af90
Show file tree
Hide file tree
Showing 15 changed files with 678 additions and 92 deletions.
3 changes: 3 additions & 0 deletions UPGRADING
Expand Up @@ -356,6 +356,9 @@ PHP 8.1 UPGRADE NOTES
. Returning a non-array from __sleep will raise a warning
. Returning by reference from a void function is deprecated.
RFC: https://wiki.php.net/rfc/deprecations_php_8_1
. Automatic conversion of "false" into an empty array on write operands is
deprecated.
RFC: https://wiki.php.net/rfc/autovivification_false

- Ctype:
. Passing a non-string value to ctype_*() functions is deprecated. A future
Expand Down
4 changes: 2 additions & 2 deletions Zend/Optimizer/zend_inference.c
Expand Up @@ -4753,7 +4753,7 @@ ZEND_API int zend_may_throw_ex(const zend_op *opline, const zend_ssa_op *ssa_op,
return 1;
}
}
return (t1 & (MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_TRUE|MAY_BE_STRING|MAY_BE_LONG|MAY_BE_DOUBLE)) || opline->op2_type == IS_UNUSED ||
return (t1 & (MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_TRUE|MAY_BE_FALSE|MAY_BE_STRING|MAY_BE_LONG|MAY_BE_DOUBLE)) || opline->op2_type == IS_UNUSED ||
(t2 & (MAY_BE_UNDEF|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE));
case ZEND_ASSIGN_OBJ:
if (t1 & (MAY_BE_ANY-(MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_OBJECT))) {
Expand Down Expand Up @@ -4869,7 +4869,7 @@ ZEND_API int zend_may_throw_ex(const zend_op *opline, const zend_ssa_op *ssa_op,
return 0;
case ZEND_FETCH_DIM_W:
case ZEND_FETCH_LIST_W:
if (t1 & (MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE|MAY_BE_STRING|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)) {
if (t1 & (MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE|MAY_BE_STRING|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)) {
return 1;
}
if (t2 & (MAY_BE_RESOURCE|MAY_BE_ARRAY|MAY_BE_OBJECT)) {
Expand Down
194 changes: 194 additions & 0 deletions Zend/tests/falsetoarray.phpt
@@ -0,0 +1,194 @@
--TEST--
Autovivification of false to array
--FILE--
<?php

// control
$undef[] = 42;

// control
$null = null;
$null[] = 42;

// control
$false = false;
$false = [42];

print "[001]\n";
$false = false;
$false[] = 42;

print "[002]\n";
$ref = false;
$ref2 = &$ref;
$ref2[] = 42;

echo "\nFunction\n";
function ffalse(bool $a, ?bool $b, &$c, ...$d) {
print "[003]\n";
$a[] = 42;
print "[004]\n";
$b[] = 42;
print "[005]\n";
$c[] = 42;
print "[006]\n";
$d[0][] = 42;
}
$ref = false;
ffalse(false, false, $ref, false);

echo "\nProperties\n";
class Cfalse {
public $def;
private $untyped = false;
static private $st = false;
static private $st2 = false;
static private $st3 = false;
public function __construct(public $pu, private $pr = false) {
print "[007]\n";
$this->def = false;
$this->def[] = 42;
print "[008]\n";
$this->untyped[] = 42;
print "[009]\n";
self::$st[] = 42;
print "[010]\n";
static::$st2[] = 42;
print "[011]\n";
$this::$st3[] = 42;
print "[012]\n";
$this->pu[] = 42;
print "[013]\n";
$this->pr[] = 42;
}
}
new Cfalse(false, false);

echo "\nDestructuring\n";

print "[014]\n";
$add = false;
foreach ([42] as $add[]);

print "[015]\n";
$arr = false;
[$arr[]] = [42];

print "[016]\n";
$arr = [ 0 => [ 0 => false ] ];
$arr[0][0][0][] = 42;

print "[017]\n";
$false = false;
$r42 = 42;
$false[] &= $r42;

$false = false;
$false2 = false;
$false3 = false;
function &g(){
print "[018]\n";
global $false;
$false[] = 42;

$var1 = false;
$GLOBALS["false2"] =& $var1;

print "[019]\n";
$GLOBALS["false3"][] = 42;

print "[020]\n";
static $f2 = false;
return $f2;
}

$false = &g();
$false[] = 42;
print "[021]\n";
$false2[] = 42;

print "[022]\n";
$a = false;
unset($a[0][0]);

print "[023]\n";
$a = false;
unset($a[0]);

?>
--EXPECTF--
[001]

Deprecated: Automatic conversion of false to array is deprecated in %s
[002]

Deprecated: Automatic conversion of false to array is deprecated in %s

Function
[003]

Deprecated: Automatic conversion of false to array is deprecated in %s
[004]

Deprecated: Automatic conversion of false to array is deprecated in %s
[005]

Deprecated: Automatic conversion of false to array is deprecated in %s
[006]

Deprecated: Automatic conversion of false to array is deprecated in %s

Properties
[007]

Deprecated: Automatic conversion of false to array is deprecated in %s
[008]

Deprecated: Automatic conversion of false to array is deprecated in %s
[009]

Deprecated: Automatic conversion of false to array is deprecated in %s
[010]

Deprecated: Automatic conversion of false to array is deprecated in %s
[011]

Deprecated: Automatic conversion of false to array is deprecated in %s
[012]

Deprecated: Automatic conversion of false to array is deprecated in %s
[013]

Deprecated: Automatic conversion of false to array is deprecated in %s

Destructuring
[014]

Deprecated: Automatic conversion of false to array is deprecated in %s
[015]

Deprecated: Automatic conversion of false to array is deprecated in %s
[016]

Deprecated: Automatic conversion of false to array is deprecated in %s
[017]

Deprecated: Automatic conversion of false to array is deprecated in %s
[018]

Deprecated: Automatic conversion of false to array is deprecated in %s
[019]

Deprecated: Automatic conversion of false to array is deprecated in %s
[020]

Deprecated: Automatic conversion of false to array is deprecated in %s
[021]

Deprecated: Automatic conversion of false to array is deprecated in %s
[022]

Deprecated: Automatic conversion of false to array is deprecated in %s
[023]

Deprecated: Automatic conversion of false to array is deprecated in %s
10 changes: 9 additions & 1 deletion Zend/tests/indexing_001.phpt
Expand Up @@ -52,7 +52,7 @@ foreach ($testvalues as $testvalue) {
}

?>
--EXPECT--
--EXPECTF--
*** Indexing - Testing value assignment with key ***
array(1) {
["foo"]=>
Expand All @@ -67,6 +67,8 @@ Cannot use a scalar value as an array
int(1)
Cannot use a scalar value as an array
bool(true)

Deprecated: Automatic conversion of false to array is deprecated in %s
array(1) {
["foo"]=>
array(1) {
Expand Down Expand Up @@ -102,6 +104,8 @@ Cannot use a scalar value as an array
int(1)
Cannot use a scalar value as an array
bool(true)

Deprecated: Automatic conversion of false to array is deprecated in %s
array(1) {
["foo"]=>
&array(1) {
Expand Down Expand Up @@ -132,6 +136,8 @@ Cannot use a scalar value as an array
int(1)
Cannot use a scalar value as an array
bool(true)

Deprecated: Automatic conversion of false to array is deprecated in %s
array(1) {
[0]=>
array(1) {
Expand Down Expand Up @@ -163,6 +169,8 @@ Cannot use a scalar value as an array
int(1)
Cannot use a scalar value as an array
bool(true)

Deprecated: Automatic conversion of false to array is deprecated in %s
array(1) {
[0]=>
&array(1) {
Expand Down
4 changes: 4 additions & 0 deletions Zend/tests/unset_non_array.phpt
Expand Up @@ -95,13 +95,17 @@ try {
?>
--EXPECTF--
Warning: Undefined variable $x in %s on line %d

Deprecated: Automatic conversion of false to array is deprecated in %s
Cannot unset offset in a non-array variable
Cannot unset offset in a non-array variable
Cannot unset offset in a non-array variable
Cannot unset string offsets
Cannot use object of type stdClass as array

Warning: Undefined variable $x in %s on line %d

Deprecated: Automatic conversion of false to array is deprecated in %s
Cannot unset offset in a non-array variable
Cannot unset offset in a non-array variable
Cannot unset offset in a non-array variable
Expand Down
8 changes: 8 additions & 0 deletions Zend/zend_execute.c
Expand Up @@ -1583,6 +1583,11 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_deprecated_function(const zend_functi
}
}

ZEND_API ZEND_COLD void ZEND_FASTCALL zend_false_to_array_deprecated(void)
{
zend_error(E_DEPRECATED, "Automatic conversion of false to array is deprecated");
}

static zend_never_inline void zend_assign_to_string_offset(zval *str, zval *dim, zval *value OPLINE_DC EXECUTE_DATA_DC)
{
zend_uchar c;
Expand Down Expand Up @@ -2330,6 +2335,9 @@ static zend_always_inline void zend_fetch_dimension_address(zval *result, zval *
if (type != BP_VAR_W && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
ZVAL_UNDEFINED_OP1();
}
if (Z_TYPE_P(container) == IS_FALSE) {
zend_false_to_array_deprecated();
}
if (type != BP_VAR_UNSET) {
array_init(container);
goto fetch_from_array;
Expand Down
1 change: 1 addition & 0 deletions Zend/zend_execute.h
Expand Up @@ -58,6 +58,7 @@ extern ZEND_API const zend_internal_function zend_pass_function;

ZEND_API ZEND_COLD void ZEND_FASTCALL zend_missing_arg_error(zend_execute_data *execute_data);
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_deprecated_function(const zend_function *fbc);
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_false_to_array_deprecated(void);
ZEND_COLD void ZEND_FASTCALL zend_param_must_be_ref(const zend_function *func, uint32_t arg_num);

ZEND_API bool ZEND_FASTCALL zend_verify_ref_assignable_zval(zend_reference *ref, zval *zv, bool strict);
Expand Down
9 changes: 9 additions & 0 deletions Zend/zend_vm_def.h
Expand Up @@ -1210,6 +1210,9 @@ ZEND_VM_C_LABEL(assign_dim_op_new_array):
if (OP1_TYPE == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(container) == IS_UNDEF)) {
ZVAL_UNDEFINED_OP1();
}
if (Z_TYPE_P(container) == IS_FALSE) {
zend_false_to_array_deprecated();
}
ZVAL_ARR(container, zend_new_array(8));
ZEND_VM_C_GOTO(assign_dim_op_new_array);
} else {
Expand Down Expand Up @@ -2590,6 +2593,10 @@ ZEND_VM_C_LABEL(try_assign_dim_array):
FREE_OP_DATA();
}
} else if (EXPECTED(Z_TYPE_P(object_ptr) <= IS_FALSE)) {
if (Z_TYPE_P(object_ptr) == IS_FALSE) {
zend_false_to_array_deprecated();
}

if (Z_ISREF_P(orig_object_ptr)
&& ZEND_REF_HAS_TYPE_SOURCES(Z_REF_P(orig_object_ptr))
&& !zend_verify_ref_array_assignable(Z_REF_P(orig_object_ptr))) {
Expand Down Expand Up @@ -6453,6 +6460,8 @@ ZEND_VM_C_LABEL(num_index_dim):
zend_throw_error(NULL, "Cannot unset string offsets");
} else if (UNEXPECTED(Z_TYPE_P(container) > IS_FALSE)) {
zend_throw_error(NULL, "Cannot unset offset in a non-array variable");
} else if (UNEXPECTED(Z_TYPE_P(container) == IS_FALSE)) {
zend_false_to_array_deprecated();
}
} while (0);

Expand Down

0 comments on commit 052af90

Please sign in to comment.