Skip to content

Commit 501e55d

Browse files
Fix GH-14402: Add support for serialization in SplPriorityQueue, SplMinHeap and SplMaxHeap
1 parent b57578f commit 501e55d

11 files changed

+555
-1
lines changed

NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,8 @@ PHP NEWS
149149
. Fix RCN violations in array functions. (nielsdos)
150150
. Fixed GH-18976 pack() overflow with h/H format and INT_MAX repeater value.
151151
(David Carlier)
152+
. Fixed GH-14402 (SplPriorityQueue, SplMinHeap, and SplMaxHeap lost their
153+
data on serialize()). (alexandre-daubois)
152154

153155
- Streams:
154156
. Fixed GH-13264 (fgets() and stream_get_line() do not return false on filter

ext/spl/spl_heap.c

Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1087,6 +1087,201 @@ PHP_METHOD(SplPriorityQueue, __debugInfo)
10871087
RETURN_ARR(spl_heap_object_get_debug_info(spl_ce_SplPriorityQueue, Z_OBJ_P(ZEND_THIS)));
10881088
} /* }}} */
10891089

1090+
static void spl_heap_serialize_properties(zval *return_value, spl_heap_object *intern)
1091+
{
1092+
zval *prop;
1093+
zend_string *key;
1094+
zend_ulong h;
1095+
HashTable *props = zend_std_get_properties(&intern->std);
1096+
1097+
if (!props || zend_hash_num_elements(props) == 0) {
1098+
return;
1099+
}
1100+
1101+
ZEND_HASH_FOREACH_KEY_VAL_IND(props, h, key, prop) {
1102+
if (key) {
1103+
add_assoc_zval(return_value, ZSTR_VAL(key), prop);
1104+
} else {
1105+
add_index_zval(return_value, h, prop);
1106+
}
1107+
Z_TRY_ADDREF_P(prop);
1108+
} ZEND_HASH_FOREACH_END();
1109+
}
1110+
1111+
static void spl_heap_serialize_flags(zval *return_value, spl_heap_object *intern)
1112+
{
1113+
zval tmp;
1114+
ZVAL_LONG(&tmp, intern->flags);
1115+
add_assoc_zval(return_value, "__flags", &tmp);
1116+
}
1117+
1118+
static void spl_heap_unserialize_properties(HashTable *data, spl_heap_object *intern)
1119+
{
1120+
zval *val;
1121+
zend_string *key;
1122+
zend_ulong h;
1123+
1124+
ZEND_HASH_FOREACH_KEY_VAL_IND(data, h, key, val) {
1125+
if (key && !zend_string_equals_literal(key, "__heap_elements") && !zend_string_equals_literal(key, "__flags")) {
1126+
zend_hash_update(zend_std_get_properties(&intern->std), key, val);
1127+
Z_TRY_ADDREF_P(val);
1128+
} else if (!key && h != 0) {
1129+
zend_hash_index_update(zend_std_get_properties(&intern->std), h, val);
1130+
Z_TRY_ADDREF_P(val);
1131+
}
1132+
} ZEND_HASH_FOREACH_END();
1133+
}
1134+
1135+
static void spl_heap_unserialize_flags(HashTable *data, spl_heap_object *intern)
1136+
{
1137+
zval *flags_val = zend_hash_str_find(data, "__flags", sizeof("__flags") - 1);
1138+
if (flags_val == NULL) {
1139+
return;
1140+
}
1141+
1142+
intern->flags = (int) zval_get_long(flags_val);
1143+
}
1144+
1145+
static void spl_pqueue_serialize_elements(zval *return_value, spl_heap_object *intern)
1146+
{
1147+
zval heap_elements;
1148+
int heap_idx;
1149+
1150+
if (intern->heap->count == 0) {
1151+
return;
1152+
}
1153+
1154+
array_init_size(&heap_elements, intern->heap->count);
1155+
1156+
for (heap_idx = 0; heap_idx < intern->heap->count; ++heap_idx) {
1157+
spl_pqueue_elem *elem = spl_heap_elem(intern->heap, heap_idx);
1158+
zval entry;
1159+
1160+
array_init(&entry);
1161+
add_assoc_zval_ex(&entry, "data", sizeof("data") - 1, &elem->data);
1162+
Z_TRY_ADDREF(elem->data);
1163+
add_assoc_zval_ex(&entry, "priority", sizeof("priority") - 1, &elem->priority);
1164+
Z_TRY_ADDREF(elem->priority);
1165+
1166+
zend_hash_next_index_insert(Z_ARRVAL(heap_elements), &entry);
1167+
}
1168+
1169+
add_assoc_zval(return_value, "__heap_elements", &heap_elements);
1170+
}
1171+
1172+
static void spl_pqueue_unserialize_elements(HashTable *data, spl_heap_object *intern, zval *this_ptr)
1173+
{
1174+
zval *heap_elements = zend_hash_str_find(data, "__heap_elements", sizeof("__heap_elements") - 1);
1175+
zval *val;
1176+
1177+
if (heap_elements == NULL || Z_TYPE_P(heap_elements) != IS_ARRAY) {
1178+
return;
1179+
}
1180+
1181+
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(heap_elements), val) {
1182+
if (Z_TYPE_P(val) == IS_ARRAY) {
1183+
zval *data_val = zend_hash_str_find(Z_ARRVAL_P(val), "data", sizeof("data") - 1);
1184+
zval *priority_val = zend_hash_str_find(Z_ARRVAL_P(val), "priority", sizeof("priority") - 1);
1185+
1186+
if (data_val && priority_val) {
1187+
spl_pqueue_elem elem;
1188+
ZVAL_COPY(&elem.data, data_val);
1189+
ZVAL_COPY(&elem.priority, priority_val);
1190+
spl_ptr_heap_insert(intern->heap, &elem, this_ptr);
1191+
}
1192+
}
1193+
} ZEND_HASH_FOREACH_END();
1194+
}
1195+
1196+
static void spl_heap_serialize_elements(zval *return_value, spl_heap_object *intern)
1197+
{
1198+
zval heap_elements;
1199+
int heap_idx;
1200+
1201+
if (intern->heap->count == 0) {
1202+
return;
1203+
}
1204+
1205+
array_init_size(&heap_elements, intern->heap->count);
1206+
1207+
for (heap_idx = 0; heap_idx < intern->heap->count; ++heap_idx) {
1208+
zval *elem = spl_heap_elem(intern->heap, heap_idx);
1209+
zend_hash_next_index_insert(Z_ARRVAL(heap_elements), elem);
1210+
Z_TRY_ADDREF_P(elem);
1211+
}
1212+
1213+
add_assoc_zval(return_value, "__heap_elements", &heap_elements);
1214+
}
1215+
1216+
static void spl_heap_unserialize_elements(HashTable *data, spl_heap_object *intern, zval *this_ptr)
1217+
{
1218+
zval *val;
1219+
zval *heap_elements = zend_hash_str_find(data, "__heap_elements", sizeof("__heap_elements") - 1);
1220+
1221+
if (heap_elements == NULL || Z_TYPE_P(heap_elements) != IS_ARRAY) {
1222+
return;
1223+
}
1224+
1225+
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(heap_elements), val) {
1226+
Z_TRY_ADDREF_P(val);
1227+
spl_ptr_heap_insert(intern->heap, val, this_ptr);
1228+
} ZEND_HASH_FOREACH_END();
1229+
}
1230+
1231+
PHP_METHOD(SplPriorityQueue, __serialize)
1232+
{
1233+
spl_heap_object *intern = Z_SPLHEAP_P(ZEND_THIS);
1234+
1235+
ZEND_PARSE_PARAMETERS_NONE();
1236+
1237+
array_init(return_value);
1238+
1239+
spl_pqueue_serialize_elements(return_value, intern);
1240+
spl_heap_serialize_flags(return_value, intern);
1241+
spl_heap_serialize_properties(return_value, intern);
1242+
}
1243+
1244+
PHP_METHOD(SplPriorityQueue, __unserialize)
1245+
{
1246+
HashTable *data;
1247+
spl_heap_object *intern = Z_SPLHEAP_P(ZEND_THIS);
1248+
1249+
ZEND_PARSE_PARAMETERS_START(1, 1)
1250+
Z_PARAM_ARRAY_HT(data)
1251+
ZEND_PARSE_PARAMETERS_END();
1252+
1253+
spl_heap_unserialize_flags(data, intern);
1254+
spl_pqueue_unserialize_elements(data, intern, ZEND_THIS);
1255+
spl_heap_unserialize_properties(data, intern);
1256+
}
1257+
1258+
PHP_METHOD(SplHeap, __serialize)
1259+
{
1260+
spl_heap_object *intern = Z_SPLHEAP_P(ZEND_THIS);
1261+
1262+
ZEND_PARSE_PARAMETERS_NONE();
1263+
1264+
array_init(return_value);
1265+
1266+
spl_heap_serialize_elements(return_value, intern);
1267+
spl_heap_serialize_flags(return_value, intern);
1268+
spl_heap_serialize_properties(return_value, intern);
1269+
}
1270+
1271+
PHP_METHOD(SplHeap, __unserialize)
1272+
{
1273+
HashTable *data;
1274+
spl_heap_object *intern = Z_SPLHEAP_P(ZEND_THIS);
1275+
1276+
ZEND_PARSE_PARAMETERS_START(1, 1)
1277+
Z_PARAM_ARRAY_HT(data)
1278+
ZEND_PARSE_PARAMETERS_END();
1279+
1280+
spl_heap_unserialize_flags(data, intern);
1281+
spl_heap_unserialize_elements(data, intern, ZEND_THIS);
1282+
spl_heap_unserialize_properties(data, intern);
1283+
}
1284+
10901285
/* iterator handler table */
10911286
static const zend_object_iterator_funcs spl_heap_it_funcs = {
10921287
spl_heap_it_dtor,

ext/spl/spl_heap.stub.php

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

9292
/** @tentative-return-type */
9393
public function __debugInfo(): array {}
94+
95+
/** @tentative-return-type */
96+
public function __serialize(): array {}
97+
98+
/** @tentative-return-type */
99+
public function __unserialize(array $data): void {}
94100
}
95101

