Skip to content

Commit dbf7d82

Browse files
committed
Allow references to typed properties during unserialization
Still needs more work to make the assignment successful in more cases.
1 parent 401ff4c commit dbf7d82

File tree

2 files changed

+48
-17
lines changed

2 files changed

+48
-17
lines changed

ext/standard/tests/serialize/typed_property_refs.phpt

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,38 @@ class B {
1313
public int $b;
1414
}
1515

16+
class C {
17+
public int $a;
18+
public string $b;
19+
}
20+
1621
var_dump(unserialize('O:1:"A":2:{s:1:"a";i:1;s:1:"b";R:2;}'));
1722
var_dump(unserialize('O:1:"B":2:{s:1:"a";i:1;s:1:"b";R:2;}'));
23+
var_dump(unserialize('O:1:"A":2:{s:1:"a";N;s:1:"b";R:2;}'));
24+
var_dump(unserialize('O:1:"B":2:{s:1:"a";N;s:1:"b";R:2;}'));
25+
var_dump(unserialize('O:1:"C":2:{s:1:"a";i:1;s:1:"b";R:2;}'));
26+
var_dump(unserialize('O:1:"C":2:{s:1:"b";s:1:"x";s:1:"a";R:2;}'));
1827

1928
?>
2029
--EXPECTF--
2130
object(A)#1 (2) {
2231
["a"]=>
23-
int(1)
32+
&int(1)
2433
["b"]=>
25-
int(1)
34+
&int(1)
2635
}
2736

2837
Notice: unserialize(): Error at offset 35 of 36 bytes in %s on line %d
2938
bool(false)
39+
40+
Notice: unserialize(): Error at offset 21 of 34 bytes in %s on line %d
41+
bool(false)
42+
43+
Notice: unserialize(): Error at offset 33 of 34 bytes in %s on line %d
44+
bool(false)
45+
46+
Notice: unserialize(): Error at offset 35 of 36 bytes in %s on line %d
47+
bool(false)
48+
49+
Notice: unserialize(): Error at offset 39 of 40 bytes in %s on line %d
50+
bool(false)

ext/standard/var_unserializer.re

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ struct php_unserialize_data {
4646
var_dtor_entries *first_dtor;
4747
var_dtor_entries *last_dtor;
4848
HashTable *allowed_classes;
49-
HashTable *refs;
49+
HashTable *ref_props;
5050
var_entries entries;
5151
};
5252

@@ -58,7 +58,7 @@ PHPAPI php_unserialize_data_t php_var_unserialize_init() {
5858
d->last = &d->entries;
5959
d->first_dtor = d->last_dtor = NULL;
6060
d->allowed_classes = NULL;
61-
d->refs = NULL;
61+
d->ref_props = NULL;
6262
d->entries.used_slots = 0;
6363
d->entries.next = NULL;
6464
if (!BG(serialize_lock)) {
@@ -242,8 +242,8 @@ PHPAPI void var_destroy(php_unserialize_data_t *var_hashx)
242242

243243
zval_ptr_dtor_nogc(&wakeup_name);
244244

245-
if ((*var_hashx)->refs) {
246-
zend_array_destroy((*var_hashx)->refs);
245+
if ((*var_hashx)->ref_props) {
246+
zend_array_destroy((*var_hashx)->ref_props);
247247
}
248248
}
249249

@@ -497,6 +497,7 @@ string_key:
497497

498498
if ((old_data = zend_hash_find(ht, Z_STR(key))) != NULL) {
499499
if (Z_TYPE_P(old_data) == IS_INDIRECT) {
500+
// TODO Deduplicate with above code?
500501
old_data = Z_INDIRECT_P(old_data);
501502
if (Z_STRVAL(key)[0] == 0) {
502503
zend_string *member, *class;
@@ -515,18 +516,19 @@ string_key:
515516
info = zend_get_property_info(ce, Z_STR(key), 1);
516517
}
517518
var_push_dtor(var_hash, old_data);
518-
old_data = zend_hash_update_ind(ht, Z_STR(key), &d);
519+
data = zend_hash_update_ind(ht, Z_STR(key), &d);
519520

520521
if (EXPECTED(!info->type)) {
521522
info = NULL;
522-
data = old_data;
523523
} else {
524-
/* little hack to disallow references */
525-
if (!(*var_hash)->refs) {
526-
(*var_hash)->refs = emalloc(sizeof(HashTable));
527-
zend_hash_init((*var_hash)->refs, 8, NULL, ZVAL_PTR_DTOR, 0);
524+
/* Remember to which property this slot belongs, so we can add a type
525+
* source if it is turned into a reference lateron. */
526+
if (!(*var_hash)->ref_props) {
527+
(*var_hash)->ref_props = emalloc(sizeof(HashTable));
528+
zend_hash_init((*var_hash)->ref_props, 8, NULL, NULL, 0);
528529
}
529-
data = zend_hash_next_index_insert((*var_hash)->refs, &d);
530+
zend_hash_index_update_ptr(
531+
(*var_hash)->ref_props, (zend_uintptr_t) data, info);
530532
}
531533
} else {
532534
var_push_dtor(var_hash, old_data);
@@ -551,6 +553,8 @@ string_key:
551553
}
552554

553555
if (UNEXPECTED(info)) {
556+
// TODO Throw an error instead?
557+
// TODO Handle references correctly
554558
ZVAL_COPY_VALUE(&tmp, data);
555559
if (UNEXPECTED(!zend_verify_property_type(info, &tmp, 1))) {
556560
zval_dtor(&key);
@@ -745,13 +749,19 @@ static int php_var_unserialize_internal(UNSERIALIZE_PARAMETER, int as_key)
745749
return 0;
746750
}
747751
748-
if (Z_ISREF_P(rval_ref)) {
749-
ZVAL_COPY(rval, rval_ref);
750-
} else {
752+
if (!Z_ISREF_P(rval_ref)) {
753+
zend_property_info *info = NULL;
754+
if ((*var_hash)->ref_props) {
755+
info = zend_hash_index_find_ptr((*var_hash)->ref_props, (zend_uintptr_t) rval_ref);
756+
}
751757
ZVAL_NEW_REF(rval_ref, rval_ref);
752-
ZVAL_COPY(rval, rval_ref);
758+
if (info) {
759+
ZEND_REF_ADD_TYPE_SOURCE(Z_REF_P(rval_ref), info);
760+
}
753761
}
754762
763+
ZVAL_COPY(rval, rval_ref);
764+
755765
return 1;
756766
}
757767

0 commit comments

Comments
 (0)