@@ -652,9 +652,13 @@ PHP_FUNCTION(var_export)
652
652
}
653
653
/* }}} */
654
654
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 unique , bool subtract_rc );
656
656
657
- static inline zend_long php_add_var_hash (php_serialize_data_t data , zval * var ) /* {{{ */
657
+ /**
658
+ * @param bool unique Whether the element can only appear once in the serialized data. It can appear
659
+ * multiple times if it is embedded in an array with RC > 1.
660
+ */
661
+ static inline zend_long php_add_var_hash (php_serialize_data_t data , zval * var , bool unique ) /* {{{ */
658
662
{
659
663
zval * zv ;
660
664
zend_ulong key ;
@@ -666,6 +670,10 @@ static inline zend_long php_add_var_hash(php_serialize_data_t data, zval *var) /
666
670
/* pass */
667
671
} else if (Z_TYPE_P (var ) != IS_OBJECT ) {
668
672
return 0 ;
673
+ } else if (unique
674
+ && Z_REFCOUNT_P (var ) == 1
675
+ && (Z_OBJ_P (var )-> properties == NULL || GC_REFCOUNT (Z_OBJ_P (var )-> properties ) == 1 )) {
676
+ return 0 ;
669
677
}
670
678
671
679
/* References to objects are treated as if the reference didn't exist */
@@ -921,7 +929,7 @@ static int php_var_serialize_get_sleep_props(
921
929
}
922
930
/* }}} */
923
931
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 ) /* {{{ */
932
+ 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 unique ) /* {{{ */
925
933
{
926
934
smart_str_append_unsigned (buf , count );
927
935
smart_str_appendl (buf , ":{" , 2 );
@@ -951,19 +959,19 @@ static void php_var_serialize_nested_data(smart_str *buf, zval *struc, HashTable
951
959
if (Z_TYPE_P (data ) == IS_ARRAY ) {
952
960
if (UNEXPECTED (Z_IS_RECURSIVE_P (data ))
953
961
|| UNEXPECTED (Z_TYPE_P (struc ) == IS_ARRAY && Z_ARR_P (data ) == Z_ARR_P (struc ))) {
954
- php_add_var_hash (var_hash , struc );
962
+ php_add_var_hash (var_hash , struc , unique );
955
963
smart_str_appendl (buf , "N;" , 2 );
956
964
} else {
957
965
if (Z_REFCOUNTED_P (data )) {
958
966
Z_PROTECT_RECURSION_P (data );
959
967
}
960
- php_var_serialize_intern (buf , data , var_hash );
968
+ php_var_serialize_intern (buf , data , var_hash , unique , false );
961
969
if (Z_REFCOUNTED_P (data )) {
962
970
Z_UNPROTECT_RECURSION_P (data );
963
971
}
964
972
}
965
973
} else {
966
- php_var_serialize_intern (buf , data , var_hash );
974
+ php_var_serialize_intern (buf , data , var_hash , unique , false );
967
975
}
968
976
} ZEND_HASH_FOREACH_END ();
969
977
}
@@ -978,13 +986,13 @@ static void php_var_serialize_class(smart_str *buf, zval *struc, HashTable *ht,
978
986
if (php_var_serialize_get_sleep_props (& props , struc , ht ) == SUCCESS ) {
979
987
php_var_serialize_class_name (buf , struc );
980
988
php_var_serialize_nested_data (
981
- buf , struc , & props , zend_hash_num_elements (& props ), /* incomplete_class */ 0 , var_hash );
989
+ buf , struc , & props , zend_hash_num_elements (& props ), /* incomplete_class */ 0 , var_hash , GC_REFCOUNT ( & props ) == 1 );
982
990
}
983
991
zend_hash_destroy (& props );
984
992
}
985
993
/* }}} */
986
994
987
- static void php_var_serialize_intern (smart_str * buf , zval * struc , php_serialize_data_t var_hash ) /* {{{ */
995
+ static void php_var_serialize_intern (smart_str * buf , zval * struc , php_serialize_data_t var_hash , bool unique , bool subtract_rc ) /* {{{ */
988
996
{
989
997
zend_long var_already ;
990
998
HashTable * myht ;
@@ -993,7 +1001,7 @@ static void php_var_serialize_intern(smart_str *buf, zval *struc, php_serialize_
993
1001
return ;
994
1002
}
995
1003
996
- if (var_hash && (var_already = php_add_var_hash (var_hash , struc ))) {
1004
+ if (var_hash && (var_already = php_add_var_hash (var_hash , struc , unique ))) {
997
1005
if (var_already == -1 ) {
998
1006
/* Reference to an object that failed to serialize, replace with null. */
999
1007
smart_str_appendl (buf , "N;" , 2 );
@@ -1102,7 +1110,7 @@ static void php_var_serialize_intern(smart_str *buf, zval *struc, php_serialize_
1102
1110
if (Z_ISREF_P (data ) && Z_REFCOUNT_P (data ) == 1 ) {
1103
1111
data = Z_REFVAL_P (data );
1104
1112
}
1105
- php_var_serialize_intern (buf , data , var_hash );
1113
+ php_var_serialize_intern (buf , data , var_hash , Z_REFCOUNT_P ( & retval ) == 1 , false );
1106
1114
} ZEND_HASH_FOREACH_END ();
1107
1115
smart_str_appendc (buf , '}' );
1108
1116
@@ -1224,7 +1232,7 @@ static void php_var_serialize_intern(smart_str *buf, zval *struc, php_serialize_
1224
1232
prop = Z_REFVAL_P (prop );
1225
1233
}
1226
1234
1227
- php_var_serialize_intern (buf , prop , var_hash );
1235
+ php_var_serialize_intern (buf , prop , var_hash , true, false );
1228
1236
}
1229
1237
smart_str_appendc (buf , '}' );
1230
1238
} else {
@@ -1239,15 +1247,15 @@ static void php_var_serialize_intern(smart_str *buf, zval *struc, php_serialize_
1239
1247
if (count > 0 && incomplete_class ) {
1240
1248
-- count ;
1241
1249
}
1242
- php_var_serialize_nested_data (buf , struc , myht , count , incomplete_class , var_hash );
1250
+ php_var_serialize_nested_data (buf , struc , myht , count , incomplete_class , var_hash , GC_REFCOUNT ( myht ) == 1 );
1243
1251
zend_release_properties (myht );
1244
1252
return ;
1245
1253
}
1246
1254
case IS_ARRAY :
1247
1255
smart_str_appendl (buf , "a:" , 2 );
1248
1256
myht = Z_ARRVAL_P (struc );
1249
1257
php_var_serialize_nested_data (
1250
- buf , struc , myht , zend_array_count (myht ), /* incomplete_class */ 0 , var_hash );
1258
+ buf , struc , myht , zend_array_count (myht ), /* incomplete_class */ 0 , var_hash , unique && ( GC_REFCOUNT ( myht ) - ( subtract_rc ? 1 : 0 )) == 1 );
1251
1259
return ;
1252
1260
case IS_REFERENCE :
1253
1261
struc = Z_REFVAL_P (struc );
@@ -1261,11 +1269,17 @@ static void php_var_serialize_intern(smart_str *buf, zval *struc, php_serialize_
1261
1269
1262
1270
PHPAPI void php_var_serialize (smart_str * buf , zval * struc , php_serialize_data_t * data ) /* {{{ */
1263
1271
{
1264
- php_var_serialize_intern (buf , struc , * data );
1272
+ php_var_serialize_intern (buf , struc , * data , true, false );
1265
1273
smart_str_0 (buf );
1266
1274
}
1267
1275
/* }}} */
1268
1276
1277
+ static void php_var_serialize_cv (smart_str * buf , zval * struc , php_serialize_data_t * data )
1278
+ {
1279
+ php_var_serialize_intern (buf , struc , * data , true, true);
1280
+ smart_str_0 (buf );
1281
+ }
1282
+
1269
1283
PHPAPI php_serialize_data_t php_var_serialize_init (void ) {
1270
1284
struct php_serialize_data * d ;
1271
1285
/* fprintf(stderr, "SERIALIZE_INIT == lock: %u, level: %u\n", BG(serialize_lock), BG(serialize).level); */
@@ -1306,8 +1320,30 @@ PHP_FUNCTION(serialize)
1306
1320
Z_PARAM_ZVAL (struc )
1307
1321
ZEND_PARSE_PARAMETERS_END ();
1308
1322
1323
+ bool data_is_cv = false;
1324
+ if (Z_TYPE_P (struc ) == IS_ARRAY
1325
+ && !(GC_FLAGS (Z_ARRVAL_P (struc )) & IS_ARRAY_IMMUTABLE )
1326
+ && EG (current_execute_data )
1327
+ && EG (current_execute_data )-> prev_execute_data ) {
1328
+ zend_execute_data * execute_data = EG (current_execute_data )-> prev_execute_data ;
1329
+ if (execute_data -> func && ZEND_USER_CODE (execute_data -> func -> type )) {
1330
+ zend_op_array * func = & execute_data -> func -> op_array ;
1331
+ const zend_op * opline = execute_data -> opline ;
1332
+ if (func -> opcodes < opline ) {
1333
+ const zend_op * prev_opline = opline - 1 ;
1334
+ if ((prev_opline -> opcode == ZEND_SEND_VAR || prev_opline -> opcode == ZEND_SEND_VAR_EX ) && prev_opline -> op1_type == IS_CV ) {
1335
+ data_is_cv = true;
1336
+ }
1337
+ }
1338
+ }
1339
+ }
1340
+
1309
1341
PHP_VAR_SERIALIZE_INIT (var_hash );
1310
- php_var_serialize (& buf , struc , & var_hash );
1342
+ if (data_is_cv ) {
1343
+ php_var_serialize_cv (& buf , struc , & var_hash );
1344
+ } else {
1345
+ php_var_serialize (& buf , struc , & var_hash );
1346
+ }
1311
1347
PHP_VAR_SERIALIZE_DESTROY (var_hash );
1312
1348
1313
1349
if (EG (exception )) {
0 commit comments