1
0
mirror of https://github.com/php/php-src.git synced 2026-03-24 00:02:20 +01:00

Fix GH-14402: Add support for serialization in SplPriorityQueue, SplMinHeap and SplMaxHeap (#19447)

This commit is contained in:
Alexandre Daubois
2025-09-22 09:43:09 +02:00
committed by GitHub
parent 4bc060c0f3
commit e6c07b004b
13 changed files with 1109 additions and 1 deletions

2
NEWS
View File

@@ -54,6 +54,8 @@ PHP NEWS
(Girgias)
. Fixed bug GH-19801 (leaks in var_dump() and debug_zval_dump()).
(alexandre-daubois)
. Fixed GH-14402 (SplPriorityQueue, SplMinHeap, and SplMaxHeap lost their
data on serialize()). (alexandre-daubois)
- URI:
. Fixed bug GH-19780 (InvalidUrlException should check $errors argument).

View File

@@ -1070,6 +1070,247 @@ 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_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);
array_init_size(&heap_elements, heap_count);
if (heap_count == 0) {
add_assoc_zval(return_value, "heap_elements", &heap_elements);
return;
}
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 zend_result 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) {
return FAILURE;
}
zend_long flags_value = Z_LVAL_P(flags_val);
if (is_pqueue) {
flags_value &= SPL_PQUEUE_EXTR_MASK;
if (!flags_value) {
return FAILURE;
}
} else if (flags_value != 0) { /* Regular heaps should not have user-visible flags */
return FAILURE;
}
intern->flags = (int) flags_value;
zval *heap_elements = zend_hash_str_find(state_ht, "heap_elements", strlen("heap_elements"));
if (!heap_elements) {
return FAILURE;
}
if (Z_TYPE_P(heap_elements) != IS_ARRAY) {
return FAILURE;
}
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(heap_elements), zval *val) {
if (is_pqueue) {
/* PriorityQueue elements are serialized as arrays with 'data' and 'priority' keys */
if (Z_TYPE_P(val) != IS_ARRAY || zend_hash_num_elements(Z_ARRVAL_P(val)) != 2) {
return FAILURE;
}
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) {
return FAILURE;
}
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);
if (EG(exception)) {
return FAILURE;
}
} else {
Z_TRY_ADDREF_P(val);
spl_ptr_heap_insert(intern->heap, val, this_ptr);
if (EG(exception)) {
return FAILURE;
}
}
} ZEND_HASH_FOREACH_END();
return SUCCESS;
}
PHP_METHOD(SplPriorityQueue, __serialize)
{
spl_heap_object *intern = Z_SPLHEAP_P(ZEND_THIS);
zval props, state;
ZEND_PARSE_PARAMETERS_NONE();
if (UNEXPECTED(spl_heap_consistency_validations(intern, false) != SUCCESS)) {
RETURN_THROWS();
}
if (intern->heap->flags & SPL_HEAP_WRITE_LOCKED) {
zend_throw_exception(spl_ce_RuntimeException, "Cannot serialize heap while it is being modified.", 0);
RETURN_THROWS();
}
array_init(return_value);
ZVAL_ARR(&props, zend_std_get_properties(&intern->std));
Z_TRY_ADDREF(props);
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();
}
object_properties_load(&intern->std, Z_ARRVAL_P(props));
if (EG(exception)) {
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(intern->std.ce->name));
RETURN_THROWS();
}
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();
}
if (spl_heap_unserialize_internal_state(Z_ARRVAL_P(state), intern, ZEND_THIS, true) != SUCCESS) {
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(intern->std.ce->name));
RETURN_THROWS();
}
if (EG(exception)) {
RETURN_THROWS();
}
if (UNEXPECTED(spl_heap_consistency_validations(intern, false) != SUCCESS)) {
RETURN_THROWS();
}
}
PHP_METHOD(SplHeap, __serialize)
{
spl_heap_object *intern = Z_SPLHEAP_P(ZEND_THIS);
zval props, state;
ZEND_PARSE_PARAMETERS_NONE();
if (UNEXPECTED(spl_heap_consistency_validations(intern, false) != SUCCESS)) {
RETURN_THROWS();
}
if (intern->heap->flags & SPL_HEAP_WRITE_LOCKED) {
zend_throw_exception(spl_ce_RuntimeException, "Cannot serialize heap while it is being modified.", 0);
RETURN_THROWS();
}
array_init(return_value);
ZVAL_ARR(&props, zend_std_get_properties(&intern->std));
Z_TRY_ADDREF(props);
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();
}
object_properties_load(&intern->std, Z_ARRVAL_P(props));
if (EG(exception)) {
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(intern->std.ce->name));
RETURN_THROWS();
}
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();
}
if (spl_heap_unserialize_internal_state(Z_ARRVAL_P(state), intern, ZEND_THIS, false) != SUCCESS) {
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(intern->std.ce->name));
RETURN_THROWS();
}
if (EG(exception)) {
RETURN_THROWS();
}
if (UNEXPECTED(spl_heap_consistency_validations(intern, false) != SUCCESS)) {
RETURN_THROWS();
}
}
/* iterator handler table */
static const zend_object_iterator_funcs spl_heap_it_funcs = {
spl_heap_it_dtor,

View File

@@ -82,6 +82,12 @@ class SplPriorityQueue implements Iterator, Countable
/** @tentative-return-type */
public function __debugInfo(): array {}
/** @tentative-return-type */
public function __serialize(): array {}
/** @tentative-return-type */
public function __unserialize(array $data): void {}
}
abstract class SplHeap implements Iterator, Countable
@@ -127,6 +133,12 @@ abstract class SplHeap implements Iterator, Countable
/** @tentative-return-type */
public function __debugInfo(): array {}
/** @tentative-return-type */
public function __serialize(): array {}
/** @tentative-return-type */
public function __unserialize(array $data): void {}
}
class SplMinHeap extends SplHeap

