Skip to content

Commit 89e9372

Browse files
committed
Added support for object references in recursive serialize() calls. FR #36424
1 parent de53105 commit 89e9372

File tree

10 files changed

+291
-245
lines changed

10 files changed

+291
-245
lines changed

NEWS

+2
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@
4242
- Added scalar typehinting. (Ilia, Derick)
4343
- Added support for JSON_NUMERIC_CHECK option in json_encode() that converts
4444
numeric strings to integers. (Ilia)
45+
- Added support for object references in recursive serialize() calls. FR #36424.
46+
(Mike)
4547

4648
- default_charset if not specified is now UTF-8 instead of ISO-8859-1. (Rasmus)
4749

ext/spl/spl_array.c

+53-167
Original file line numberDiff line numberDiff line change
@@ -59,23 +59,19 @@ PHPAPI zend_class_entry *spl_ce_RecursiveArrayIterator;
5959
#define SPL_ARRAY_CLONE_MASK 0x0300FFFF
6060

6161
typedef struct _spl_array_object {
62-
zend_object std;
63-
zval *array;
64-
zval *retval;
65-
HashPosition pos;
66-
ulong pos_h;
67-
int ar_flags;
68-
int is_self;
69-
zend_function *fptr_offset_get;
70-
zend_function *fptr_offset_set;
71-
zend_function *fptr_offset_has;
72-
zend_function *fptr_offset_del;
73-
zend_function *fptr_count;
74-
zend_function *fptr_serialize;
75-
zend_function *fptr_unserialize;
76-
zend_class_entry *ce_get_iterator;
77-
php_serialize_data_t *serialize_data;
78-
php_unserialize_data_t *unserialize_data;
62+
zend_object std;
63+
zval *array;
64+
zval *retval;
65+
HashPosition pos;
66+
ulong pos_h;
67+
int ar_flags;
68+
int is_self;
69+
zend_function *fptr_offset_get;
70+
zend_function *fptr_offset_set;
71+
zend_function *fptr_offset_has;
72+
zend_function *fptr_offset_del;
73+
zend_function *fptr_count;
74+
zend_class_entry* ce_get_iterator;
7975
HashTable *debug_info;
8076
} spl_array_object;
8177

@@ -161,8 +157,6 @@ static void spl_array_object_free_storage(void *object TSRMLS_DC)
161157
/* }}} */
162158

163159
zend_object_iterator *spl_array_get_iterator(zend_class_entry *ce, zval *object, int by_ref TSRMLS_DC);
164-
int spl_array_serialize(zval *object, unsigned char **buffer, zend_uint *buf_len, zend_serialize_data *data TSRMLS_DC);
165-
int spl_array_unserialize(zval **object, zend_class_entry *ce, const unsigned char *buf, zend_uint buf_len, zend_unserialize_data *data TSRMLS_DC);
166160

