Skip to content

Commit c320450

Browse files
committed
Use property guard instead of custom write_property logic
Although now there is a memory leak?
1 parent 79211f6 commit c320450

8 files changed

+26
-149
lines changed

ext/spl/spl_array.c

Lines changed: 16 additions & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -460,130 +460,6 @@ static uint32_t spl_array_set_refcount(bool is_child, HashTable *ht, uint32_t re
460460
return old_refcount;
461461
} /* }}} */
462462

463-
/* For reasons ArrayObject must not go through the overloaded __set()/__get()/etc.
464-
* magic methods, so to emit a warning for dynamic props (or Error on classes
465-
* that do not support them, such as readonly classes) or an error for incompatible
466-
* typed properties, we need to reimplement the logic of zend_std_write_property()
467-
* somewhat. Thanks SPL. */
468-
static zval *zend_ignore_set_magic_method_write_property(
469-
HashTable *derived_properties_table,
470-
zend_object *zobj,
471-
zend_string *property_name,
472-
zval *value
473-
) {
474-
ZEND_ASSERT(!Z_ISREF_P(value));
475-
476-
zend_class_entry *ce = zobj->ce;
477-
zval *zv;
478-
if (UNEXPECTED(zend_hash_num_elements(&ce->properties_info) == 0)
479-
|| UNEXPECTED((zv = zend_hash_find(&ce->properties_info, property_name)) == NULL)) {
480-
/* Dynamic prop handling */
481-
dynamic_prop:
482-
/* TODO Modifying private/protected props?
483-
if (UNEXPECTED(ZSTR_VAL(member)[0] == '\0') && ZSTR_LEN(member) != 0) {
484-
zend_bad_property_name();
485-
return NULL;
486-
}
487-
*/
488-
// Dynamic props are not allowed on this class
489-
if (ce->ce_flags & ZEND_ACC_NO_DYNAMIC_PROPERTIES) {
490-
zend_throw_error(NULL, "Cannot create dynamic property %s::$%s", ZSTR_VAL(ce->name), ZSTR_VAL(property_name));
491-
return NULL;
492-
}
493-
if (UNEXPECTED(!(ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES))) {
494-
GC_ADDREF(zobj);
495-
zend_error(E_DEPRECATED, "Creation of dynamic property %s::$%s is deprecated", ZSTR_VAL(ce->name), ZSTR_VAL(property_name));
496-
if (UNEXPECTED(GC_DELREF(zobj) == 0)) {
497-
zend_objects_store_del(zobj);
498-
if (!EG(exception)) {
499-
/* We cannot continue execution and have to throw an exception */
500-
zend_throw_error(NULL, "Cannot create dynamic property %s::$%s", ZSTR_VAL(ce->name), ZSTR_VAL(property_name));
501-
}
502-
return NULL;
503-
}
504-
}
505-
/* Create/Update dynamic prop */
506-
zend_hash_update_ind(derived_properties_table, property_name, value);
507-
return value;
508-
}
509-
510-
const zend_property_info *property_info = (zend_property_info*)Z_PTR_P(zv);
511-
uint32_t prop_flags = property_info->flags;
512-
513-
if (prop_flags & (ZEND_ACC_CHANGED|ZEND_ACC_PROTECTED|ZEND_ACC_PRIVATE)) {
514-
zend_throw_error(NULL, "Cannot access %s property %s::$%s", zend_visibility_string(prop_flags), ZSTR_VAL(ce->name), ZSTR_VAL(property_name));
515-
return NULL;
516-
}
517-
if (UNEXPECTED(prop_flags & ZEND_ACC_STATIC)) {
518-
zend_error(E_NOTICE, "Accessing static property %s::$%s as non static", ZSTR_VAL(ce->name), ZSTR_VAL(property_name));
519-
/* Handle like dynamic prop */
520-
goto dynamic_prop;
521-
}
522-
523-
uintptr_t property_offset = property_info->offset;
524-
zval *variable_ptr = OBJ_PROP(zobj, property_offset);
525-
526-
// Property is already set, check readonly violation
527-
if (Z_TYPE_P(variable_ptr) != IS_UNDEF) {
528-
if (UNEXPECTED((prop_flags & ZEND_ACC_READONLY) && !(Z_PROP_FLAG_P(variable_ptr) & IS_PROP_REINITABLE))) {
529-
Z_TRY_DELREF_P(value);
530-
zend_readonly_property_modification_error(property_info);
531-
return NULL;
532-
}
533-
}
534-
535-
/* Remove fact that property can be reinitialized */
536-
Z_PROP_FLAG_P(variable_ptr) &= ~IS_PROP_REINITABLE;
537-
Z_TRY_ADDREF_P(value);
538-
/* Check value is of valid type if property has type */
539-
if (UNEXPECTED(ZEND_TYPE_IS_SET(property_info->type))) {
540-
zval tmp;
541-
ZVAL_COPY_VALUE(&tmp, value);
542-
// Increase refcount to prevent object from being released in __toString()
543-
GC_ADDREF(zobj);
544-
/* Engine never uses strict types */
545-
bool type_matched = zend_verify_property_type(property_info, &tmp, /* strict_types */ false);
546-
if (UNEXPECTED(GC_DELREF(zobj) == 0)) {
547-
zend_object_released_while_assigning_to_property_error(property_info);
548-
zend_objects_store_del(zobj);
549-
zval_ptr_dtor(&tmp);
550-
variable_ptr = &EG(error_zval);
551-
return NULL;
552-
}
553-
if (UNEXPECTED(!type_matched)) {
554-
zval_ptr_dtor(value);
555-
return NULL;
556-
}
557-
value = &tmp;
558-
Z_PROP_FLAG_P(variable_ptr) = 0;
559-
}
560-
561-
zend_refcounted *garbage = NULL;
562-
/* Engine never uses strict types */
563-
variable_ptr = zend_assign_to_variable_ex(variable_ptr, value, IS_TMP_VAR, /* strict_types */ false, &garbage);
564-
if (garbage) {
565-
if (GC_DELREF(garbage) == 0) {
566-
zend_execute_data *execute_data = EG(current_execute_data);
567-
// Assign to result variable before calling the destructor as it may release the object
568-
if (execute_data
569-
&& EX(func)
570-
&& ZEND_USER_CODE(EX(func)->common.type)
571-
&& EX(opline)
572-
&& EX(opline)->opcode == ZEND_ASSIGN_OBJ
573-
&& EX(opline)->result_type
574-
) {
575-
ZVAL_COPY_DEREF(EX_VAR(EX(opline)->result.var), variable_ptr);
576-
variable_ptr = NULL;
577-
}
578-
rc_dtor_func(garbage);
579-
} else {
580-
gc_check_possible_root_no_ref(garbage);
581-
}
582-
}
583-
//ZVAL_COPY_VALUE(variable_ptr, value);
584-
return variable_ptr;
585-
}
586-
587463
static void spl_array_write_dimension_ex(int check_inherited, zend_object *object, zval *offset, zval *value) /* {{{ */
588464
{
589465
spl_array_object *intern = spl_array_from_obj(object);
@@ -620,11 +496,8 @@ static void spl_array_write_dimension_ex(int check_inherited, zend_object *objec
620496
return;
621497
}
622498

623-
Z_TRY_ADDREF_P(value);
624-
625499
if (get_hash_key(&key, intern, offset) == FAILURE) {
626500
zend_illegal_container_offset(object->ce->name, offset, BP_VAR_W);
627-
zval_ptr_dtor(value);
628501
return;
629502
}
630503

@@ -637,14 +510,27 @@ static void spl_array_write_dimension_ex(int check_inherited, zend_object *objec
637510
/* For reasons ArrayObject must not go through the overloaded __set()/__get()/etc.
638511
* magic methods, so to emit a warning for dynamic props (or Error on classes
639512
* that do not support them, such as readonly classes) or an error for incompatible
640-
* typed properties, we need to reimplement the logic of zend_std_write_property()
641-
* somewhat. Thanks SPL. */
642-
zend_ignore_set_magic_method_write_property(ht, obj, key.key, value);
513+
* typed properties, we set a property guard so that when calling the write_property
514+
* handler it does not call the magic __set() method. Thanks SPL. */
515+
if (obj->ce->ce_flags & ZEND_ACC_USE_GUARDS) {
516+
uint32_t *guard = zend_get_property_guard(obj, key.key);
517+
uint32_t backup = *guard;
518+
(*guard) |= ZEND_GUARD_PROPERTY_SET;
519+
obj->handlers->write_property(obj, key.key, value, /* cache_slot */ NULL);
520+
*guard = backup;
521+
} else {
522+
/* No overload method is defined on the object */
523+
obj->handlers->write_property(obj, key.key, value, /* cache_slot */ NULL);
524+
}
643525
} else {
526+
/* Increase refcount for value before adding to the Hashtable */
527+
Z_TRY_ADDREF_P(value);
644528
zend_hash_update_ind(ht, key.key, value);
645529
}
646530
spl_hash_key_release(&key);
647531
} else {
532+
/* Increase refcount for value before adding to the Hashtable */
533+
Z_TRY_ADDREF_P(value);
648534
ZEND_ASSERT(!spl_array_is_object(intern));
649535
zend_hash_index_update(ht, key.h, value);
650536
}

