Skip to content

Commit e6c07b0

Browse files
Fix phpGH-14402: Add support for serialization in SplPriorityQueue, SplMinHeap and SplMaxHeap (php#19447)
1 parent 4bc060c commit e6c07b0

13 files changed

+1109
-1
lines changed

NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ PHP NEWS
5454
(Girgias)
5555
. Fixed bug GH-19801 (leaks in var_dump() and debug_zval_dump()).
5656
(alexandre-daubois)
57+
. Fixed GH-14402 (SplPriorityQueue, SplMinHeap, and SplMaxHeap lost their
58+
data on serialize()). (alexandre-daubois)
5759

5860
- URI:
5961
. Fixed bug GH-19780 (InvalidUrlException should check $errors argument).

ext/spl/spl_heap.c

Lines changed: 241 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1070,6 +1070,247 @@ PHP_METHOD(SplPriorityQueue, __debugInfo)
10701070
RETURN_ARR(spl_heap_object_get_debug_info(spl_ce_SplPriorityQueue, Z_OBJ_P(ZEND_THIS)));
10711071
} /* }}} */
10721072

1073+
static void spl_heap_serialize_internal_state(zval *return_value, spl_heap_object *intern, bool is_pqueue)
1074+
{
1075+
zval heap_elements;
1076+
int heap_count = intern->heap->count;
1077+
1078+
array_init(return_value);
1079+
add_assoc_long(return_value, "flags", intern->flags);
1080+
1081+
array_init_size(&heap_elements, heap_count);
1082+
1083+
if (heap_count == 0) {
1084+
add_assoc_zval(return_value, "heap_elements", &heap_elements);
1085+
return;
1086+
}
1087+
1088+
for (int heap_idx = 0; heap_idx < heap_count; ++heap_idx) {
1089+
if (is_pqueue) {
1090+
spl_pqueue_elem *elem = spl_heap_elem(intern->heap, heap_idx);
1091+
zval entry;
1092+
array_init(&entry);
1093+
add_assoc_zval_ex(&entry, "data", strlen("data"), &elem->data);
1094+
Z_TRY_ADDREF(elem->data);
1095+
add_assoc_zval_ex(&entry, "priority", strlen("priority"), &elem->priority);
1096+
Z_TRY_ADDREF(elem->priority);
1097+
zend_hash_next_index_insert(Z_ARRVAL(heap_elements), &entry);
1098+
} else {
1099+
zval *elem = spl_heap_elem(intern->heap, heap_idx);
1100+
zend_hash_next_index_insert(Z_ARRVAL(heap_elements), elem);
1101+
Z_TRY_ADDREF_P(elem);
1102+
}
1103+
}
1104+
1105+
add_assoc_zval(return_value, "heap_elements", &heap_elements);
1106+
}
1107+
1108+
static zend_result spl_heap_unserialize_internal_state(HashTable *state_ht, spl_heap_object *intern, zval *this_ptr, bool is_pqueue)
1109+
{
1110+
zval *flags_val = zend_hash_str_find(state_ht, "flags", strlen("flags"));
1111+
if (!flags_val || Z_TYPE_P(flags_val) != IS_LONG) {
1112+
return FAILURE;
1113+
}
1114+
1115+
zend_long flags_value = Z_LVAL_P(flags_val);
1116+
1117+
if (is_pqueue) {
1118+
flags_value &= SPL_PQUEUE_EXTR_MASK;
1119+
if (!flags_value) {
1120+
return FAILURE;
1121+
}
1122+
} else if (flags_value != 0) { /* Regular heaps should not have user-visible flags */
1123+
return FAILURE;
1124+
}
1125+
1126+
intern->flags = (int) flags_value;
1127+
1128+
zval *heap_elements = zend_hash_str_find(state_ht, "heap_elements", strlen("heap_elements"));
1129+
if (!heap_elements) {
1130+
return FAILURE;
1131+
}
1132+
1133+
if (Z_TYPE_P(heap_elements) != IS_ARRAY) {
1134+
return FAILURE;
1135+
}
1136+
1137+
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(heap_elements), zval *val) {
1138+
if (is_pqueue) {
1139+
/* PriorityQueue elements are serialized as arrays with 'data' and 'priority' keys */
1140+
if (Z_TYPE_P(val) != IS_ARRAY || zend_hash_num_elements(Z_ARRVAL_P(val)) != 2) {
1141+
return FAILURE;
1142+
}
1143+
1144+
zval *data_val = zend_hash_str_find(Z_ARRVAL_P(val), "data", strlen("data") );
1145+
zval *priority_val = zend_hash_str_find(Z_ARRVAL_P(val), "priority", strlen("priority"));
1146+
1147+
if (!data_val || !priority_val) {
1148+
return FAILURE;
1149+
}
1150+
1151+
spl_pqueue_elem elem;
1152+
ZVAL_COPY(&elem.data, data_val);
1153+
ZVAL_COPY(&elem.priority, priority_val);
1154+
spl_ptr_heap_insert(intern->heap, &elem, this_ptr);
1155+
if (EG(exception)) {
1156+
return FAILURE;
1157+
}
1158+
} else {
1159+
Z_TRY_ADDREF_P(val);
1160+
spl_ptr_heap_insert(intern->heap, val, this_ptr);
1161+
if (EG(exception)) {
1162+
return FAILURE;
1163+
}
1164+
}
1165+
} ZEND_HASH_FOREACH_END();
1166+
1167+
return SUCCESS;
1168+
}
1169+
1170+
PHP_METHOD(SplPriorityQueue, __serialize)
1171+
{
1172+
spl_heap_object *intern = Z_SPLHEAP_P(ZEND_THIS);
1173+
zval props, state;
1174+
1175+
ZEND_PARSE_PARAMETERS_NONE();
1176+
1177+
if (UNEXPECTED(spl_heap_consistency_validations(intern, false) != SUCCESS)) {
1178+
RETURN_THROWS();
1179+
}
1180+
1181+
if (intern->heap->flags & SPL_HEAP_WRITE_LOCKED) {
1182+
zend_throw_exception(spl_ce_RuntimeException, "Cannot serialize heap while it is being modified.", 0);
1183+
RETURN_THROWS();
1184+
}
1185+
1186+
array_init(return_value);
1187+
1188+
ZVAL_ARR(&props, zend_std_get_properties(&intern->std));
1189+
Z_TRY_ADDREF(props);
1190+
zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &props);
1191+
1192+
spl_heap_serialize_internal_state(&state, intern, true);
1193+
zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &state);
1194+
}
1195+
1196+
PHP_METHOD(SplPriorityQueue, __unserialize)
1197+
{
1198+
HashTable *data;
1199+
spl_heap_object *intern = Z_SPLHEAP_P(ZEND_THIS);
1200+
1201+
ZEND_PARSE_PARAMETERS_START(1, 1)
1202+
Z_PARAM_ARRAY_HT(data)
1203+
ZEND_PARSE_PARAMETERS_END();
1204+
1205+
if (zend_hash_num_elements(data) != 2) {
1206+
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(intern->std.ce->name));
1207+
RETURN_THROWS();
1208+
}
1209+
1210+
zval *props = zend_hash_index_find(data, 0);
1211+
if (!props || Z_TYPE_P(props) != IS_ARRAY) {
1212+
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(intern->std.ce->name));
1213+
RETURN_THROWS();
1214+
}
1215+
1216+
object_properties_load(&intern->std, Z_ARRVAL_P(props));
1217+
if (EG(exception)) {
1218+
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(intern->std.ce->name));
1219+
RETURN_THROWS();
1220+
}
1221+
1222+
zval *state = zend_hash_index_find(data, 1);
1223+
if (!state || Z_TYPE_P(state) != IS_ARRAY) {
1224+
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(intern->std.ce->name));
1225+
RETURN_THROWS();
1226+
}
1227+
1228+
if (spl_heap_unserialize_internal_state(Z_ARRVAL_P(state), intern, ZEND_THIS, true) != SUCCESS) {
1229+
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(intern->std.ce->name));
1230+
RETURN_THROWS();
1231+
}
1232+
1233+
if (EG(exception)) {
1234+
RETURN_THROWS();
1235+
}
1236+
1237+
if (UNEXPECTED(spl_heap_consistency_validations(intern, false) != SUCCESS)) {
1238+
RETURN_THROWS();
1239+
}
1240+
}
1241+
1242+
PHP_METHOD(SplHeap, __serialize)
1243+
{
1244+
spl_heap_object *intern = Z_SPLHEAP_P(ZEND_THIS);
1245+
zval props, state;
1246+
1247+
ZEND_PARSE_PARAMETERS_NONE();
1248+
1249+
if (UNEXPECTED(spl_heap_consistency_validations(intern, false) != SUCCESS)) {
1250+
RETURN_THROWS();
1251+
}
1252+
1253+
if (intern->heap->flags & SPL_HEAP_WRITE_LOCKED) {
1254+
zend_throw_exception(spl_ce_RuntimeException, "Cannot serialize heap while it is being modified.", 0);
1255+
RETURN_THROWS();
1256+
}
1257+
1258+
array_init(return_value);
1259+
1260+
ZVAL_ARR(&props, zend_std_get_properties(&intern->std));
1261+
Z_TRY_ADDREF(props);
1262+
zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &props);
1263+
1264+
spl_heap_serialize_internal_state(&state, intern, false);
1265+
zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &state);
1266+
}
1267+
1268+
PHP_METHOD(SplHeap, __unserialize)
1269+
{
1270+
HashTable *data;
1271+
spl_heap_object *intern = Z_SPLHEAP_P(ZEND_THIS);
1272+
1273+
ZEND_PARSE_PARAMETERS_START(1, 1)
1274+
Z_PARAM_ARRAY_HT(data)
1275+
ZEND_PARSE_PARAMETERS_END();
1276+
1277+
if (zend_hash_num_elements(data) != 2) {
1278+
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(intern->std.ce->name));
1279+
RETURN_THROWS();
1280+
}
1281+
1282+
zval *props = zend_hash_index_find(data, 0);
1283+
if (!props || Z_TYPE_P(props) != IS_ARRAY) {
1284+
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(intern->std.ce->name));
1285+
RETURN_THROWS();
1286+
}
1287+
1288+
object_properties_load(&intern->std, Z_ARRVAL_P(props));
1289+
if (EG(exception)) {
1290+
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(intern->std.ce->name));
1291+
RETURN_THROWS();
1292+
}
1293+
1294+
zval *state = zend_hash_index_find(data, 1);
1295+
if (!state || Z_TYPE_P(state) != IS_ARRAY) {
1296+
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(intern->std.ce->name));
1297+
RETURN_THROWS();
1298+
}
1299+
1300+
if (spl_heap_unserialize_internal_state(Z_ARRVAL_P(state), intern, ZEND_THIS, false) != SUCCESS) {
1301+
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(intern->std.ce->name));
1302+
RETURN_THROWS();
1303+
}
1304+
1305+
if (EG(exception)) {
1306+
RETURN_THROWS();
1307+
}
1308+
1309+
if (UNEXPECTED(spl_heap_consistency_validations(intern, false) != SUCCESS)) {
1310+
RETURN_THROWS();
1311+
}
1312+
}
1313+
10731314
/* iterator handler table */
10741315
static const zend_object_iterator_funcs spl_heap_it_funcs = {
10751316
spl_heap_it_dtor,

ext/spl/spl_heap.stub.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,12 @@ public function getExtractFlags(): int {}
8282

8383
/** @tentative-return-type */
8484
public function __debugInfo(): array {}
85+
86+
/** @tentative-return-type */
87+
public function __serialize(): array {}
88+
89+
/** @tentative-return-type */
90+
public function __unserialize(array $data): void {}
8591
}
8692

8793
abstract class SplHeap implements Iterator, Countable
@@ -127,6 +133,12 @@ public function isCorrupted(): bool {}
127133

128134
/** @tentative-return-type */
129135
public function __debugInfo(): array {}
136+
137+
/** @tentative-return-type */
138+
public function __serialize(): array {}
139+
140+
/** @tentative-return-type */
141+
public function __unserialize(array $data): void {}
130142
}
131143

132144
class SplMinHeap extends SplHeap

ext/spl/spl_heap_arginfo.h

Lines changed: 19 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)