View File

@@ -1,5 +1,5 @@
/* This is a generated file, edit the .stub.php file instead.
* Stub hash: 47273e114c9c7089bf708a2f18f2e9e522abceb6 */
* Stub hash: 3256398ed9e798f141fd3cb73370c0d8b2dbd0f1 */
ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_SplPriorityQueue_compare, 0, 2, IS_LONG, 0)
ZEND_ARG_TYPE_INFO(0, priority1, IS_MIXED, 0)
@@ -47,6 +47,12 @@ ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_SplPriorityQueue___debugInfo, 0, 0, IS_ARRAY, 0)
ZEND_END_ARG_INFO()
#define arginfo_class_SplPriorityQueue___serialize arginfo_class_SplPriorityQueue___debugInfo
ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_SplPriorityQueue___unserialize, 0, 1, IS_VOID, 0)
ZEND_ARG_TYPE_INFO(0, data, IS_ARRAY, 0)
ZEND_END_ARG_INFO()
#define arginfo_class_SplHeap_extract arginfo_class_SplPriorityQueue_top
ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_SplHeap_insert, 0, 1, IS_TRUE, 0)
@@ -80,6 +86,10 @@ ZEND_END_ARG_INFO()
#define arginfo_class_SplHeap___debugInfo arginfo_class_SplPriorityQueue___debugInfo
#define arginfo_class_SplHeap___serialize arginfo_class_SplPriorityQueue___debugInfo
#define arginfo_class_SplHeap___unserialize arginfo_class_SplPriorityQueue___unserialize
#define arginfo_class_SplMinHeap_compare arginfo_class_SplHeap_compare
#define arginfo_class_SplMaxHeap_compare arginfo_class_SplHeap_compare
@@ -100,11 +110,15 @@ ZEND_METHOD(SplHeap, recoverFromCorruption);
ZEND_METHOD(SplHeap, isCorrupted);
ZEND_METHOD(SplPriorityQueue, getExtractFlags);
ZEND_METHOD(SplPriorityQueue, __debugInfo);
ZEND_METHOD(SplPriorityQueue, __serialize);
ZEND_METHOD(SplPriorityQueue, __unserialize);
ZEND_METHOD(SplHeap, extract);
ZEND_METHOD(SplHeap, insert);
ZEND_METHOD(SplHeap, top);
ZEND_METHOD(SplHeap, current);
ZEND_METHOD(SplHeap, __debugInfo);
ZEND_METHOD(SplHeap, __serialize);
ZEND_METHOD(SplHeap, __unserialize);
ZEND_METHOD(SplMinHeap, compare);
ZEND_METHOD(SplMaxHeap, compare);
@@ -125,6 +139,8 @@ static const zend_function_entry class_SplPriorityQueue_methods[] = {
ZEND_RAW_FENTRY("isCorrupted", zim_SplHeap_isCorrupted, arginfo_class_SplPriorityQueue_isCorrupted, ZEND_ACC_PUBLIC, NULL, NULL)
ZEND_ME(SplPriorityQueue, getExtractFlags, arginfo_class_SplPriorityQueue_getExtractFlags, ZEND_ACC_PUBLIC)
ZEND_ME(SplPriorityQueue, __debugInfo, arginfo_class_SplPriorityQueue___debugInfo, ZEND_ACC_PUBLIC)
ZEND_ME(SplPriorityQueue, __serialize, arginfo_class_SplPriorityQueue___serialize, ZEND_ACC_PUBLIC)
ZEND_ME(SplPriorityQueue, __unserialize, arginfo_class_SplPriorityQueue___unserialize, ZEND_ACC_PUBLIC)
ZEND_FE_END
};
@@ -143,6 +159,8 @@ static const zend_function_entry class_SplHeap_methods[] = {
ZEND_RAW_FENTRY("compare", NULL, arginfo_class_SplHeap_compare, ZEND_ACC_PROTECTED|ZEND_ACC_ABSTRACT, NULL, NULL)
ZEND_ME(SplHeap, isCorrupted, arginfo_class_SplHeap_isCorrupted, ZEND_ACC_PUBLIC)
ZEND_ME(SplHeap, __debugInfo, arginfo_class_SplHeap___debugInfo, ZEND_ACC_PUBLIC)
ZEND_ME(SplHeap, __serialize, arginfo_class_SplHeap___serialize, ZEND_ACC_PUBLIC)
ZEND_ME(SplHeap, __unserialize, arginfo_class_SplHeap___unserialize, ZEND_ACC_PUBLIC)
ZEND_FE_END
};

