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

Implement SeekableIterator for SplObjectStorage (#13665)

This commit is contained in:
Niels Dossche
2024-05-05 13:26:54 +02:00
committed by GitHub
parent 8637a3f5b0
commit f97bd071d5
6 changed files with 202 additions and 5 deletions

3
NEWS
View File

@@ -239,6 +239,9 @@ PHP NEWS
. Add support for AEGIS-128L and AEGIS-256 (jedisct1)
. Enable AES-GCM on aarch64 with the ARM crypto extensions (jedisct1)
- SPL:
. Implement SeekableIterator for SplObjectStorage. (nielsdos)
- Standard:
. Implement GH-12188 (Indication for the int size in phpinfo()). (timwolla)
. Partly fix GH-12143 (Incorrect round() result for 0.49999999999999994).

View File

@@ -504,6 +504,10 @@ PHP 8.4 UPGRADE NOTES
. sodium_crypto_aead_aes256gcm_*() functions are now enabled on aarch64 CPUs
with the ARM cryptographic extensions.
- SPL:
. Added seek() method to SplObjectStorage, now it implements
SeekableIterator.
- Standard:
. Added the http_get_last_response_headers() and
http_clear_last_response_headers() that allows retrieving the same content

View File

@@ -758,6 +758,49 @@ PHP_METHOD(SplObjectStorage, next)
intern->index++;
} /* }}} */
/* {{{ Seek to position. */
PHP_METHOD(SplObjectStorage, seek)
{
zend_long position;
spl_SplObjectStorage *intern = Z_SPLOBJSTORAGE_P(ZEND_THIS);
if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &position) == FAILURE) {
RETURN_THROWS();
}
if (position < 0 || position >= zend_hash_num_elements(&intern->storage)) {
zend_throw_exception_ex(spl_ce_OutOfBoundsException, 0, "Seek position " ZEND_LONG_FMT " is out of range", position);
RETURN_THROWS();
}
if (position == 0) {
/* fast path */
zend_hash_internal_pointer_reset_ex(&intern->storage, &intern->pos);
intern->index = 0;
} else if (position > intern->index) {
/* unlike the optimization below, it's not cheap to go to the end */
do {
zend_hash_move_forward_ex(&intern->storage, &intern->pos);
intern->index++;
} while (position > intern->index);
} else if (position < intern->index) {
/* optimization: check if it's more profitable to reset and do a forwards seek instead, it's cheap to reset */
if (intern->index - position > position) {
zend_hash_internal_pointer_reset_ex(&intern->storage, &intern->pos);
intern->index = 0;
do {
zend_hash_move_forward_ex(&intern->storage, &intern->pos);
intern->index++;
} while (position > intern->index);
} else {
do {
zend_hash_move_backwards_ex(&intern->storage, &intern->pos);
intern->index--;
} while (position < intern->index);
}
}
} /* }}} */
/* {{{ Serializes storage */
PHP_METHOD(SplObjectStorage, serialize)
{
@@ -1326,7 +1369,7 @@ PHP_MINIT_FUNCTION(spl_observer)
spl_ce_SplObserver = register_class_SplObserver();
spl_ce_SplSubject = register_class_SplSubject();
spl_ce_SplObjectStorage = register_class_SplObjectStorage(zend_ce_countable, zend_ce_iterator, zend_ce_serializable, zend_ce_arrayaccess);
spl_ce_SplObjectStorage = register_class_SplObjectStorage(zend_ce_countable, spl_ce_SeekableIterator, zend_ce_serializable, zend_ce_arrayaccess);
spl_ce_SplObjectStorage->create_object = spl_SplObjectStorage_new;
spl_ce_SplObjectStorage->default_object_handlers = &spl_handler_SplObjectStorage;

View File

@@ -20,7 +20,7 @@ interface SplSubject
public function notify(): void;
}
class SplObjectStorage implements Countable, Iterator, Serializable, ArrayAccess
class SplObjectStorage implements Countable, SeekableIterator, Serializable, ArrayAccess
{
/** @tentative-return-type */
public function attach(object $object, mixed $info = null): void {}
@@ -64,6 +64,8 @@ class SplObjectStorage implements Countable, Iterator, Serializable, ArrayAccess
/** @tentative-return-type */
public function next(): void {}
public function seek(int $offset): void {}
/** @tentative-return-type */
public function unserialize(string $data): void {}

View File

@@ -1,5 +1,5 @@
/* This is a generated file, edit the .stub.php file instead.
* Stub hash: 1fc23a91e7531eeb73729d4ad44addf0190c3a62 */
* Stub hash: a846c9dd240b6f0666cd5e805abfacabe360cf4c */
ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_SplObserver_update, 0, 1, IS_VOID, 0)
ZEND_ARG_OBJ_INFO(0, subject, SplSubject, 0)
@@ -59,6 +59,10 @@ ZEND_END_ARG_INFO()
#define arginfo_class_SplObjectStorage_next arginfo_class_SplSubject_notify
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_SplObjectStorage_seek, 0, 1, IS_VOID, 0)
ZEND_ARG_TYPE_INFO(0, offset, IS_LONG, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_SplObjectStorage_unserialize, 0, 1, IS_VOID, 0)
ZEND_ARG_TYPE_INFO(0, data, IS_STRING, 0)
ZEND_END_ARG_INFO()
@@ -147,6 +151,7 @@ ZEND_METHOD(SplObjectStorage, valid);
ZEND_METHOD(SplObjectStorage, key);
ZEND_METHOD(SplObjectStorage, current);
ZEND_METHOD(SplObjectStorage, next);
ZEND_METHOD(SplObjectStorage, seek);
ZEND_METHOD(SplObjectStorage, unserialize);
ZEND_METHOD(SplObjectStorage, serialize);
ZEND_METHOD(SplObjectStorage, offsetGet);
@@ -194,6 +199,7 @@ static const zend_function_entry class_SplObjectStorage_methods[] = {
ZEND_ME(SplObjectStorage, key, arginfo_class_SplObjectStorage_key, ZEND_ACC_PUBLIC)
ZEND_ME(SplObjectStorage, current, arginfo_class_SplObjectStorage_current, ZEND_ACC_PUBLIC)
ZEND_ME(SplObjectStorage, next, arginfo_class_SplObjectStorage_next, ZEND_ACC_PUBLIC)
ZEND_ME(SplObjectStorage, seek, arginfo_class_SplObjectStorage_seek, ZEND_ACC_PUBLIC)
ZEND_ME(SplObjectStorage, unserialize, arginfo_class_SplObjectStorage_unserialize, ZEND_ACC_PUBLIC)
ZEND_ME(SplObjectStorage, serialize, arginfo_class_SplObjectStorage_serialize, ZEND_ACC_PUBLIC)
ZEND_RAW_FENTRY("offsetExists", zim_SplObjectStorage_contains, arginfo_class_SplObjectStorage_offsetExists, ZEND_ACC_PUBLIC, NULL, NULL)
@@ -244,13 +250,13 @@ static zend_class_entry *register_class_SplSubject(void)
return class_entry;
}
static zend_class_entry *register_class_SplObjectStorage(zend_class_entry *class_entry_Countable, zend_class_entry *class_entry_Iterator, zend_class_entry *class_entry_Serializable, zend_class_entry *class_entry_ArrayAccess)
static zend_class_entry *register_class_SplObjectStorage(zend_class_entry *class_entry_Countable, zend_class_entry *class_entry_SeekableIterator, zend_class_entry *class_entry_Serializable, zend_class_entry *class_entry_ArrayAccess)
{
zend_class_entry ce, *class_entry;
INIT_CLASS_ENTRY(ce, "SplObjectStorage", class_SplObjectStorage_methods);
class_entry = zend_register_internal_class_ex(&ce, NULL);
zend_class_implements(class_entry, 4, class_entry_Countable, class_entry_Iterator, class_entry_Serializable, class_entry_ArrayAccess);
zend_class_implements(class_entry, 4, class_entry_Countable, class_entry_SeekableIterator, class_entry_Serializable, class_entry_ArrayAccess);
return class_entry;
}