167161
/* {{{ spl_array_object_new_ex */
168162
static zend_object_value spl_array_object_new_ex(zend_class_entry *class_type, spl_array_object **obj, zval *orig, int clone_orig TSRMLS_DC)
@@ -182,8 +176,6 @@ static zend_object_value spl_array_object_new_ex(zend_class_entry *class_type, s
182176
object_properties_init(&intern->std, class_type);
183177

184178
intern->ar_flags = 0;
185-
intern->serialize_data = NULL;
186-
intern->unserialize_data = NULL;
187179
intern->debug_info = NULL;
188180
intern->ce_get_iterator = spl_ce_ArrayIterator;
189181
if (orig) {
@@ -250,14 +242,6 @@ static zend_object_value spl_array_object_new_ex(zend_class_entry *class_type, s
250242
if (intern->fptr_count->common.scope == parent) {
251243
intern->fptr_count = NULL;
252244
}
253-
zend_hash_find(&class_type->function_table, "serialize", sizeof("serialize"), (void **) &intern->fptr_serialize);
254-
if (intern->fptr_serialize->common.scope == parent) {
255-
intern->fptr_serialize = NULL;
256-
}
257-
zend_hash_find(&class_type->function_table, "unserialize", sizeof("unserialize"), (void **) &intern->fptr_unserialize);
258-
if (intern->fptr_unserialize->common.scope == parent) {
259-
intern->fptr_unserialize = NULL;
260-
}
261245
}
262246
/* Cache iterator functions if ArrayIterator or derived. Check current's */
263247
/* cache since only current is always required */
@@ -1567,27 +1551,35 @@ SPL_METHOD(Array, getChildren)
15671551
}
15681552
/* }}} */
15691553

1570-
smart_str spl_array_serialize_helper(spl_array_object *intern, php_serialize_data_t *var_hash_p TSRMLS_DC) { /* {{{ */
1554+
/* {{{ proto string ArrayObject::serialize()
1555+
Serialize the object */
1556+
SPL_METHOD(Array, serialize)
1557+
{
1558+
zval *object = getThis();
1559+
spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
15711560
HashTable *aht = spl_array_get_hash_table(intern, 0 TSRMLS_CC);
15721561
zval members, *pmembers;
1562+
php_serialize_data_t var_hash;
15731563
smart_str buf = {0};
15741564
zval *flags;
15751565

15761566
if (!aht) {
15771567
php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Array was modified outside object and is no longer an array");
1578-
return buf;
1568+
return;
15791569
}
15801570

1571+
PHP_VAR_SERIALIZE_INIT(var_hash);
1572+
15811573
MAKE_STD_ZVAL(flags);
15821574
ZVAL_LONG(flags, (intern->ar_flags & SPL_ARRAY_CLONE_MASK));
15831575

15841576
/* storage */
15851577
smart_str_appendl(&buf, "x:", 2);
1586-
php_var_serialize(&buf, &flags, var_hash_p TSRMLS_CC);
1578+
php_var_serialize(&buf, &flags, &var_hash TSRMLS_CC);
15871579
zval_ptr_dtor(&flags);
15881580

15891581
if (!(intern->ar_flags & SPL_ARRAY_IS_SELF)) {
1590-
php_var_serialize(&buf, &intern->array, var_hash_p TSRMLS_CC);
1582+
php_var_serialize(&buf, &intern->array, &var_hash TSRMLS_CC);
15911583
smart_str_appendc(&buf, ';');
15921584
}
15931585

@@ -1600,34 +1592,10 @@ smart_str spl_array_serialize_helper(spl_array_object *intern, php_serialize_dat
16001592
Z_ARRVAL(members) = intern->std.properties;
16011593
Z_TYPE(members) = IS_ARRAY;
16021594
pmembers = &members;
1603-
php_var_serialize(&buf, &pmembers, var_hash_p TSRMLS_CC); /* finishes the string */
1595+
php_var_serialize(&buf, &pmembers, &var_hash TSRMLS_CC); /* finishes the string */
16041596

16051597
/* done */
1606-
return buf;
1607-
}
1608-
/* }}} */
1609-
1610-
/* {{{ proto string ArrayObject::serialize()
1611-
Serialize the object */
1612-
SPL_METHOD(Array, serialize)
1613-
{
1614-
zval *object = getThis();
1615-
spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
1616-
int was_in_serialize = intern->serialize_data != NULL;
1617-
smart_str buf;
1618-
1619-
if (!was_in_serialize) {
1620-
intern->serialize_data = emalloc(sizeof(php_serialize_data_t));
1621-
PHP_VAR_SERIALIZE_INIT(*intern->serialize_data);
1622-
}
1623-
1624-
buf = spl_array_serialize_helper(intern, intern->serialize_data TSRMLS_CC);
1625-
1626-
if (!was_in_serialize) {
1627-
PHP_VAR_SERIALIZE_DESTROY(*intern->serialize_data);
1628-
efree(intern->serialize_data);
1629-
intern->serialize_data = NULL;
1630-
}
1598+
PHP_VAR_SERIALIZE_DESTROY(var_hash);
16311599

16321600
if (buf.c) {
16331601
RETURN_STRINGL(buf.c, buf.len, 0);
@@ -1636,55 +1604,40 @@ SPL_METHOD(Array, serialize)
16361604
RETURN_NULL();
16371605
} /* }}} */
16381606

1639-
int spl_array_serialize(zval *object, unsigned char **buffer, zend_uint *buf_len, zend_serialize_data *data TSRMLS_DC) /* {{{ */
1607+
/* {{{ proto void ArrayObject::unserialize(string serialized)
1608+
* unserialize the object
1609+
*/
1610+
SPL_METHOD(Array, unserialize)
16401611
{
1641-
spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
1642-
1643-
if (intern->fptr_serialize) {
1644-
int retval;
1645-
php_serialize_data_t *before;
1646-
1647-
before = intern->serialize_data;
1648-
intern->serialize_data = (php_serialize_data_t *)data;
1649-
1650-
retval = zend_user_serialize(object, buffer, buf_len, data TSRMLS_CC);
1651-
1652-
intern->serialize_data = before;
1653-
1654-
return retval;
1655-
} else {
1656-
smart_str buf;
1657-
1658-
buf = spl_array_serialize_helper(intern, (php_serialize_data_t *)data TSRMLS_CC);
1659-
1660-
if (buf.c) {
1661-
*buffer = (unsigned char*)estrndup(buf.c, buf.len);
1662-
*buf_len = buf.len;
1663-
efree(buf.c);
1664-
return SUCCESS;
1665-
} else {
1666-
return FAILURE;
1667-
}
1668-
}
1669-
}
1670-
/* }}} */
1612+
spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(getThis() TSRMLS_CC);
16711613

1672-
void spl_array_unserialize_helper(spl_array_object *intern, const unsigned char *buf, int buf_len, php_unserialize_data_t *var_hash_p TSRMLS_DC) /* {{{ */
1673-
{
1614+
char *buf;
1615+
int buf_len;
16741616
const unsigned char *p, *s;
1617+
php_unserialize_data_t var_hash;
16751618
zval *pmembers, *pflags = NULL;
16761619
long flags;
1620+
1621+
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &buf, &buf_len) == FAILURE) {
1622+
return;
1623+
}
1624+
1625+
if (buf_len == 0) {
1626+
zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Empty serialized string cannot be empty");
1627+
return;
1628+
}
16771629

16781630
/* storage */
1679-
s = p = buf;
1631+
s = p = (const unsigned char*)buf;
1632+
PHP_VAR_UNSERIALIZE_INIT(var_hash);
16801633

16811634
if (*p!= 'x' || *++p != ':') {
16821635
goto outexcept;
16831636
}
16841637
++p;
16851638

16861639
ALLOC_INIT_ZVAL(pflags);
1687-
if (!php_var_unserialize(&pflags, &p, s + buf_len, var_hash_p TSRMLS_CC) || Z_TYPE_P(pflags) != IS_LONG) {
1640+
if (!php_var_unserialize(&pflags, &p, s + buf_len, &var_hash TSRMLS_CC) || Z_TYPE_P(pflags) != IS_LONG) {
16881641
zval_ptr_dtor(&pflags);
16891642
goto outexcept;
16901643
}
@@ -1710,7 +1663,7 @@ void spl_array_unserialize_helper(spl_array_object *intern, const unsigned char
17101663
intern->ar_flags |= flags & SPL_ARRAY_CLONE_MASK;
17111664
zval_ptr_dtor(&intern->array);
17121665
ALLOC_INIT_ZVAL(intern->array);
1713-
if (!php_var_unserialize(&intern->array, &p, s + buf_len, var_hash_p TSRMLS_CC)) {
1666+
if (!php_var_unserialize(&intern->array, &p, s + buf_len, &var_hash TSRMLS_CC)) {
17141667
goto outexcept;
17151668
}
17161669
}
@@ -1726,7 +1679,7 @@ void spl_array_unserialize_helper(spl_array_object *intern, const unsigned char
17261679
++p;
17271680

17281681
ALLOC_INIT_ZVAL(pmembers);
1729-
if (!php_var_unserialize(&pmembers, &p, s + buf_len, var_hash_p TSRMLS_CC)) {
1682+
if (!php_var_unserialize(&pmembers, &p, s + buf_len, &var_hash TSRMLS_CC)) {
17301683
zval_ptr_dtor(&pmembers);
17311684
goto outexcept;
17321685
}
@@ -1739,80 +1692,17 @@ void spl_array_unserialize_helper(spl_array_object *intern, const unsigned char
17391692
zval_ptr_dtor(&pmembers);
17401693

17411694
/* done reading $serialized */
1695+
1696+
PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
17421697
return;
17431698

17441699
outexcept:
1745-
zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Error at offset %ld of %d bytes", (long)((char*)p - (char *)buf), buf_len);
1700+
PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
1701+
zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Error at offset %ld of %d bytes", (long)((char*)p - buf), buf_len);
17461702
return;
17471703

1748-
}
1749-
/* }}} */
1750-
1751-
/* {{{ proto void ArrayObject::unserialize(string serialized)
1752-
Unserialize the object */
1753-
SPL_METHOD(Array, unserialize)
1754-
{
1755-
char *buf;
1756-
int buf_len;
1757-
spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(getThis() TSRMLS_CC);
1758-
int was_in_unserialize = intern->unserialize_data != NULL;
1759-
1760-
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &buf, &buf_len) == FAILURE) {
1761-
return;
1762-
}
1763-
1764-
if (buf_len == 0) {
1765-
zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Empty serialized string cannot be empty");
1766-
return;
1767-
}
1768-
1769-
if (!was_in_unserialize) {
1770-
intern->unserialize_data = emalloc(sizeof(php_unserialize_data_t));
1771-
PHP_VAR_UNSERIALIZE_INIT(*intern->unserialize_data);
1772-
}
1773-
1774-
spl_array_unserialize_helper(intern, (const unsigned char *)buf, buf_len, intern->unserialize_data TSRMLS_CC);
1775-
1776-
if (!was_in_unserialize) {
1777-
PHP_VAR_UNSERIALIZE_DESTROY(*intern->unserialize_data);
1778-
efree(intern->unserialize_data);
1779-
intern->unserialize_data = NULL;
1780-
}
17811704
} /* }}} */
17821705

1783-
int spl_array_unserialize(zval **object, zend_class_entry *ce, const unsigned char *buf, zend_uint buf_len, zend_unserialize_data *data TSRMLS_DC)
1784-
{
1785-
spl_array_object *intern;
1786-
1787-
object_init_ex(*object, ce);
1788-
intern = (spl_array_object*)zend_object_store_get_object(*object TSRMLS_CC);
1789-
1790-
if (intern->fptr_unserialize) {
1791-
zval *zdata;
1792-
php_unserialize_data_t *before;
1793-
MAKE_STD_ZVAL(zdata);
1794-
ZVAL_STRINGL(zdata, (char *)buf, buf_len, 1);
1795-
1796-
before = intern->unserialize_data;
1797-
intern->unserialize_data = (php_unserialize_data_t *)data;
1798-
1799-
zend_call_method_with_1_params(object, ce, &ce->unserialize_func, "unserialize", NULL, zdata);
1800-
1801-
intern->unserialize_data = before;
1802-
1803-
zval_ptr_dtor(&zdata);
1804-
} else {
1805-
spl_array_unserialize_helper(intern, buf, buf_len, (php_unserialize_data_t *)data TSRMLS_CC);
1806-
}
1807-
1808-
if (EG(exception)) {
1809-
return FAILURE;
1810-
} else {
1811-
return SUCCESS;
1812-
}
1813-
}
1814-
/* }}} */
1815-
18161706
/* {{{ arginfo and function tbale */
18171707
ZEND_BEGIN_ARG_INFO(arginfo_array___construct, 0)
18181708
ZEND_ARG_INFO(0, array)
@@ -1928,8 +1818,6 @@ PHP_MINIT_FUNCTION(spl_array)
19281818
REGISTER_SPL_IMPLEMENTS(ArrayObject, Aggregate);
19291819
REGISTER_SPL_IMPLEMENTS(ArrayObject, ArrayAccess);
19301820
REGISTER_SPL_IMPLEMENTS(ArrayObject, Serializable);
1931-
spl_ce_ArrayObject->serialize = spl_array_serialize;
1932-
spl_ce_ArrayObject->unserialize = spl_array_unserialize;
19331821
memcpy(&spl_handler_ArrayObject, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
19341822

19351823
spl_handler_ArrayObject.clone_obj = spl_array_object_clone;
@@ -1952,8 +1840,6 @@ PHP_MINIT_FUNCTION(spl_array)
19521840
REGISTER_SPL_IMPLEMENTS(ArrayIterator, ArrayAccess);
19531841
REGISTER_SPL_IMPLEMENTS(ArrayIterator, SeekableIterator);
19541842
REGISTER_SPL_IMPLEMENTS(ArrayIterator, Serializable);
1955-
spl_ce_ArrayIterator->serialize = spl_array_serialize;
1956-
spl_ce_ArrayIterator->unserialize = spl_array_unserialize;
19571843
memcpy(&spl_handler_ArrayIterator, &spl_handler_ArrayObject, sizeof(zend_object_handlers));
19581844
spl_ce_ArrayIterator->get_iterator = spl_array_get_iterator;
19591845

ext/spl/spl_observer.c

+7-5
Original file line numberDiff line numberDiff line change
@@ -638,17 +638,19 @@ SPL_METHOD(SplObjectStorage, serialize)
638638
spl_SplObjectStorage *intern = (spl_SplObjectStorage*)zend_object_store_get_object(getThis() TSRMLS_CC);
639639

640640
spl_SplObjectStorageElement *element;
641-
zval members, *pmembers;
641+
zval members, *pmembers, *flags;
642642
HashPosition pos;
643643
php_serialize_data_t var_hash;
644644
smart_str buf = {0};
645645

646646
PHP_VAR_SERIALIZE_INIT(var_hash);
647647

648648
/* storage */
649-
smart_str_appendl(&buf, "x:i:", 4);
650-
smart_str_append_long(&buf, zend_hash_num_elements(&intern->storage));
651-
smart_str_appendc(&buf, ';');
649+
smart_str_appendl(&buf, "x:", 2);
650+
MAKE_STD_ZVAL(flags);
651+
ZVAL_LONG(flags, zend_hash_num_elements(&intern->storage));
652+
php_var_serialize(&buf, &flags, &var_hash TSRMLS_CC);
653+
zval_ptr_dtor(&flags);
652654

653655
zend_hash_internal_pointer_reset_ex(&intern->storage, &pos);
654656

@@ -716,7 +718,7 @@ SPL_METHOD(SplObjectStorage, unserialize)
716718
++p;
717719

718720
ALLOC_INIT_ZVAL(pcount);
719-
if (!php_var_unserialize(&pcount, &p, s + buf_len, NULL TSRMLS_CC) || Z_TYPE_P(pcount) != IS_LONG) {
721+
if (!php_var_unserialize(&pcount, &p, s + buf_len, &var_hash TSRMLS_CC) || Z_TYPE_P(pcount) != IS_LONG) {
720722
zval_ptr_dtor(&pcount);
721723
goto outexcept;
722724
}

ext/spl/tests/bug49263.phpt

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ var_dump(unserialize($ss));
1919
?>
2020
===DONE===
2121
--EXPECTF--
22-
C:16:"SplObjectStorage":113:{x:i:2;O:8:"stdClass":0:{},a:2:{s:4:"prev";i:2;s:4:"next";O:8:"stdClass":0:{}};r:4;,a:1:{s:4:"prev";r:1;};m:a:0:{}}
22+
C:16:"SplObjectStorage":113:{x:i:2;O:8:"stdClass":0:{},a:2:{s:4:"prev";i:2;s:4:"next";O:8:"stdClass":0:{}};r:6;,a:1:{s:4:"prev";r:3;};m:a:0:{}}
2323
object(SplObjectStorage)#2 (1) {
2424
["storage":"SplObjectStorage":private]=>
2525
array(2) {

0 commit comments

Comments
 (0)