View File

@@ -0,0 +1,67 @@
--TEST--
SplHeap and SplPriorityQueue serialization fails when corrupted
--FILE--
<?php
class ThrowingHeap extends SplMaxHeap {
public function compare($a, $b): int {
if ($a === 'throw' || $b === 'throw') {
throw new Exception('Comparison failed');
}
return parent::compare($a, $b);
}
}
$heap = new ThrowingHeap();
$heap->insert(1);
$heap->insert(2);
try {
$heap->insert('throw');
} catch (Exception $e) {
// no-op, heap should now be corrupted
}
echo "Heap is corrupted: " . ($heap->isCorrupted() ? 'YES' : 'NO') . "\n";
try {
serialize($heap);
echo "FAIL: Serialization should have thrown\n";
} catch (Exception $e) {
echo "Serialization failed: " . $e->getMessage() . "\n";
}
class ThrowingPQ extends SplPriorityQueue {
public function compare($priority1, $priority2): int {
if ($priority1 === 'throw' || $priority2 === 'throw') {
throw new Exception('Priority comparison failed');
}
return parent::compare($priority1, $priority2);
}
}
$pq = new ThrowingPQ();
$pq->insert('data1', 1);
$pq->insert('data2', 2);
try {
$pq->insert('data3', 'throw');
} catch (Exception $e) {
// no-op, queue is corrupted
}
echo "PriorityQueue is corrupted: " . ($pq->isCorrupted() ? 'YES' : 'NO') . "\n";
try {
serialize($pq);
echo "FAIL: PQ Serialization should have thrown\n";
} catch (Exception $e) {
echo "PQ Serialization failed: " . $e->getMessage() . "\n";
}
?>
--EXPECT--
Heap is corrupted: YES
Serialization failed: Heap is corrupted, heap properties are no longer ensured.
PriorityQueue is corrupted: YES
PQ Serialization failed: Heap is corrupted, heap properties are no longer ensured.

View File