View File

@@ -0,0 +1,139 @@
--TEST--
SplObjectStorage::seek() basic functionality
--FILE--
<?php
class Test {
public function __construct(public string $marker) {}
}
$a = new Test("a");
$b = new Test("b");
$c = new Test("c");
$d = new Test("d");
$e = new Test("e");
$storage = new SplObjectStorage();
$storage[$a] = 1;
$storage[$b] = 2;
$storage[$c] = 3;
$storage[$d] = 4;
$storage[$e] = 5;
echo "--- Error cases ---\n";
try {
$storage->seek(-1);
} catch (OutOfBoundsException $e) {
echo $e->getMessage(), "\n";
}
try {
$storage->seek(5);
} catch (OutOfBoundsException $e) {
echo $e->getMessage(), "\n";
}
var_dump($storage->key());
var_dump($storage->current());
echo "--- Normal cases ---\n";
$storage->seek(2);
var_dump($storage->key());
var_dump($storage->current());
$storage->seek(1);
var_dump($storage->key());
var_dump($storage->current());
$storage->seek(4);
var_dump($storage->key());
var_dump($storage->current());
$storage->seek(0);
var_dump($storage->key());
var_dump($storage->current());
$storage->seek(3);
var_dump($storage->key());
var_dump($storage->current());
$storage->seek(3);
var_dump($storage->key());
var_dump($storage->current());
echo "--- With holes cases ---\n";
$storage->detach($b);
$storage->detach($d);
foreach (range(0, 2) as $index) {
$storage->seek($index);
var_dump($storage->key());
var_dump($storage->current());
}
try {
$storage->seek(3);
} catch (OutOfBoundsException $e) {
echo $e->getMessage(), "\n";
}
?>
--EXPECT--
--- Error cases ---
Seek position -1 is out of range
Seek position 5 is out of range
int(0)
object(Test)#1 (1) {
["marker"]=>
string(1) "a"
}
--- Normal cases ---
int(2)
object(Test)#3 (1) {
["marker"]=>
string(1) "c"
}
int(1)
object(Test)#2 (1) {
["marker"]=>
string(1) "b"
}
int(4)
object(Test)#5 (1) {
["marker"]=>
string(1) "e"
}
int(0)
object(Test)#1 (1) {
["marker"]=>
string(1) "a"
}
int(3)
object(Test)#4 (1) {
["marker"]=>
string(1) "d"
}
int(3)
object(Test)#4 (1) {
["marker"]=>
string(1) "d"
}
--- With holes cases ---
int(0)
object(Test)#1 (1) {
["marker"]=>
string(1) "a"
}
int(1)
object(Test)#3 (1) {
["marker"]=>
string(1) "c"
}
int(2)
object(Test)#5 (1) {
["marker"]=>
string(1) "e"
}
Seek position 3 is out of range