96102
abstract class SplHeap implements Iterator, Countable
@@ -136,6 +142,12 @@ public function isCorrupted(): bool {}
136142

137143
/** @tentative-return-type */
138144
public function __debugInfo(): array {}
145+
146+
/** @tentative-return-type */
147+
public function __serialize(): array {}
148+
149+
/** @tentative-return-type */
150+
public function __unserialize(array $data): void {}
139151
}
140152

141153
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.
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
--TEST--
2+
SplMaxHeap serialization and unserialization
3+
--FILE--
4+
<?php
5+
$heap = new SplMaxHeap();
6+
$heap->insert(50);
7+
$heap->insert(20);
8+
$heap->insert(80);
9+
$heap->insert(10);
10+
$heap->insert(30);
11+
12+
echo "Original heap count: " . count($heap) . "\n";
13+
14+
$temp = clone $heap;
15+
$extracted = array();
16+
while (!$temp->isEmpty()) {
17+
$extracted[] = $temp->extract();
18+
}
19+
echo "Original extraction order: " . implode(', ', $extracted) . "\n";
20+
21+
$serialized = serialize($heap);
22+
$unserialized = unserialize($serialized);
23+
24+
echo "Unserialized heap count: " . count($unserialized) . "\n";
25+
26+
$extracted = array();
27+
while (!$unserialized->isEmpty()) {
28+
$extracted[] = $unserialized->extract();
29+
}
30+
echo "Unserialized extraction order: " . implode(', ', $extracted) . "\n";
31+
32+
?>
33+
--EXPECT--
34+
Original heap count: 5
35+
Original extraction order: 80, 50, 30, 20, 10
36+
Unserialized heap count: 5
37+
Unserialized extraction order: 80, 50, 30, 20, 10
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
--TEST--
2+
SplMaxHeap serialization with arrays and complex data
3+
--FILE--
4+
<?php
5+
$heap = new SplMaxHeap();
6+
7+
$heap->insert(100);
8+
$heap->insert(50);
9+
$heap->insert(75);
10+
11+
echo "Original heap count: " . count($heap) . "\n";
12+
13+
$serialized = serialize($heap);
14+
$unserialized = unserialize($serialized);
15+
16+
echo "Unserialized heap count: " . count($unserialized) . "\n";
17+
18+
$extracted = [];
19+
while (!$unserialized->isEmpty()) {
20+
$extracted[] = $unserialized->extract();
21+
}
22+
echo "Extraction order: " . implode(', ', $extracted) . "\n";
23+
24+
$heap2 = new SplMaxHeap();
25+
$heap2->insert(['type' => 'array1', 'value' => 10]);
26+
$heap2->insert(['type' => 'array2', 'value' => 20]);
27+
$heap2->insert(['type' => 'array3', 'value' => 5]);
28+
29+
echo "\nArray heap count: " . count($heap2) . "\n";
30+
31+
$serialized2 = serialize($heap2);
32+
$unserialized2 = unserialize($serialized2);
33+
34+
echo "Unserialized array heap count: " . count($unserialized2) . "\n";
35+
36+
while (!$unserialized2->isEmpty()) {
37+
$item = $unserialized2->extract();
38+
echo "Array: " . json_encode($item) . "\n";
39+
}
40+
41+
?>
42+
--EXPECT--
43+
Original heap count: 3
44+
Unserialized heap count: 3
45+
Extraction order: 100, 75, 50
46+
47+
Array heap count: 3
48+
Unserialized array heap count: 3
49+
Array: {"type":"array3","value":5}
50+
Array: {"type":"array2","value":20}
51+
Array: {"type":"array1","value":10}

0 commit comments

Comments
 (0)