@@ -0,0 +1,84 @@
--TEST--
SplHeap and SplPriorityQueue unserialization error handling
--FILE--
<?php
// Test malformed data cases
$invalid_cases = [
// Wrong array count
[],
[[], [], []],
// Invalid properties type
['not_array', []],
[123, []],
// Invalid state type
[[], 'not_array'],
[[], 123],
// Missing flags
[[], []],
// Invalid flags type
[[], ['flags' => 'not_int']],
// Missing heap_elements
[[], ['flags' => 0]],
// Invalid heap_elements type
[[], ['flags' => 0, 'heap_elements' => 'not_array']],
[[], ['flags' => 0, 'heap_elements' => 123]],
];
foreach ($invalid_cases as $i => $case) {
try {
$heap = new SplMaxHeap();
$heap->__unserialize($case);
echo "Case $i: UNEXPECTED SUCCESS\n";
} catch (Exception $e) {
echo "Case $i: " . $e->getMessage() . "\n";
}
}
$pq_invalid_cases = [
// Invalid flags for PQ
[[], ['flags' => 0]],
// Invalid element structure
[[], ['flags' => 1, 'heap_elements' => ['not_array']]],
// Missing data/priority keys
[[], ['flags' => 1, 'heap_elements' => [['data' => 'test']]]],
[[], ['flags' => 1, 'heap_elements' => [['priority' => 1]]]],
[[], ['flags' => 1, 'heap_elements' => [[]]]],
];
foreach ($pq_invalid_cases as $i => $case) {
try {
$pq = new SplPriorityQueue();
$pq->__unserialize($case);
echo "PQ Case $i: UNEXPECTED SUCCESS\n";
} catch (Exception $e) {
echo "PQ Case $i: " . $e->getMessage() . "\n";
}
}
?>
--EXPECT--
Case 0: Invalid serialization data for SplMaxHeap object
Case 1: Invalid serialization data for SplMaxHeap object
Case 2: Invalid serialization data for SplMaxHeap object
Case 3: Invalid serialization data for SplMaxHeap object
Case 4: Invalid serialization data for SplMaxHeap object
Case 5: Invalid serialization data for SplMaxHeap object
Case 6: Invalid serialization data for SplMaxHeap object
Case 7: Invalid serialization data for SplMaxHeap object
Case 8: Invalid serialization data for SplMaxHeap object
Case 9: Invalid serialization data for SplMaxHeap object
Case 10: Invalid serialization data for SplMaxHeap object
PQ Case 0: Invalid serialization data for SplPriorityQueue object
PQ Case 1: Invalid serialization data for SplPriorityQueue object
PQ Case 2: Invalid serialization data for SplPriorityQueue object
PQ Case 3: Invalid serialization data for SplPriorityQueue object
PQ Case 4: Invalid serialization data for SplPriorityQueue object

View File

@@ -0,0 +1,131 @@
--TEST--
SplHeap serialization binary format
--FILE--
<?php
// Test SplMaxHeap empty
$heap = new SplMaxHeap();
$s = serialize($heap);
echo $s . "\n";
var_dump(unserialize($s));
// Test SplMaxHeap with data
$heap = new SplMaxHeap();
$heap->insert(100);
$heap->insert(50);
$heap->insert(200);
$s = serialize($heap);
echo $s . "\n";
$unserialized = unserialize($s);
var_dump($unserialized);
// Test SplMinHeap
$min = new SplMinHeap();
$min->insert(30);
$min->insert(10);
$min->insert(20);
$s = serialize($min);
echo $s . "\n";
var_dump(unserialize($s));
// Test SplPriorityQueue empty
$pq = new SplPriorityQueue();
$s = serialize($pq);
echo $s . "\n";
var_dump(unserialize($s));
// Test SplPriorityQueue with data
$pq = new SplPriorityQueue();
$pq->insert('low', 1);
$pq->insert('high', 10);
$pq->insert('medium', 5);
$pq->setExtractFlags(SplPriorityQueue::EXTR_BOTH);
$s = serialize($pq);
echo $s . "\n";
var_dump(unserialize($s));
?>
--EXPECT--
O:10:"SplMaxHeap":2:{i:0;a:0:{}i:1;a:2:{s:5:"flags";i:0;s:13:"heap_elements";a:0:{}}}
object(SplMaxHeap)#2 (3) {
["flags":"SplHeap":private]=>
int(0)
["isCorrupted":"SplHeap":private]=>
bool(false)
["heap":"SplHeap":private]=>
array(0) {
}
}
O:10:"SplMaxHeap":2:{i:0;a:0:{}i:1;a:2:{s:5:"flags";i:0;s:13:"heap_elements";a:3:{i:0;i:200;i:1;i:50;i:2;i:100;}}}
object(SplMaxHeap)#1 (3) {
["flags":"SplHeap":private]=>
int(0)
["isCorrupted":"SplHeap":private]=>
bool(false)
["heap":"SplHeap":private]=>
array(3) {
[0]=>
int(200)
[1]=>
int(50)
[2]=>
int(100)
}
}
O:10:"SplMinHeap":2:{i:0;a:0:{}i:1;a:2:{s:5:"flags";i:0;s:13:"heap_elements";a:3:{i:0;i:10;i:1;i:30;i:2;i:20;}}}
object(SplMinHeap)#4 (3) {
["flags":"SplHeap":private]=>
int(0)
["isCorrupted":"SplHeap":private]=>
bool(false)
["heap":"SplHeap":private]=>
array(3) {
[0]=>
int(10)
[1]=>
int(30)
[2]=>
int(20)
}
}
O:16:"SplPriorityQueue":2:{i:0;a:0:{}i:1;a:2:{s:5:"flags";i:1;s:13:"heap_elements";a:0:{}}}
object(SplPriorityQueue)#5 (3) {
["flags":"SplPriorityQueue":private]=>
int(1)
["isCorrupted":"SplPriorityQueue":private]=>
bool(false)
["heap":"SplPriorityQueue":private]=>
array(0) {
}
}
O:16:"SplPriorityQueue":2:{i:0;a:0:{}i:1;a:2:{s:5:"flags";i:3;s:13:"heap_elements";a:3:{i:0;a:2:{s:4:"data";s:4:"high";s:8:"priority";i:10;}i:1;a:2:{s:4:"data";s:3:"low";s:8:"priority";i:1;}i:2;a:2:{s:4:"data";s:6:"medium";s:8:"priority";i:5;}}}}
object(SplPriorityQueue)#4 (3) {
["flags":"SplPriorityQueue":private]=>
int(3)
["isCorrupted":"SplPriorityQueue":private]=>
bool(false)
["heap":"SplPriorityQueue":private]=>
array(3) {
[0]=>
array(2) {
["data"]=>
string(4) "high"
["priority"]=>
int(10)
}
[1]=>
array(2) {
["data"]=>
string(3) "low"
["priority"]=>
int(1)
}
[2]=>
array(2) {
["data"]=>
string(6) "medium"
["priority"]=>
int(5)
}
}
}