ext/spl/tests/ArrayObject/appending_object.phpt

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

1919
try {
20-
$ao->append('no-key');
20+
$ao[] = 'no-key';
2121
var_dump($c);
2222
} catch (\Throwable $e) {
2323
echo $e::class, ': ', $e->getMessage(), PHP_EOL;

ext/spl/tests/arrayObject_clone_basic3.phpt

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ var_dump($wrappedObject, $innerArrayObject, $outerArrayObject, $clonedOuterArray
2525
?>
2626
--EXPECTF--
2727
Deprecated: Creation of dynamic property ArrayObject::$new.oAO is deprecated in %s on line %d
28-
object(C)#1 (5) {
28+
object(C)#1 (4) {
2929
["p"]=>
3030
string(9) "C::p.orig"
3131
["dynamic1"]=>
@@ -34,12 +34,12 @@ object(C)#1 (5) {
3434
string(44) "new prop added to $wrappedObject after clone"
3535
["new.iAO"]=>
3636
string(35) "new element added $innerArrayObject"
37+
}
38+
object(ArrayObject)#2 (2) {
3739
["new.oAO"]=>
3840
string(38) "new element added to $outerArrayObject"
39-
}
40-
object(ArrayObject)#2 (1) {
4141
["storage":"ArrayObject":private]=>
42-
object(C)#1 (5) {
42+
object(C)#1 (4) {
4343
["p"]=>
4444
string(9) "C::p.orig"
4545
["dynamic1"]=>
@@ -48,15 +48,15 @@ object(ArrayObject)#2 (1) {
4848
string(44) "new prop added to $wrappedObject after clone"
4949
["new.iAO"]=>
5050
string(35) "new element added $innerArrayObject"
51-
["new.oAO"]=>
52-
string(38) "new element added to $outerArrayObject"
5351
}
5452
}
5553
object(ArrayObject)#3 (1) {
5654
["storage":"ArrayObject":private]=>
57-
object(ArrayObject)#2 (1) {
55+
object(ArrayObject)#2 (2) {
56+
["new.oAO"]=>
57+
string(38) "new element added to $outerArrayObject"
5858
["storage":"ArrayObject":private]=>
59-
object(C)#1 (5) {
59+
object(C)#1 (4) {
6060
["p"]=>
6161
string(9) "C::p.orig"
6262
["dynamic1"]=>
@@ -65,8 +65,6 @@ object(ArrayObject)#3 (1) {
6565
string(44) "new prop added to $wrappedObject after clone"
6666
["new.iAO"]=>
6767
string(35) "new element added $innerArrayObject"
68-
["new.oAO"]=>
69-
string(38) "new element added to $outerArrayObject"
7068
}
7169
}
7270
}

