Skip to content

Commit e374a03

Browse files
committed
Skip serialize object recording when array paths are rc1
1 parent b2b7519 commit e374a03

File tree

2 files changed

+57
-24
lines changed

2 files changed

+57
-24
lines changed

ext/standard/tests/serialize/serialization_objects_019.phpt

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,41 @@
22
Object serialization with references
33
--FILE--
44
<?php
5-
function gen() {
6-
$s = new stdClass;
7-
$r = new stdClass;
8-
$r->a = [$s];
9-
$r->b = $r->a;
10-
return $r;
5+
6+
function rcn() {
7+
$root = new stdClass;
8+
$end = new stdClass;
9+
$root->a = [$end];
10+
$root->b = $root->a;
11+
unset($end);
12+
echo serialize($root), "\n";
1113
}
12-
var_dump(serialize(gen()));
13-
?>
14-
--EXPECTF--
15-
string(78) "O:8:"stdClass":2:{s:1:"a";a:1:{i:0;O:8:"stdClass":0:{}}s:1:"b";a:1:{i:0;r:3;}}"
1614

15+
function rcn_rc1() {
16+
$root = new stdClass;
17+
$end = new stdClass;
18+
$root->a = [[$end]];
19+
$root->b = $root->a;
20+
unset($end);
21+
echo serialize($root), "\n";
22+
}
1723

24+
function rcn_properties_ht() {
25+
$object = new stdClass;
26+
$object->object = new stdClass;
27+
$array = (array) $object;
28+
$root = [$object, $array];
29+
unset($object);
30+
unset($array);
31+
echo serialize($root), "\n";
32+
}
33+
34+
rcn();
35+
rcn_rc1();
36+
rcn_properties_ht();
37+
38+
?>
39+
--EXPECT--
40+
O:8:"stdClass":2:{s:1:"a";a:1:{i:0;O:8:"stdClass":0:{}}s:1:"b";a:1:{i:0;r:3;}}
41+
O:8:"stdClass":2:{s:1:"a";a:1:{i:0;a:1:{i:0;O:8:"stdClass":0:{}}}s:1:"b";a:1:{i:0;a:1:{i:0;r:4;}}}
42+
a:2:{i:0;O:8:"stdClass":1:{s:6:"object";O:8:"stdClass":0:{}}i:1;a:1:{s:6:"object";r:3;}}

ext/standard/var.c

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -652,9 +652,12 @@ PHP_FUNCTION(var_export)
652652
}
653653
/* }}} */
654654

655-
static void php_var_serialize_intern(smart_str *buf, zval *struc, php_serialize_data_t var_hash);
655+
static void php_var_serialize_intern(smart_str *buf, zval *struc, php_serialize_data_t var_hash, bool in_rcn_array, bool is_root);
656656

657-
static inline zend_long php_add_var_hash(php_serialize_data_t data, zval *var) /* {{{ */
657+
/**
658+
* @param bool in_rcn_array Whether the element appears in a potentially nested array with RC > 1.
659+
*/
660+
static inline zend_long php_add_var_hash(php_serialize_data_t data, zval *var, bool in_rcn_array) /* {{{ */
658661
{
659662
zval *zv;
660663
zend_ulong key;
@@ -666,6 +669,10 @@ static inline zend_long php_add_var_hash(php_serialize_data_t data, zval *var) /
666669
/* pass */
667670
} else if (Z_TYPE_P(var) != IS_OBJECT) {
668671
return 0;
672+
} else if (!in_rcn_array
673+
&& Z_REFCOUNT_P(var) == 1
674+
&& (Z_OBJ_P(var)->properties == NULL || GC_REFCOUNT(Z_OBJ_P(var)->properties) == 1)) {
675+
return 0;
669676
}
670677

671678
/* References to objects are treated as if the reference didn't exist */
@@ -921,7 +928,7 @@ static int php_var_serialize_get_sleep_props(
921928
}
922929
/* }}} */
923930