View File

@@ -0,0 +1,91 @@
--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();
var_dump($serialized_data);
$pq = new SplPriorityQueue();
$pq->insert('data', 10);
$pq->setExtractFlags(SplPriorityQueue::EXTR_DATA);
$pq_data = $pq->__serialize();
var_dump($pq_data);
class CustomHeap extends SplMaxHeap {
public $flags = 'user_property';
public $heap_elements = 'user_property';
}
$custom = new CustomHeap();
$custom->insert(42);
$custom_data = $custom->__serialize();
var_dump($custom_data);
?>
--EXPECT--
array(2) {
[0]=>
array(0) {
}
[1]=>
array(2) {
["flags"]=>
int(0)
["heap_elements"]=>
array(3) {
[0]=>
int(200)
[1]=>
int(50)
[2]=>
int(100)
}
}
}
array(2) {
[0]=>
array(0) {
}
[1]=>
array(2) {
["flags"]=>
int(1)
["heap_elements"]=>
array(1) {
[0]=>
array(2) {
["data"]=>
string(4) "data"
["priority"]=>
int(10)
}
}
}
}
array(2) {
[0]=>
array(2) {
["flags"]=>
UNKNOWN:0
["heap_elements"]=>
UNKNOWN:0
}
[1]=>
array(2) {
["flags"]=>
int(0)
["heap_elements"]=>
array(1) {
[0]=>
int(42)
}
}
}

View File

