-
Notifications
You must be signed in to change notification settings - Fork 7.9k
Fix GH-14402: Add support for serialization in SplPriorityQueue, SplMinHeap and SplMaxHeap #19447
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 2 commits
2c9bfbb
2236ca2
5f5c0d9
04bb7cd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||
---|---|---|---|---|---|---|---|---|
|
@@ -1075,6 +1075,221 @@ PHP_METHOD(SplPriorityQueue, __debugInfo) | |||||||
RETURN_ARR(spl_heap_object_get_debug_info(spl_ce_SplPriorityQueue, Z_OBJ_P(ZEND_THIS))); | ||||||||
} /* }}} */ | ||||||||
|
||||||||
static void spl_heap_serialize_properties(zval *return_value, spl_heap_object *intern) | ||||||||
{ | ||||||||
HashTable *props = zend_std_get_properties(&intern->std); | ||||||||
|
||||||||
ZVAL_ARR(return_value, props); | ||||||||
GC_ADDREF(props); | ||||||||
} | ||||||||
|
||||||||
static void spl_heap_serialize_internal_state(zval *return_value, spl_heap_object *intern, bool is_pqueue) | ||||||||
{ | ||||||||
zval heap_elements; | ||||||||
int heap_count = intern->heap->count; | ||||||||
|
||||||||
array_init(return_value); | ||||||||
add_assoc_long(return_value, "flags", intern->flags); | ||||||||
|
||||||||
if (heap_count == 0) { | ||||||||
return; | ||||||||
} | ||||||||
|
||||||||
array_init_size(&heap_elements, heap_count); | ||||||||
|
||||||||
for (int heap_idx = 0; heap_idx < heap_count; ++heap_idx) { | ||||||||
if (is_pqueue) { | ||||||||
spl_pqueue_elem *elem = spl_heap_elem(intern->heap, heap_idx); | ||||||||
zval entry; | ||||||||
array_init(&entry); | ||||||||
add_assoc_zval_ex(&entry, "data", strlen("data"), &elem->data); | ||||||||
Z_TRY_ADDREF(elem->data); | ||||||||
add_assoc_zval_ex(&entry, "priority", strlen("priority"), &elem->priority); | ||||||||
Z_TRY_ADDREF(elem->priority); | ||||||||
zend_hash_next_index_insert(Z_ARRVAL(heap_elements), &entry); | ||||||||
} else { | ||||||||
zval *elem = spl_heap_elem(intern->heap, heap_idx); | ||||||||
zend_hash_next_index_insert(Z_ARRVAL(heap_elements), elem); | ||||||||
Z_TRY_ADDREF_P(elem); | ||||||||
} | ||||||||
} | ||||||||
|
||||||||
add_assoc_zval(return_value, "heap_elements", &heap_elements); | ||||||||
} | ||||||||
|
||||||||
static void spl_heap_unserialize_properties(HashTable *props_ht, spl_heap_object *intern) | ||||||||
{ | ||||||||
object_properties_load(&intern->std, props_ht); | ||||||||
if (!EG(exception)) { | ||||||||
return; | ||||||||
} | ||||||||
|
||||||||
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(intern->std.ce->name)); | ||||||||
} | ||||||||
TimWolla marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||
|
||||||||
static void spl_heap_unserialize_internal_state(HashTable *state_ht, spl_heap_object *intern, zval *this_ptr, bool is_pqueue) | ||||||||
{ | ||||||||
zval *flags_val = zend_hash_str_find(state_ht, "flags", strlen("flags")); | ||||||||
if (!flags_val || Z_TYPE_P(flags_val) != IS_LONG) { | ||||||||
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(intern->std.ce->name)); | ||||||||
return; | ||||||||
} | ||||||||
|
||||||||
zend_long flags_value = Z_LVAL_P(flags_val); | ||||||||
|
||||||||
if (is_pqueue) { | ||||||||
flags_value &= SPL_PQUEUE_EXTR_MASK; | ||||||||
if (!flags_value) { | ||||||||
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(intern->std.ce->name)); | ||||||||
return; | ||||||||
} | ||||||||
} | ||||||||
|
||||||||
intern->flags = (int) flags_value; | ||||||||
|
||||||||
zval *heap_elements = zend_hash_str_find(state_ht, "heap_elements", strlen("heap_elements")); | ||||||||
if (!heap_elements) { | ||||||||
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(intern->std.ce->name)); | ||||||||
return; | ||||||||
TimWolla marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||
} | ||||||||
|
||||||||
if (Z_TYPE_P(heap_elements) != IS_ARRAY) { | ||||||||
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(intern->std.ce->name)); | ||||||||
return; | ||||||||
} | ||||||||
|
||||||||
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(heap_elements), zval *val) { | ||||||||
if (is_pqueue) { | ||||||||
if (Z_TYPE_P(val) != IS_ARRAY) { | ||||||||
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(intern->std.ce->name)); | ||||||||
return; | ||||||||
} | ||||||||
TimWolla marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||
|
||||||||
zval *data_val = zend_hash_str_find(Z_ARRVAL_P(val), "data", strlen("data") ); | ||||||||
zval *priority_val = zend_hash_str_find(Z_ARRVAL_P(val), "priority", strlen("priority")); | ||||||||
|
||||||||
if (!data_val || !priority_val) { | ||||||||
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(intern->std.ce->name)); | ||||||||
return; | ||||||||
} | ||||||||
|
||||||||
spl_pqueue_elem elem; | ||||||||
ZVAL_COPY(&elem.data, data_val); | ||||||||
ZVAL_COPY(&elem.priority, priority_val); | ||||||||
spl_ptr_heap_insert(intern->heap, &elem, this_ptr); | ||||||||
} else { | ||||||||
Z_TRY_ADDREF_P(val); | ||||||||
spl_ptr_heap_insert(intern->heap, val, this_ptr); | ||||||||
} | ||||||||
} ZEND_HASH_FOREACH_END(); | ||||||||
} | ||||||||
|
||||||||
PHP_METHOD(SplPriorityQueue, __serialize) | ||||||||
{ | ||||||||
spl_heap_object *intern = Z_SPLHEAP_P(ZEND_THIS); | ||||||||
zval props, state; | ||||||||
|
||||||||
ZEND_PARSE_PARAMETERS_NONE(); | ||||||||
|
||||||||
array_init(return_value); | ||||||||
|
||||||||
spl_heap_serialize_properties(&props, intern); | ||||||||
zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &props); | ||||||||
|
||||||||
spl_heap_serialize_internal_state(&state, intern, true); | ||||||||
zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &state); | ||||||||
} | ||||||||
|
||||||||
PHP_METHOD(SplPriorityQueue, __unserialize) | ||||||||
{ | ||||||||
HashTable *data; | ||||||||
spl_heap_object *intern = Z_SPLHEAP_P(ZEND_THIS); | ||||||||
|
||||||||
ZEND_PARSE_PARAMETERS_START(1, 1) | ||||||||
Z_PARAM_ARRAY_HT(data) | ||||||||
ZEND_PARSE_PARAMETERS_END(); | ||||||||
|
||||||||
if (zend_hash_num_elements(data) != 2) { | ||||||||
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(intern->std.ce->name)); | ||||||||
RETURN_THROWS(); | ||||||||
} | ||||||||
|
||||||||
zval *props = zend_hash_index_find(data, 0); | ||||||||
if (!props || Z_TYPE_P(props) != IS_ARRAY) { | ||||||||
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(intern->std.ce->name)); | ||||||||
RETURN_THROWS(); | ||||||||
} | ||||||||
|
||||||||
spl_heap_unserialize_properties(Z_ARRVAL_P(props), intern); | ||||||||
if (EG(exception)) { | ||||||||
RETURN_THROWS(); | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For this one you should throw to wrap the exception into the “Invalid serialization data for %s object” one. When throwing while an exception is already active, the engine will automatically set
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In case object_properties_load() sets one in the executor globals, alright I get it. PR updated |
||||||||
} | ||||||||
|
||||||||
zval *state = zend_hash_index_find(data, 1); | ||||||||
if (!state || Z_TYPE_P(state) != IS_ARRAY) { | ||||||||
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(intern->std.ce->name)); | ||||||||
RETURN_THROWS(); | ||||||||
} | ||||||||
|
||||||||
spl_heap_unserialize_internal_state(Z_ARRVAL_P(state), intern, ZEND_THIS, true); | ||||||||
if (EG(exception)) { | ||||||||
RETURN_THROWS(); | ||||||||
} | ||||||||
} | ||||||||
|
||||||||
PHP_METHOD(SplHeap, __serialize) | ||||||||
{ | ||||||||
spl_heap_object *intern = Z_SPLHEAP_P(ZEND_THIS); | ||||||||
zval props, state; | ||||||||
|
||||||||
ZEND_PARSE_PARAMETERS_NONE(); | ||||||||
|
||||||||
array_init(return_value); | ||||||||
|
||||||||
spl_heap_serialize_properties(&props, intern); | ||||||||
zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &props); | ||||||||
|
||||||||
spl_heap_serialize_internal_state(&state, intern, false); | ||||||||
zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &state); | ||||||||
} | ||||||||
|
||||||||
PHP_METHOD(SplHeap, __unserialize) | ||||||||
{ | ||||||||
HashTable *data; | ||||||||
spl_heap_object *intern = Z_SPLHEAP_P(ZEND_THIS); | ||||||||
|
||||||||
ZEND_PARSE_PARAMETERS_START(1, 1) | ||||||||
Z_PARAM_ARRAY_HT(data) | ||||||||
ZEND_PARSE_PARAMETERS_END(); | ||||||||
|
||||||||
if (zend_hash_num_elements(data) != 2) { | ||||||||
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(intern->std.ce->name)); | ||||||||
RETURN_THROWS(); | ||||||||
} | ||||||||
|
||||||||
zval *props = zend_hash_index_find(data, 0); | ||||||||
if (!props || Z_TYPE_P(props) != IS_ARRAY) { | ||||||||
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(intern->std.ce->name)); | ||||||||
RETURN_THROWS(); | ||||||||
} | ||||||||
|
||||||||
spl_heap_unserialize_properties(Z_ARRVAL_P(props), intern); | ||||||||
if (EG(exception)) { | ||||||||
RETURN_THROWS(); | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ditto |
||||||||
} | ||||||||
|
||||||||
zval *state = zend_hash_index_find(data, 1); | ||||||||
if (!state || Z_TYPE_P(state) != IS_ARRAY) { | ||||||||
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(intern->std.ce->name)); | ||||||||
RETURN_THROWS(); | ||||||||
} | ||||||||
|
||||||||
spl_heap_unserialize_internal_state(Z_ARRVAL_P(state), intern, ZEND_THIS, false); | ||||||||
if (EG(exception)) { | ||||||||
RETURN_THROWS(); | ||||||||
} | ||||||||
} | ||||||||
|
||||||||
/* iterator handler table */ | ||||||||
static const zend_object_iterator_funcs spl_heap_it_funcs = { | ||||||||
spl_heap_it_dtor, | ||||||||
|
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
--TEST-- | ||
SplHeap serialization format - indexed array format prevents conflicts | ||
--FILE-- | ||
<?php | ||
|
||
$heap = new SplMaxHeap(); | ||
$heap->insert(100); | ||
$heap->insert(50); | ||
$heap->insert(200); | ||
|
||
$serialized_data = $heap->__serialize(); | ||
|
||
echo "Serialization data structure:\n"; | ||
echo "Is indexed array: " . (isset($serialized_data[0], $serialized_data[1]) && count($serialized_data) === 2 ? 'YES' : 'NO') . "\n"; | ||
echo "Index 0 (properties) is array: " . (is_array($serialized_data[0]) ? 'YES' : 'NO') . "\n"; | ||
echo "Index 1 (internal state) is array: " . (is_array($serialized_data[1]) ? 'YES' : 'NO') . "\n"; | ||
|
||
$internal_state = $serialized_data[1]; | ||
echo "Internal state contains 'flags': " . (array_key_exists('flags', $internal_state) ? 'YES' : 'NO') . "\n"; | ||
echo "Internal state contains 'heap_elements': " . (array_key_exists('heap_elements', $internal_state) ? 'YES' : 'NO') . "\n"; | ||
echo "Flags value is numeric: " . (is_numeric($internal_state['flags']) ? 'YES' : 'NO') . "\n"; | ||
echo "Heap elements is array: " . (is_array($internal_state['heap_elements']) ? 'YES' : 'NO') . "\n"; | ||
echo "Heap elements count: " . count($internal_state['heap_elements']) . "\n"; | ||
|
||
$min_heap = new SplMinHeap(); | ||
$min_heap->insert(100); | ||
$min_heap->insert(50); | ||
|
||
$min_data = $min_heap->__serialize(); | ||
|
||
$pq = new SplPriorityQueue(); | ||
$pq->insert('data', 10); | ||
$pq->setExtractFlags(SplPriorityQueue::EXTR_DATA); | ||
|
||
$pq_data = $pq->__serialize(); | ||
|
||
$pq_internal = $pq_data[1]; | ||
if (isset($pq_internal['heap_elements']) && is_array($pq_internal['heap_elements'])) { | ||
$first_element = $pq_internal['heap_elements'][0]; | ||
echo "PQ elements have 'data' key: " . (array_key_exists('data', $first_element) ? 'YES' : 'NO') . "\n"; | ||
echo "PQ elements have 'priority' key: " . (array_key_exists('priority', $first_element) ? 'YES' : 'NO') . "\n"; | ||
} | ||
|
||
?> | ||
--EXPECT-- | ||
Serialization data structure: | ||
Is indexed array: YES | ||
Index 0 (properties) is array: YES | ||
Index 1 (internal state) is array: YES | ||
Internal state contains 'flags': YES | ||
Internal state contains 'heap_elements': YES | ||
Flags value is numeric: YES | ||
Heap elements is array: YES | ||
Heap elements count: 3 | ||
PQ elements have 'data' key: YES | ||
PQ elements have 'priority' key: YES |
Uh oh!
There was an error while loading. Please reload this page.