Skip to content

Commit caefc6a

Browse files
committed
Don't use custom object handlers for enum properties
Instead mark name/value as readonly and the class as NO_DYNAMIC_PROPERTIES. This gives us the desired limitations using native features. In fact, this also fixes a bug where opcache cache slot merging might result in a write to the name/value properties being allowed. The readonly implementation handles this case correctly.
1 parent 96f9501 commit caefc6a

12 files changed

+45
-46
lines changed

Zend/tests/enum/no-dynamic-properties.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,4 @@ try {
1717

1818
?>
1919
--EXPECT--
20-
Enum properties are immutable
20+
Cannot create dynamic property Foo::$baz

Zend/tests/enum/no-pass-properties-by-ref.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,5 @@ var_dump(Foo::Bar->value);
2222

2323
?>
2424
--EXPECT--
25-
Cannot acquire reference to property Foo::$value
25+
Cannot modify readonly property Foo::$value
2626
int(0)

Zend/tests/enum/no-return-properties-by-ref.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,5 @@ var_dump(Foo::Bar->value);
2323

2424
?>
2525
--EXPECT--
26-
Cannot acquire reference to property Foo::$value
26+
Cannot modify readonly property Foo::$value
2727
int(0)

Zend/tests/enum/no-unset-propertes.phpt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,6 @@ try {
3232

3333
?>
3434
--EXPECT--
35-
Enum properties are immutable
36-
Enum properties are immutable
37-
Enum properties are immutable
35+
Cannot unset readonly property Foo::$name
36+
Cannot unset readonly property IntFoo::$name
37+
Cannot unset readonly property IntFoo::$value
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
--TEST--
2+
Readonly enum properties should not be writable via cache slot merging
3+
--FILE--
4+
<?php
5+
6+
enum Test {
7+
case A;
8+
9+
public function modify() {
10+
// Cache slots for the read and write are merged.
11+
var_dump($this->name);
12+
$this->name = 'foobar';
13+
}
14+
}
15+
16+
try {
17+
Test::A->modify();
18+
} catch (Error $e) {
19+
echo $e->getMessage(), "\n";
20+
}
21+
var_dump(Test::A->name);
22+
23+
?>
24+
--EXPECT--
25+
string(1) "A"
26+
Enum properties are immutable
27+
string(1) "A"

Zend/tests/enum/no-write-properties-through-foreach-reference.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,5 @@ var_dump(Foo::Bar->value);
1818

1919
?>
2020
--EXPECT--
21-
Cannot acquire reference to property Foo::$value
21+
Cannot modify readonly property Foo::$value
2222
int(0)

Zend/tests/enum/no-write-properties-through-references.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,5 @@ var_dump(Foo::Bar->value);
1919

2020
?>
2121
--EXPECT--
22-
Cannot acquire reference to property Foo::$value
22+
Cannot modify readonly property Foo::$value
2323
int(0)

Zend/tests/enum/no-write-properties.phpt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,6 @@ try {
3232

3333
?>
3434
--EXPECT--
35-
Enum properties are immutable
36-
Enum properties are immutable
37-
Enum properties are immutable
35+
Cannot modify readonly property Foo::$name
36+
Cannot modify readonly property Foo::$name
37+
Cannot create dynamic property Foo::$value

Zend/zend_enum.c

Lines changed: 4 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -119,32 +119,6 @@ void zend_verify_enum(zend_class_entry *ce)
119119
zend_verify_enum_interfaces(ce);
120120
}
121121

122-
static zval *zend_enum_read_property(zend_object *zobj, zend_string *name, int type, void **cache_slot, zval *rv) /* {{{ */
123-
{
124-
if (type == BP_VAR_W || type == BP_VAR_RW || type == BP_VAR_UNSET) {
125-
zend_throw_error(NULL, "Cannot acquire reference to property %s::$%s", ZSTR_VAL(zobj->ce->name), ZSTR_VAL(name));
126-
return &EG(uninitialized_zval);
127-
}
128-
129-
return zend_std_read_property(zobj, name, type, cache_slot, rv);
130-
}
131-
132-
static ZEND_COLD zval *zend_enum_write_property(zend_object *object, zend_string *member, zval *value, void **cache_slot)
133-
{
134-
ZEND_ENUM_PROPERTY_ERROR();
135-
return &EG(uninitialized_zval);
136-
}
137-
138-
static ZEND_COLD void zend_enum_unset_property(zend_object *object, zend_string *member, void **cache_slot)
139-
{
140-
ZEND_ENUM_PROPERTY_ERROR();
141-
}
142-
143-
static zval *zend_enum_get_property_ptr_ptr(zend_object *zobj, zend_string *name, int type, void **cache_slot)
144-
{
145-
return NULL;
146-
}
147-
148122
static int zend_implement_unit_enum(zend_class_entry *interface, zend_class_entry *class_type)
149123
{
150124
if (class_type->ce_flags & ZEND_ACC_ENUM) {
@@ -186,10 +160,6 @@ void zend_register_enum_ce(void)
186160
zend_ce_backed_enum->interface_gets_implemented = zend_implement_backed_enum;
187161

188162
memcpy(&enum_handlers, &std_object_handlers, sizeof(zend_object_handlers));
189-
enum_handlers.read_property = zend_enum_read_property;
190-
enum_handlers.write_property = zend_enum_write_property;
191-
enum_handlers.unset_property = zend_enum_unset_property;
192-
enum_handlers.get_property_ptr_ptr = zend_enum_get_property_ptr_ptr;
193163
enum_handlers.clone_obj = NULL;
194164
enum_handlers.compare = zend_objects_not_comparable;
195165
}
@@ -359,16 +329,18 @@ void zend_enum_register_funcs(zend_class_entry *ce)
359329

360330
void zend_enum_register_props(zend_class_entry *ce)
361331
{
332+
ce->ce_flags |= ZEND_ACC_NO_DYNAMIC_PROPERTIES;
333+
362334
zval name_default_value;
363335
ZVAL_UNDEF(&name_default_value);
364336
zend_type name_type = ZEND_TYPE_INIT_CODE(IS_STRING, 0, 0);
365-
zend_declare_typed_property(ce, ZSTR_KNOWN(ZEND_STR_NAME), &name_default_value, ZEND_ACC_PUBLIC, NULL, name_type);
337+
zend_declare_typed_property(ce, ZSTR_KNOWN(ZEND_STR_NAME), &name_default_value, ZEND_ACC_PUBLIC | ZEND_ACC_READONLY, NULL, name_type);
366338

367339
if (ce->enum_backing_type != IS_UNDEF) {
368340
zval value_default_value;
369341
ZVAL_UNDEF(&value_default_value);
370342
zend_type value_type = ZEND_TYPE_INIT_CODE(ce->enum_backing_type, 0, 0);
371-
zend_declare_typed_property(ce, ZSTR_KNOWN(ZEND_STR_VALUE), &value_default_value, ZEND_ACC_PUBLIC, NULL, value_type);
343+
zend_declare_typed_property(ce, ZSTR_KNOWN(ZEND_STR_VALUE), &value_default_value, ZEND_ACC_PUBLIC | ZEND_ACC_READONLY, NULL, value_type);
372344
}
373345
}
374346

ext/reflection/tests/ReflectionEnumUnitCase_getEnum.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ Class [ <user> final class Foo implements UnitEnum ] {
3131
}
3232

3333
- Properties [1] {
34-
Property [ public string $name ]
34+
Property [ public readonly string $name ]
3535
}
3636

3737
- Methods [0] {

ext/reflection/tests/ReflectionEnum_toString.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ Class [ <user> final class Foo implements UnitEnum ] {
3131
}
3232

3333
- Properties [1] {
34-
Property [ public string $name ]
34+
Property [ public readonly string $name ]
3535
}
3636

3737
- Methods [0] {

ext/reflection/tests/ReflectionProperty_setValue_readonly.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,5 @@ var_dump(Foo::Bar->value);
1919

2020
?>
2121
--EXPECT--
22-
Enum properties are immutable
22+
Cannot modify readonly property Foo::$value
2323
int(0)

0 commit comments

Comments
 (0)