@@ -0,0 +1,117 @@
--TEST--
SplHeap and SplPriorityQueue inheritance safety with conflicting property names
--FILE--
<?php
class CustomHeap extends SplMaxHeap {
public $flags = 'user_flags_property';
public $heap_elements = 'user_heap_elements_property';
public $custom_prop = 'custom_value';
protected $protected_prop = 'protected_value';
private $private_prop = 'private_value';
}
$heap = new CustomHeap();
$heap->flags = 'modified_user_flags';
$heap->heap_elements = 'modified_user_elements';
$heap->custom_prop = 'modified_custom';
$heap->insert(100);
$heap->insert(50);
$heap->insert(200);
$serialized = serialize($heap);
echo $serialized . "\n";
$unserialized = unserialize($serialized);
var_dump($unserialized);
class CustomPriorityQueue extends SplPriorityQueue {
public $flags = 'user_flags_property';
public $heap_elements = 'user_heap_elements_property';
public $custom_data = 'custom_data_value';
protected $protected_priority = 'protected_priority_value';
}
$pq = new CustomPriorityQueue();
$pq->flags = 'modified_user_flags';
$pq->heap_elements = 'modified_user_elements';
$pq->custom_data = 'modified_custom_data';
$pq->insert('low_priority', 1);
$pq->insert('high_priority', 10);
$pq->insert('medium_priority', 5);
$pq->setExtractFlags(SplPriorityQueue::EXTR_BOTH);
$serialized_pq = serialize($pq);
echo $serialized_pq . "\n";
$unserialized_pq = unserialize($serialized_pq);
var_dump($unserialized_pq);
?>
--EXPECTF--
O:10:"CustomHeap":2:{i:0;a:5:{s:5:"flags";s:19:"modified_user_flags";s:13:"heap_elements";s:22:"modified_user_elements";s:11:"custom_prop";s:15:"modified_custom";s:17:"%0*%0protected_prop";s:15:"protected_value";s:24:"%0CustomHeap%0private_prop";s:13:"private_value";}i:1;a:2:{s:5:"flags";i:0;s:13:"heap_elements";a:3:{i:0;i:200;i:1;i:50;i:2;i:100;}}}
object(CustomHeap)#2 (8) {
["flags"]=>
string(19) "modified_user_flags"
["heap_elements"]=>
string(22) "modified_user_elements"
["custom_prop"]=>
string(15) "modified_custom"
["protected_prop":protected]=>
string(15) "protected_value"
["private_prop":"CustomHeap":private]=>
string(13) "private_value"
["flags":"SplHeap":private]=>
int(0)
["isCorrupted":"SplHeap":private]=>
bool(false)
["heap":"SplHeap":private]=>
array(3) {
[0]=>
int(200)
[1]=>
int(50)
[2]=>
int(100)
}
}
O:19:"CustomPriorityQueue":2:{i:0;a:4:{s:5:"flags";s:19:"modified_user_flags";s:13:"heap_elements";s:22:"modified_user_elements";s:11:"custom_data";s:20:"modified_custom_data";s:21:"%0*%0protected_priority";s:24:"protected_priority_value";}i:1;a:2:{s:5:"flags";i:3;s:13:"heap_elements";a:3:{i:0;a:2:{s:4:"data";s:13:"high_priority";s:8:"priority";i:10;}i:1;a:2:{s:4:"data";s:12:"low_priority";s:8:"priority";i:1;}i:2;a:2:{s:4:"data";s:15:"medium_priority";s:8:"priority";i:5;}}}}
object(CustomPriorityQueue)#4 (7) {
["flags"]=>
string(19) "modified_user_flags"
["heap_elements"]=>
string(22) "modified_user_elements"
["custom_data"]=>
string(20) "modified_custom_data"
["protected_priority":protected]=>
string(24) "protected_priority_value"
["flags":"SplPriorityQueue":private]=>
int(3)
["isCorrupted":"SplPriorityQueue":private]=>
bool(false)
["heap":"SplPriorityQueue":private]=>
array(3) {
[0]=>
array(2) {
["data"]=>
string(13) "high_priority"
["priority"]=>
int(10)
}
[1]=>
array(2) {
["data"]=>
string(12) "low_priority"
["priority"]=>
int(1)
}
[2]=>
array(2) {
["data"]=>
string(15) "medium_priority"
["priority"]=>
int(5)
}
}
}

View File

@@ -0,0 +1,89 @@
--TEST--
SplMaxHeap serialization with complex data types
--FILE--
<?php
$heap = new SplMaxHeap();
$heap->insert(['type' => 'array1', 'value' => 10]);
$heap->insert(['type' => 'array2', 'value' => 20]);
$heap->insert(['type' => 'array3', 'value' => 5]);
$serialized = serialize($heap);
echo $serialized . "\n";
$unserialized = unserialize($serialized);
var_dump($unserialized);
class TestObj {
public $val;
public function __construct($v) { $this->val = $v; }
}
$heap2 = new SplMaxHeap();
$heap2->insert(new TestObj(30));
$heap2->insert(new TestObj(10));
$heap2->insert(new TestObj(20));
$serialized2 = serialize($heap2);
echo $serialized2 . "\n";
$unserialized2 = unserialize($serialized2);
var_dump($unserialized2);
?>
--EXPECT--
O:10:"SplMaxHeap":2:{i:0;a:0:{}i:1;a:2:{s:5:"flags";i:0;s:13:"heap_elements";a:3:{i:0;a:2:{s:4:"type";s:6:"array3";s:5:"value";i:5;}i:1;a:2:{s:4:"type";s:6:"array1";s:5:"value";i:10;}i:2;a:2:{s:4:"type";s:6:"array2";s:5:"value";i:20;}}}}
object(SplMaxHeap)#2 (3) {
["flags":"SplHeap":private]=>
int(0)
["isCorrupted":"SplHeap":private]=>
bool(false)
["heap":"SplHeap":private]=>
array(3) {
[0]=>
array(2) {
["type"]=>
string(6) "array3"
["value"]=>
int(5)
}
[1]=>
array(2) {
["type"]=>
string(6) "array1"
["value"]=>
int(10)
}
[2]=>
array(2) {
["type"]=>
string(6) "array2"
["value"]=>
int(20)
}
}
}
O:10:"SplMaxHeap":2:{i:0;a:0:{}i:1;a:2:{s:5:"flags";i:0;s:13:"heap_elements";a:3:{i:0;O:7:"TestObj":1:{s:3:"val";i:30;}i:1;O:7:"TestObj":1:{s:3:"val";i:10;}i:2;O:7:"TestObj":1:{s:3:"val";i:20;}}}}
object(SplMaxHeap)#7 (3) {
["flags":"SplHeap":private]=>
int(0)
["isCorrupted":"SplHeap":private]=>
bool(false)
["heap":"SplHeap":private]=>
array(3) {
[0]=>
object(TestObj)#8 (1) {
["val"]=>
int(30)
}
[1]=>
object(TestObj)#9 (1) {
["val"]=>
int(10)
}
[2]=>
object(TestObj)#10 (1) {
["val"]=>
int(20)
}
}
}