924-
static void php_var_serialize_nested_data(smart_str *buf, zval *struc, HashTable *ht, uint32_t count, bool incomplete_class, php_serialize_data_t var_hash) /* {{{ */
931+
static void php_var_serialize_nested_data(smart_str *buf, zval *struc, HashTable *ht, uint32_t count, bool incomplete_class, php_serialize_data_t var_hash, bool in_rcn_array) /* {{{ */
925932
{
926933
smart_str_append_unsigned(buf, count);
927934
smart_str_appendl(buf, ":{", 2);
@@ -951,19 +958,19 @@ static void php_var_serialize_nested_data(smart_str *buf, zval *struc, HashTable
951958
if (Z_TYPE_P(data) == IS_ARRAY) {
952959
if (UNEXPECTED(Z_IS_RECURSIVE_P(data))
953960
|| UNEXPECTED(Z_TYPE_P(struc) == IS_ARRAY && Z_ARR_P(data) == Z_ARR_P(struc))) {
954-
php_add_var_hash(var_hash, struc);
961+
php_add_var_hash(var_hash, struc, in_rcn_array);
955962
smart_str_appendl(buf, "N;", 2);
956963
} else {
957964
if (Z_REFCOUNTED_P(data)) {
958965
Z_PROTECT_RECURSION_P(data);
959966
}
960-
php_var_serialize_intern(buf, data, var_hash);
967+
php_var_serialize_intern(buf, data, var_hash, in_rcn_array, false);
961968
if (Z_REFCOUNTED_P(data)) {
962969
Z_UNPROTECT_RECURSION_P(data);
963970
}
964971
}
965972
} else {
966-
php_var_serialize_intern(buf, data, var_hash);
973+
php_var_serialize_intern(buf, data, var_hash, in_rcn_array, false);
967974
}
968975
} ZEND_HASH_FOREACH_END();
969976
}
@@ -978,13 +985,13 @@ static void php_var_serialize_class(smart_str *buf, zval *struc, HashTable *ht,
978985
if (php_var_serialize_get_sleep_props(&props, struc, ht) == SUCCESS) {
979986
php_var_serialize_class_name(buf, struc);
980987
php_var_serialize_nested_data(
981-
buf, struc, &props, zend_hash_num_elements(&props), /* incomplete_class */ 0, var_hash);
988+
buf, struc, &props, zend_hash_num_elements(&props), /* incomplete_class */ 0, var_hash, GC_REFCOUNT(&props) > 1);
982989
}
983990
zend_hash_destroy(&props);
984991
}
985992
/* }}} */
986993

987-
static void php_var_serialize_intern(smart_str *buf, zval *struc, php_serialize_data_t var_hash) /* {{{ */
994+
static void php_var_serialize_intern(smart_str *buf, zval *struc, php_serialize_data_t var_hash, bool in_rcn_array, bool is_root) /* {{{ */
988995
{
989996
zend_long var_already;
990997
HashTable *myht;
@@ -993,7 +1000,7 @@ static void php_var_serialize_intern(smart_str *buf, zval *struc, php_serialize_
9931000
return;
9941001
}
9951002

996-
if (var_hash && (var_already = php_add_var_hash(var_hash, struc))) {
1003+
if (var_hash && (var_already = php_add_var_hash(var_hash, struc, in_rcn_array))) {
9971004
if (var_already == -1) {
9981005
/* Reference to an object that failed to serialize, replace with null. */
9991006
smart_str_appendl(buf, "N;", 2);
@@ -1102,7 +1109,7 @@ static void php_var_serialize_intern(smart_str *buf, zval *struc, php_serialize_
11021109
if (Z_ISREF_P(data) && Z_REFCOUNT_P(data) == 1) {
11031110
data = Z_REFVAL_P(data);
11041111
}
1105-
php_var_serialize_intern(buf, data, var_hash);
1112+
php_var_serialize_intern(buf, data, var_hash, Z_REFCOUNT(retval) > 1, false);
11061113
} ZEND_HASH_FOREACH_END();
11071114
smart_str_appendc(buf, '}');
11081115

@@ -1224,7 +1231,7 @@ static void php_var_serialize_intern(smart_str *buf, zval *struc, php_serialize_
12241231
prop = Z_REFVAL_P(prop);
12251232
}
12261233

1227-
php_var_serialize_intern(buf, prop, var_hash);
1234+
php_var_serialize_intern(buf, prop, var_hash, false, false);
12281235
}
12291236
smart_str_appendc(buf, '}');
12301237
} else {
@@ -1239,15 +1246,16 @@ static void php_var_serialize_intern(smart_str *buf, zval *struc, php_serialize_
12391246
if (count > 0 && incomplete_class) {
12401247
--count;
12411248
}
1242-
php_var_serialize_nested_data(buf, struc, myht, count, incomplete_class, var_hash);
1249+
php_var_serialize_nested_data(buf, struc, myht, count, incomplete_class, var_hash, GC_REFCOUNT(myht) > 1);
12431250
zend_release_properties(myht);
12441251
return;
12451252
}
12461253
case IS_ARRAY:
12471254
smart_str_appendl(buf, "a:", 2);
12481255
myht = Z_ARRVAL_P(struc);
12491256
php_var_serialize_nested_data(
1250-
buf, struc, myht, zend_array_count(myht), /* incomplete_class */ 0, var_hash);
1257+
buf, struc, myht, zend_array_count(myht), /* incomplete_class */ 0, var_hash,
1258+
!is_root && (in_rcn_array || GC_REFCOUNT(myht) > 1));
12511259
return;
12521260
case IS_REFERENCE:
12531261
struc = Z_REFVAL_P(struc);
@@ -1261,7 +1269,7 @@ static void php_var_serialize_intern(smart_str *buf, zval *struc, php_serialize_
12611269

12621270
PHPAPI void php_var_serialize(smart_str *buf, zval *struc, php_serialize_data_t *data) /* {{{ */
12631271
{
1264-
php_var_serialize_intern(buf, struc, *data);
1272+
php_var_serialize_intern(buf, struc, *data, false, true);
12651273
smart_str_0(buf);
12661274
}
12671275
/* }}} */

0 commit comments

Comments
 (0)