Skip to content

Commit 902d643

Browse files
committed
Deprecate implicit dynamic properties
Writing to a proprety that hasn't been declared is deprecated, unless the class uses the #[AllowDynamicProperties] attribute or defines __get()/__set(). RFC: https://wiki.php.net/rfc/deprecate_dynamic_properties
1 parent 35a01f8 commit 902d643

File tree

201 files changed

+583
-177
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

201 files changed

+583
-177
lines changed

UPGRADING

+10
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,16 @@ PHP 8.2 UPGRADE NOTES
4848
========================================
4949

5050
- Core:
51+
. Creation of dynamic properties is deprecated, unless the class opts in by
52+
using the #[AllowDynamicProperties] attribute. stdClass allows dynamic
53+
properties. Usage of __get()/__set() is not affected by this change. A
54+
dynamic properties deprecation warning can be addressed by:
55+
- Declaring the property (preferred).
56+
- Adding the #[AllowDynamicProperties] attribute to the class (which also
57+
applies to all child classes).
58+
- Using a WeakMap if you wish to associate additional data with an object
59+
you do not own.
60+
5161
. Callables that are not accepted by the $callable() syntax (but are accepted
5262
by call_user_func) are deprecated. In particular:
5363

Zend/Optimizer/sccp.c

+3-1
Original file line numberDiff line numberDiff line change
@@ -1101,7 +1101,9 @@ static void sccp_visit_instr(scdf_ctx *scdf, zend_op *opline, zend_ssa_op *ssa_o
11011101

11021102
/* Don't try to propagate assignments to (potentially) typed properties. We would
11031103
* need to deal with errors and type conversions first. */
1104-
if (!var_info->ce || (var_info->ce->ce_flags & ZEND_ACC_HAS_TYPE_HINTS)) {
1104+
// TODO: Distinguish dynamic and declared property assignments here?
1105+
if (!var_info->ce || (var_info->ce->ce_flags & ZEND_ACC_HAS_TYPE_HINTS) ||
1106+
!(var_info->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) {
11051107
SET_RESULT_BOT(result);
11061108
SET_RESULT_BOT(op1);
11071109
return;

Zend/Optimizer/zend_inference.c

+8-6
Original file line numberDiff line numberDiff line change
@@ -4842,15 +4842,17 @@ ZEND_API bool zend_may_throw_ex(const zend_op *opline, const zend_ssa_op *ssa_op
48424842
return 1;
48434843
}
48444844

4845-
if (op_array->scope != ce && ce->default_properties_count) {
4846-
zend_property_info *prop_info =
4847-
zend_hash_find_ptr(&ce->properties_info, prop_name);
4848-
if (prop_info && (!(prop_info->flags & ZEND_ACC_PUBLIC)
4849-
|| ZEND_TYPE_IS_SET(prop_info->type))) {
4845+
zend_property_info *prop_info =
4846+
zend_hash_find_ptr(&ce->properties_info, prop_name);
4847+
if (prop_info) {
4848+
if (ZEND_TYPE_IS_SET(prop_info->type)) {
48504849
return 1;
48514850
}
4851+
return !(prop_info->flags & ZEND_ACC_PUBLIC)
4852+
&& prop_info->ce != op_array->scope;
4853+
} else {
4854+
return !(ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES);
48524855
}
4853-
return 0;
48544856
}
48554857
return 1;
48564858
case ZEND_ROPE_INIT:
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
--TEST--
2+
#[AllowDynamicProperties] cannot be applied to interface
3+
--FILE--
4+
<?php
5+
6+
#[AllowDynamicProperties]
7+
interface Test {}
8+
9+
?>
10+
--EXPECTF--
11+
Fatal error: Cannot apply #[AllowDynamicProperties] to interface in %s on line %d
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
--TEST--
2+
#[AllowDynamicProperties] cannot be applied to trait
3+
--FILE--
4+
<?php
5+
6+
#[AllowDynamicProperties]
7+
trait Test {}
8+
9+
?>
10+
--EXPECTF--
11+
Fatal error: Cannot apply #[AllowDynamicProperties] to trait in %s on line %d

Zend/tests/anon/003.phpt

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ reusing anonymous classes
44
<?php
55
while (@$i++<10) {
66
var_dump(new class($i) {
7-
7+
public $i;
88
public function __construct($i) {
99
$this->i = $i;
1010
}

Zend/tests/assign_to_obj_001.phpt

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ function &a($i) {
88
}
99

1010
class A {
11+
public $a;
1112
public function test() {
1213
$this->a = a(1);
1314
unset($this->a);

Zend/tests/bug27268.phpt

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
Bug #27268 (Bad references accentuated by clone)
33
--FILE--
44
<?php
5+
#[AllowDynamicProperties]
56
class A
67
{
78
public function &getA()

Zend/tests/bug30162.phpt

+2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
Bug #30162 (Catching exception in constructor couses lose of $this)
33
--FILE--
44
<?php
5+
#[AllowDynamicProperties]
56
class FIIFO {
67

78
public function __construct() {
@@ -11,6 +12,7 @@ class FIIFO {
1112

1213
}
1314

15+
#[AllowDynamicProperties]
1416
class hariCow extends FIIFO {
1517

1618
public function __construct() {

Zend/tests/bug38779_1.phpt

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ Bug #38779 (engine crashes when require()'ing file with syntax error through use
44
<?php
55

66
class Loader {
7+
public $context;
78
private $position;
89
private $data;
910
public function stream_open($path, $mode, $options, &$opened_path) {

Zend/tests/bug47343.phpt

+2-4
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ Bug #47343 (gc_collect_cycles causes a segfault when called within a destructor
44
<?php
55
class A
66
{
7+
public $data = [];
78
public function __destruct()
89
{
910
gc_collect_cycles();
@@ -20,10 +21,7 @@ class A
2021

2122
class B
2223
{
23-
public function __construct($A)
24-
{
25-
$this->A = $A;
26-
}
24+
public function __construct(public $A) {}
2725

2826
public function __destruct()
2927
{

Zend/tests/bug49893.phpt

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ class A {
1212
}
1313
}
1414
class B {
15+
public $a;
1516
function __construct() {
1617
$this->a = new A();
1718
throw new Exception("1");

Zend/tests/bug51822.phpt

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ class DestructableObject
1212

1313
class DestructorCreator
1414
{
15+
public $test;
1516
public function __destruct()
1617
{
1718
$this->test = new DestructableObject;

Zend/tests/bug54268.phpt

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ class DestructableObject
2020
}
2121
class DestructorCreator
2222
{
23+
public $test;
2324
public function __destruct()
2425
{
2526
$this->test = new DestructableObject;

Zend/tests/bug55305.phpt

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
Bug #55305 (ref lost: 1st ref instantiated in class def, 2nd ref made w/o instantiating)
33
--FILE--
44
<?php
5+
#[AllowDynamicProperties]
56
class Foo {
67
var $foo = "test";
78
}

Zend/tests/bug60536_001.phpt

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ class Y extends X {
1212
return ++$this->x;
1313
}
1414
}
15+
#[AllowDynamicProperties]
1516
class Z extends Y {
1617
function __construct() {
1718
return ++$this->x;

Zend/tests/bug60833.phpt

+14-19
Original file line numberDiff line numberDiff line change
@@ -5,35 +5,30 @@ Bug #60833 (self, parent, static behave inconsistently case-sensitive)
55
class A {
66
static $x = "A";
77
function testit() {
8-
$this->v1 = new sELF;
9-
$this->v2 = new SELF;
8+
var_dump(new sELF);
9+
var_dump(new SELF);
1010
}
1111
}
1212

1313
class B extends A {
1414
static $x = "B";
1515
function testit() {
1616
PARENT::testit();
17-
$this->v3 = new sELF;
18-
$this->v4 = new PARENT;
19-
$this->v4 = STATIC::$x;
17+
var_dump(new sELF);
18+
var_dump(new PARENT);
19+
var_dump(STATIC::$x);
2020
}
2121
}
2222
$t = new B();
2323
$t->testit();
24-
var_dump($t);
2524
?>
26-
--EXPECTF--
27-
object(B)#%d (4) {
28-
["v1"]=>
29-
object(A)#%d (0) {
30-
}
31-
["v2"]=>
32-
object(A)#%d (0) {
33-
}
34-
["v3"]=>
35-
object(B)#%d (0) {
36-
}
37-
["v4"]=>
38-
string(1) "B"
25+
--EXPECT--
26+
object(A)#2 (0) {
3927
}
28+
object(A)#2 (0) {
29+
}
30+
object(B)#2 (0) {
31+
}
32+
object(A)#2 (0) {
33+
}
34+
string(1) "B"

Zend/tests/bug63462.phpt

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ Test script to verify that magic methods should be called only once when accessi
44
Marco Pivetta <[email protected]>
55
--FILE--
66
<?php
7+
#[AllowDynamicProperties]
78
class Test {
89
public $publicProperty;
910
protected $protectedProperty;

Zend/tests/bug64821.1.phpt

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ Bug #64821 Custom Exceptions crash when internal properties overridden (variatio
33
--FILE--
44
<?php
55

6+
#[AllowDynamicProperties]
67
class a extends exception {
78
public function __construct() {
89
$this->message = NULL;

Zend/tests/bug64960.phpt

+4
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ $a['waa'];
3131
--EXPECTF--
3232
Notice: ob_end_flush(): Failed to delete and flush buffer. No buffer to delete or flush in %sbug64960.php on line 3
3333

34+
Deprecated: Creation of dynamic property Exception::$_trace is deprecated in %s on line %d
35+
36+
Deprecated: Creation of dynamic property Exception::$_trace is deprecated in %s on line %d
37+
3438
Fatal error: Uncaught Exception in %sbug64960.php:19
3539
Stack trace:
3640
#0 [internal function]: {closure}(8, 'ob_end_clean():...', '%s', 9)

Zend/tests/bug65911.phpt

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ class A {}
66

77
class B
88
{
9+
public $foo;
910
public function go()
1011
{
1112
$this->foo = 'bar';

Zend/tests/bug66609.phpt

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ class Bar {
1010
return $foo->foo;
1111
}
1212
}
13+
#[AllowDynamicProperties]
1314
class Foo {
1415
public function __get($x) {
1516
global $bar;

Zend/tests/bug69446.phpt

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ zend.enable_gc = 1
55
--FILE--
66
<?php
77
$bar = NULL;
8+
#[AllowDynamicProperties]
89
class bad {
910
public function __destruct() {
1011
global $bar;

Zend/tests/bug70223.phpt

+3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ Bug #70223 (Incrementing value returned by magic getter)
33
--FILE--
44
<?php
55

6+
// Note that this actually writes to dynamic property A::$f.
7+
// Increment goes through __set(), not __get() by reference!
8+
#[AllowDynamicProperties]
69
class A {
710

811
private $foo = 0;

Zend/tests/bug70397.phpt

+3-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ $f = function () {
88
yield $this->value;
99
};
1010

11-
var_dump($f->call(new class {})->current());
11+
var_dump($f->call(new class {
12+
public $value;
13+
})->current());
1214

1315
?>
1416
--EXPECT--

Zend/tests/bug70805.phpt

+2
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@ Bug #70805 (Segmentation faults whilst running Drupal 8 test suite)
33
--FILE--
44
<?php
55
class A {
6+
public $b;
67
}
78

89
class B {
10+
public $a;
911
}
1012

1113
class C {

Zend/tests/bug70805_1.phpt

+2
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@ zend.enable_gc = 1
55
--FILE--
66
<?php
77
class A {
8+
public $b;
89
}
910

1011
class B {
12+
public $a;
1113
}
1214

1315
class C {

Zend/tests/bug70805_2.phpt

+2
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@ zend.enable_gc = 1
55
--FILE--
66
<?php
77
class A {
8+
public $b;
89
}
910

1011
class B {
12+
public $a;
1113
}
1214

1315
class C {

Zend/tests/bug71859.phpt

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ Bug #71859 (zend_objects_store_call_destructors operates on realloced memory, cr
33
--FILE--
44
<?php
55
class constructs_in_destructor {
6+
public $a;
67
public function __destruct() {
78
//We are now in zend_objects_store_call_destructors
89
//This causes a realloc in zend_objects_store_put

Zend/tests/bug72101.phpt

+7-6
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ class PHPUnit_Framework_MockObject_InvocationMocker {
2626

2727
class PHPUnit_Framework_MockObject_Matcher {
2828
public $stub = null;
29+
public $methodNameMatcher;
2930
public function invoked($invocation) {
3031
return $this->stub->invoke($invocation);
3132
}
@@ -77,10 +78,10 @@ $foo->bar($a, $b, $c);
7778
--EXPECTF--
7879
Fatal error: Uncaught Error: Class "DoesNotExists" not found in %s:%d
7980
Stack trace:
80-
#0 %sbug72101.php(8): {closure}(2, 'MethodCallbackB...', '%s', 8)
81-
#1 %sbug72101.php(27): PHPUnit_Framework_MockObject_Stub_ReturnCallback->invoke(Object(PHPUnit_Framework_MockObject_Invocation_Static))
82-
#2 %sbug72101.php(19): PHPUnit_Framework_MockObject_Matcher->invoked(Object(PHPUnit_Framework_MockObject_Invocation_Static))
83-
#3 %sbug72101.php(52): PHPUnit_Framework_MockObject_InvocationMocker->invoke(Object(PHPUnit_Framework_MockObject_Invocation_Static))
84-
#4 %sbug72101.php(72): Mock_MethodCallbackByReference_7b180d26->bar(0, 0, 0)
81+
#0 %sbug72101.php(%d): {closure}(2, 'MethodCallbackB...', '%s', 8)
82+
#1 %sbug72101.php(%d): PHPUnit_Framework_MockObject_Stub_ReturnCallback->invoke(Object(PHPUnit_Framework_MockObject_Invocation_Static))
83+
#2 %sbug72101.php(%d): PHPUnit_Framework_MockObject_Matcher->invoked(Object(PHPUnit_Framework_MockObject_Invocation_Static))
84+
#3 %sbug72101.php(%d): PHPUnit_Framework_MockObject_InvocationMocker->invoke(Object(PHPUnit_Framework_MockObject_Invocation_Static))
85+
#4 %sbug72101.php(%d): Mock_MethodCallbackByReference_7b180d26->bar(0, 0, 0)
8586
#5 {main}
86-
thrown in %sbug72101.php on line 61
87+
thrown in %sbug72101.php on line %d

Zend/tests/bug78010.phpt

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ memory_limit=2G
55
--FILE--
66
<?php
77

8+
#[AllowDynamicProperties]
89
class foo
910
{
1011
public function __construct()

Zend/tests/bug78340.phpt

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@ Bug #78340: Include of stream wrapper not reading whole file
44
<?php
55

66
class lib {
7+
public $context;
78
public static $files= [];
89

9-
private $bytes, $pos;
10+
private $bytes, $pos, $ino;
1011

1112
function stream_open($path, $mode, $options, $opened_path) {
1213
$this->bytes= self::$files[$path];

0 commit comments

Comments
 (0)