View File

@@ -0,0 +1,63 @@
--TEST--
SplMinHeap serialization with nested arrays
--FILE--
<?php
$heap = new SplMinHeap();
$heap->insert(['name' => 'Alice', 'nested' => ['age' => 25, 'city' => 'NYC']]);
$heap->insert(['name' => 'Bob', 'nested' => ['age' => 30, 'city' => 'LA']]);
$heap->insert(['name' => 'Charlie', 'nested' => ['age' => 35, 'city' => 'SF']]);
$serialized = serialize($heap);
echo $serialized . "\n";
$unserialized = unserialize($serialized);
var_dump($unserialized);
?>
--EXPECT--
O:10:"SplMinHeap":2:{i:0;a:0:{}i:1;a:2:{s:5:"flags";i:0;s:13:"heap_elements";a:3:{i:0;a:2:{s:4:"name";s:5:"Alice";s:6:"nested";a:2:{s:3:"age";i:25;s:4:"city";s:3:"NYC";}}i:1;a:2:{s:4:"name";s:3:"Bob";s:6:"nested";a:2:{s:3:"age";i:30;s:4:"city";s:2:"LA";}}i:2;a:2:{s:4:"name";s:7:"Charlie";s:6:"nested";a:2:{s:3:"age";i:35;s:4:"city";s:2:"SF";}}}}}
object(SplMinHeap)#2 (3) {
["flags":"SplHeap":private]=>
int(0)
["isCorrupted":"SplHeap":private]=>
bool(false)
["heap":"SplHeap":private]=>
array(3) {
[0]=>
array(2) {
["name"]=>
string(5) "Alice"
["nested"]=>
array(2) {
["age"]=>
int(25)
["city"]=>
string(3) "NYC"
}
}
[1]=>
array(2) {
["name"]=>
string(3) "Bob"
["nested"]=>
array(2) {
["age"]=>
int(30)
["city"]=>
string(2) "LA"
}
}
[2]=>
array(2) {
["name"]=>
string(7) "Charlie"
["nested"]=>
array(2) {
["age"]=>
int(35)
["city"]=>
string(2) "SF"
}
}
}
}

View File