ext/spl/tests/arrayObject_magicMethods1.phpt

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,6 @@ var_dump($ao);
6666
--EXPECTF--
6767
--> Write existent, non-existent and dynamic:
6868

69-
Deprecated: Creation of dynamic property UsesMagic::$dynamic is deprecated in %s on line %d
70-
7169
Deprecated: Creation of dynamic property UsesMagic::$dynamic is deprecated in %s on line %d
7270
Original wrapped object:
7371
object(UsesMagic)#1 (5) {

ext/spl/tests/arrayObject_magicMethods3.phpt

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,6 @@ var_dump($ao);
7070
--EXPECTF--
7171
--> Write existent, non-existent and dynamic:
7272

73-
Deprecated: Creation of dynamic property UsesMagic::$dynamic is deprecated in %s on line %d
74-
7573
Deprecated: Creation of dynamic property UsesMagic::$dynamic is deprecated in %s on line %d
7674
Original wrapped object:
7775
object(UsesMagic)#1 (5) {

ext/spl/tests/arrayObject_magicMethods4.phpt

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,6 @@ var_dump($ao);
7373
--EXPECTF--
7474
--> Write existent, non-existent and dynamic:
7575

76-
Deprecated: Creation of dynamic property C::$dynamic is deprecated in %s on line %d
77-
7876
Deprecated: Creation of dynamic property C::$dynamic is deprecated in %s on line %d
7977
Original wrapped object:
8078
object(C)#1 (5) {

ext/spl/tests/arrayObject_magicMethods6.phpt

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,6 @@ var_dump($ao);
7373
--EXPECTF--
7474
--> Write existent, non-existent and dynamic:
7575

76-
Deprecated: Creation of dynamic property C::$dynamic is deprecated in %s on line %d
77-
7876
Deprecated: Creation of dynamic property C::$dynamic is deprecated in %s on line %d
7977
Original wrapped object:
8078
object(C)#1 (5) {

ext/standard/tests/class_object/get_object_vars_variation_005.phpt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ get_object_vars() no-declared/declared discrepancies
33
--FILE--
44
<?php
55

6+
#[AllowDynamicProperties]
67
class Test {
78
public $prop;
89
}

0 commit comments

Comments
 (0)