@@ -0,0 +1,113 @@
--TEST--
SplPriorityQueue serialization with mixed data types and extract flags
--FILE--
<?php
$queue = new SplPriorityQueue();
$queue->setExtractFlags(SplPriorityQueue::EXTR_BOTH);
$array1 = ['name' => 'John', 'hobbies' => ['reading', 'gaming']];
$queue->insert($array1, 10);
class TestClass {
public $prop = 'test';
}
$obj1 = new TestClass();
$queue->insert($obj1, 15);
$queue->insert(3.14159, 12);
$queue->insert(true, 20);
$queue->insert(null, 3);
$serialized = serialize($queue);
echo $serialized . "\n";
$unserialized = unserialize($serialized);
var_dump($unserialized);
$queue2 = new SplPriorityQueue();
$queue2->setExtractFlags(SplPriorityQueue::EXTR_PRIORITY);
$queue2->insert("data", 42);
$serialized2 = serialize($queue2);
echo $serialized2 . "\n";
$unserialized2 = unserialize($serialized2);
var_dump($unserialized2);
?>
--EXPECT--
O:16:"SplPriorityQueue":2:{i:0;a:0:{}i:1;a:2:{s:5:"flags";i:3;s:13:"heap_elements";a:5:{i:0;a:2:{s:4:"data";b:1;s:8:"priority";i:20;}i:1;a:2:{s:4:"data";O:9:"TestClass":1:{s:4:"prop";s:4:"test";}s:8:"priority";i:15;}i:2;a:2:{s:4:"data";d:3.14159;s:8:"priority";i:12;}i:3;a:2:{s:4:"data";a:2:{s:4:"name";s:4:"John";s:7:"hobbies";a:2:{i:0;s:7:"reading";i:1;s:6:"gaming";}}s:8:"priority";i:10;}i:4;a:2:{s:4:"data";N;s:8:"priority";i:3;}}}}
object(SplPriorityQueue)#3 (3) {
["flags":"SplPriorityQueue":private]=>
int(3)
["isCorrupted":"SplPriorityQueue":private]=>
bool(false)
["heap":"SplPriorityQueue":private]=>
array(5) {
[0]=>
array(2) {
["data"]=>
bool(true)
["priority"]=>
int(20)
}
[1]=>
array(2) {
["data"]=>
object(TestClass)#4 (1) {
["prop"]=>
string(4) "test"
}
["priority"]=>
int(15)
}
[2]=>
array(2) {
["data"]=>
float(3.14159)
["priority"]=>
int(12)
}
[3]=>
array(2) {
["data"]=>
array(2) {
["name"]=>
string(4) "John"
["hobbies"]=>
array(2) {
[0]=>
string(7) "reading"
[1]=>
string(6) "gaming"
}
}
["priority"]=>
int(10)
}
[4]=>
array(2) {
["data"]=>
NULL
["priority"]=>
int(3)
}
}
}
O:16:"SplPriorityQueue":2:{i:0;a:0:{}i:1;a:2:{s:5:"flags";i:2;s:13:"heap_elements";a:1:{i:0;a:2:{s:4:"data";s:4:"data";s:8:"priority";i:42;}}}}
object(SplPriorityQueue)#6 (3) {
["flags":"SplPriorityQueue":private]=>
int(2)
["isCorrupted":"SplPriorityQueue":private]=>
bool(false)
["heap":"SplPriorityQueue":private]=>
array(1) {
[0]=>
array(2) {
["data"]=>
string(4) "data"
["priority"]=>
int(42)
}
}
}

View File

@@ -0,0 +1,80 @@
--TEST--
SplPriorityQueue unserialization with invalid flags should throw exception
--FILE--
<?php
try {
$data = [
[],
[
'flags' => 4, // invalid flag value (4 & 3 = 0)
'heap_elements' => []
]
];
$queue = new SplPriorityQueue();
$queue->__unserialize($data);
echo "Should have thrown exception for invalid flags\n";
} catch (Exception $e) {
echo "Exception thrown for invalid flags: " . $e->getMessage() . "\n";
}
try {
$data = [
[],
[
'flags' => 0,
'heap_elements' => []
]
];
$queue = new SplPriorityQueue();
$queue->__unserialize($data);
echo "Should have thrown exception for zero flags\n";
} catch (Exception $e) {
echo "Exception thrown for zero flags: " . $e->getMessage() . "\n";
}
try {
$data = [
[],
[
'flags' => SplPriorityQueue::EXTR_DATA,
'heap_elements' => []
]
];
$queue = new SplPriorityQueue();
$queue->__unserialize($data);
echo "Valid flags accepted\n";
} catch (Exception $e) {
echo "Valid flags rejected: " . $e->getMessage() . "\n";
}
try {
$data = [
[],
[
'flags' => 999, // extra bits that should be masked to 3 (EXTR_BOTH)
'heap_elements' => []
]
];
$queue = new SplPriorityQueue();
$queue->__unserialize($data);
if ($queue->getExtractFlags() === SplPriorityQueue::EXTR_BOTH) {
echo "Flags properly masked\n";
} else {
echo "Flags not properly masked, got: " . $queue->getExtractFlags() . "\n";
}
} catch (Exception $e) {
echo "Flags with extra bits should be masked: " . $e->getMessage() . "\n";
}
?>
--EXPECT--
Exception thrown for invalid flags: Invalid serialization data for SplPriorityQueue object
Exception thrown for zero flags: Invalid serialization data for SplPriorityQueue object
Valid flags accepted
Flags properly masked