Introduce InternalIterator

Userland classes that implement Traversable must do so either
through Iterator or IteratorAggregate. The same requirement does
not exist for internal classes: They can implement the internal
get_iterator mechanism, without exposing either the Iterator or
IteratorAggregate APIs. This makes them usable in get_iterator(),
but incompatible with any Iterator based APIs.

A lot of internal classes do this, because exposing the userland
APIs is simply a lot of work. This patch alleviates this issue by
providing a generic InternalIterator class, which acts as an
adapater between get_iterator and Iterator, and can be easily
used by many internal classes. At the same time, we extend the
requirement that Traversable implies Iterator or IteratorAggregate
to internal classes as well.

Closes GH-5216.
This commit is contained in:
Nikita Popov 2020-02-26 16:42:49 +01:00
parent 4730b06f1d
commit ff19ec2df3
33 changed files with 488 additions and 30 deletions

View File

@ -766,6 +766,17 @@ PHP 8.0 UPGRADE NOTES
longer referenced.
. The deprecated parameter `$version` of curl_version() has been removed.
- Date:
. DatePeriod now implements IteratorAggregate (instead of Traversable).
- DOM:
. DOMNamedNodeMap now implements IteratorAggregate (instead of Traversable).
. DOMNodeList now implements IteratorAggregate (instead of Traversable).
- Intl:
. IntlBreakIterator now implements IteratorAggregate (instead of Traversable).
. ResourceBundle now implements IteratorAggregate (instead of Traversable).
- Enchant:
. The enchant extension now uses libenchant-2 by default when available.
libenchant version 1 is still supported but is deprecated and could
@ -786,9 +797,13 @@ PHP 8.0 UPGRADE NOTES
- MBString:
. The Unicode data tables have been updated to version 13.0.0.
- PDO:
. PDOStatement now implements IteratorAggregate (instead of Traversable).
- MySQLi / PDO MySQL:
. When mysqlnd is not used (which is the default and recommended option),
the minimum supported libmysqlclient version is now 5.1.
. mysqli_result now implements IteratorAggregate (instead of Traversable).
- PGSQL / PDO PGSQL:
. The PGSQL and PDO PGSQL extensions now require at least libpq 9.1.

View File

@ -18,6 +18,7 @@ PHP 8.0 INTERNALS UPGRADE NOTES
o. cast_object() object handler is now required
p. ARG_COUNT() macro removed
q. GC_COLLECTABLE flag
r. Cannot implement Traversable only
2. Build system changes
a. Abstract
@ -122,6 +123,16 @@ PHP 8.0 INTERNALS UPGRADE NOTES
Assignments to GC_TYPE_INFO() might need to be changed to properly
set the value of the GC_NOT_COLLECTABLE flag.
r. Just for for userland classes, it is no longer allowed to implement only
the Traversable interface. Instead, it is necessary to implement either
Iterator or IteratorAggregate. You can do the latter by implementing
zend_ce_aggregate and providing the following method implementation:
ZEND_METHOD(MyClass, getIterator) {
ZEND_PARSE_PARAMETERS_NONE();
zend_create_internal_iterator_zval(return_value, ZEND_THIS);
}
========================
2. Build system changes
========================

View File

@ -29,6 +29,9 @@ ZEND_API zend_class_entry *zend_ce_arrayaccess;
ZEND_API zend_class_entry *zend_ce_serializable;
ZEND_API zend_class_entry *zend_ce_countable;
ZEND_API zend_class_entry *zend_ce_stringable;
ZEND_API zend_class_entry *zend_ce_internal_iterator;
static zend_object_handlers zend_internal_iterator_handlers;
/* {{{ zend_call_method
Only returns the returned zval if retval_ptr != NULL */
@ -246,20 +249,16 @@ ZEND_API zend_object_iterator *zend_user_it_get_new_iterator(zend_class_entry *c
/* {{{ zend_implement_traversable */
static int zend_implement_traversable(zend_class_entry *interface, zend_class_entry *class_type)
{
/* check that class_type is traversable at c-level or implements at least one of 'aggregate' and 'Iterator' */
uint32_t i;
if (class_type->get_iterator || (class_type->parent && class_type->parent->get_iterator)) {
return SUCCESS;
}
/* Abstract class can implement Traversable only, in which case the extending class must
* implement Iterator or IteratorAggregate. */
if (class_type->ce_flags & ZEND_ACC_EXPLICIT_ABSTRACT_CLASS) {
return SUCCESS;
}
/* Check that class_type implements at least one of 'IteratorAggregate' or 'Iterator' */
if (class_type->num_interfaces) {
ZEND_ASSERT(class_type->ce_flags & ZEND_ACC_RESOLVED_INTERFACES);
for (i = 0; i < class_type->num_interfaces; i++) {
for (uint32_t i = 0; i < class_type->num_interfaces; i++) {
if (class_type->interfaces[i] == zend_ce_aggregate || class_type->interfaces[i] == zend_ce_iterator) {
return SUCCESS;
}
@ -441,9 +440,169 @@ static int zend_implement_serializable(zend_class_entry *interface, zend_class_e
}
/* }}}*/
typedef struct {
zend_object std;
zend_object_iterator *iter;
zend_bool rewind_called;
} zend_internal_iterator;
static zend_object *zend_internal_iterator_create(zend_class_entry *ce) {
zend_internal_iterator *intern = emalloc(sizeof(zend_internal_iterator));
zend_object_std_init(&intern->std, ce);
intern->std.handlers = &zend_internal_iterator_handlers;
intern->iter = NULL;
intern->rewind_called = 0;
return &intern->std;
}
ZEND_API int zend_create_internal_iterator_zval(zval *return_value, zval *obj) {
zend_class_entry *scope = EG(current_execute_data)->func->common.scope;
ZEND_ASSERT(scope->get_iterator != zend_user_it_get_new_iterator);
zend_object_iterator *iter = scope->get_iterator(Z_OBJCE_P(obj), obj, /* by_ref */ 0);
if (!iter) {
return FAILURE;
}
zend_internal_iterator *intern =
(zend_internal_iterator *) zend_internal_iterator_create(zend_ce_internal_iterator);
intern->iter = iter;
ZVAL_OBJ(return_value, &intern->std);
return SUCCESS;
}
static void zend_internal_iterator_free(zend_object *obj) {
zend_internal_iterator *intern = (zend_internal_iterator *) obj;
if (intern->iter) {
zend_iterator_dtor(intern->iter);
}
zend_object_std_dtor(&intern->std);
}
static zend_internal_iterator *zend_internal_iterator_fetch(zval *This) {
zend_internal_iterator *intern = (zend_internal_iterator *) Z_OBJ_P(This);
if (!intern->iter) {
zend_throw_error(NULL, "The InternalIterator object has not been properly initialized");
return NULL;
}
return intern;
}
/* Many iterators will not behave correctly if rewind() is not called, make sure it happens. */
static int zend_internal_iterator_ensure_rewound(zend_internal_iterator *intern) {
if (!intern->rewind_called) {
zend_object_iterator *iter = intern->iter;
intern->rewind_called = 1;
if (iter->funcs->rewind) {
iter->funcs->rewind(iter);
if (UNEXPECTED(EG(exception))) {
return FAILURE;
}
}
}
return SUCCESS;
}
ZEND_METHOD(InternalIterator, __construct) {
zend_throw_error(NULL, "Cannot manually construct InternalIterator");
}
ZEND_METHOD(InternalIterator, current) {
ZEND_PARSE_PARAMETERS_NONE();
zend_internal_iterator *intern = zend_internal_iterator_fetch(ZEND_THIS);
if (!intern) {
RETURN_THROWS();
}
if (zend_internal_iterator_ensure_rewound(intern) == FAILURE) {
RETURN_THROWS();
}
zval *data = intern->iter->funcs->get_current_data(intern->iter);
if (data) {
ZVAL_COPY_DEREF(return_value, data);
}
}
ZEND_METHOD(InternalIterator, key) {
ZEND_PARSE_PARAMETERS_NONE();
zend_internal_iterator *intern = zend_internal_iterator_fetch(ZEND_THIS);
if (!intern) {
RETURN_THROWS();
}
if (zend_internal_iterator_ensure_rewound(intern) == FAILURE) {
RETURN_THROWS();
}
if (intern->iter->funcs->get_current_key) {
intern->iter->funcs->get_current_key(intern->iter, return_value);
} else {
RETURN_LONG(intern->iter->index);
}
}
ZEND_METHOD(InternalIterator, next) {
ZEND_PARSE_PARAMETERS_NONE();
zend_internal_iterator *intern = zend_internal_iterator_fetch(ZEND_THIS);
if (!intern) {
RETURN_THROWS();
}
if (zend_internal_iterator_ensure_rewound(intern) == FAILURE) {
RETURN_THROWS();
}
intern->iter->funcs->move_forward(intern->iter);
intern->iter->index++;
}
ZEND_METHOD(InternalIterator, valid) {
ZEND_PARSE_PARAMETERS_NONE();
zend_internal_iterator *intern = zend_internal_iterator_fetch(ZEND_THIS);
if (!intern) {
RETURN_THROWS();
}
if (zend_internal_iterator_ensure_rewound(intern) == FAILURE) {
RETURN_THROWS();
}
RETURN_BOOL(intern->iter->funcs->valid(intern->iter) == SUCCESS);
}
ZEND_METHOD(InternalIterator, rewind) {
ZEND_PARSE_PARAMETERS_NONE();
zend_internal_iterator *intern = zend_internal_iterator_fetch(ZEND_THIS);
if (!intern) {
RETURN_THROWS();
}
if (!intern->iter->funcs->rewind) {
/* Allow calling rewind() if no iteration has happened yet,
* even if the iterator does not support rewinding. */
if (intern->iter->index != 0) {
zend_throw_error(NULL, "Iterator does not support rewinding");
RETURN_THROWS();
}
intern->iter->index = 0;
return;
}
intern->iter->funcs->rewind(intern->iter);
intern->iter->index = 0;
}
/* {{{ zend_register_interfaces */
ZEND_API void zend_register_interfaces(void)
{
zend_class_entry ce;
REGISTER_MAGIC_INTERFACE(traversable, Traversable);
REGISTER_MAGIC_INTERFACE(aggregate, IteratorAggregate);
@ -454,7 +613,6 @@ ZEND_API void zend_register_interfaces(void)
REGISTER_MAGIC_INTERFACE(serializable, Serializable);
zend_class_entry ce;
INIT_CLASS_ENTRY(ce, "ArrayAccess", class_ArrayAccess_methods);
zend_ce_arrayaccess = zend_register_internal_interface(&ce);
@ -463,5 +621,17 @@ ZEND_API void zend_register_interfaces(void)
INIT_CLASS_ENTRY(ce, "Stringable", class_Stringable_methods);
zend_ce_stringable = zend_register_internal_interface(&ce);
INIT_CLASS_ENTRY(ce, "InternalIterator", class_InternalIterator_methods);
zend_ce_internal_iterator = zend_register_internal_class(&ce);
zend_class_implements(zend_ce_internal_iterator, 1, zend_ce_iterator);
zend_ce_internal_iterator->ce_flags |= ZEND_ACC_FINAL;
zend_ce_internal_iterator->create_object = zend_internal_iterator_create;
zend_ce_internal_iterator->serialize = zend_class_serialize_deny;
zend_ce_internal_iterator->unserialize = zend_class_unserialize_deny;
memcpy(&zend_internal_iterator_handlers, zend_get_std_object_handlers(),
sizeof(zend_object_handlers));
zend_internal_iterator_handlers.free_obj = zend_internal_iterator_free;
}
/* }}} */

View File

@ -78,6 +78,8 @@ ZEND_API int zend_user_unserialize(zval *object, zend_class_entry *ce, const uns
ZEND_API int zend_class_serialize_deny(zval *object, unsigned char **buffer, size_t *buf_len, zend_serialize_data *data);
ZEND_API int zend_class_unserialize_deny(zval *object, zend_class_entry *ce, const unsigned char *buf, size_t buf_len, zend_unserialize_data *data);
ZEND_API int zend_create_internal_iterator_zval(zval *return_value, zval *obj);
END_EXTERN_C()
#endif /* ZEND_INTERFACES_H */

View File

@ -63,3 +63,20 @@ interface Stringable
{
public function __toString(): string;
}
final class InternalIterator implements Iterator
{
private function __construct();
/** @return mixed */
public function current();
/** @return mixed */
public function key();
public function next(): void;
public function valid(): bool;
public function rewind(): void;
}

View File

@ -38,7 +38,27 @@ ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Stringable___toString, 0, 0, IS_STRING, 0)
ZEND_END_ARG_INFO()
#define arginfo_class_InternalIterator___construct arginfo_class_IteratorAggregate_getIterator
#define arginfo_class_InternalIterator_current arginfo_class_IteratorAggregate_getIterator
#define arginfo_class_InternalIterator_key arginfo_class_IteratorAggregate_getIterator
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_InternalIterator_next, 0, 0, IS_VOID, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_InternalIterator_valid, 0, 0, _IS_BOOL, 0)
ZEND_END_ARG_INFO()
#define arginfo_class_InternalIterator_rewind arginfo_class_InternalIterator_next
ZEND_METHOD(InternalIterator, __construct);
ZEND_METHOD(InternalIterator, current);
ZEND_METHOD(InternalIterator, key);
ZEND_METHOD(InternalIterator, next);
ZEND_METHOD(InternalIterator, valid);
ZEND_METHOD(InternalIterator, rewind);
static const zend_function_entry class_Traversable_methods[] = {
@ -88,3 +108,14 @@ static const zend_function_entry class_Stringable_methods[] = {
ZEND_ABSTRACT_ME_WITH_FLAGS(Stringable, __toString, arginfo_class_Stringable___toString, ZEND_ACC_PUBLIC|ZEND_ACC_ABSTRACT)
ZEND_FE_END
};
static const zend_function_entry class_InternalIterator_methods[] = {
ZEND_ME(InternalIterator, __construct, arginfo_class_InternalIterator___construct, ZEND_ACC_PRIVATE)
ZEND_ME(InternalIterator, current, arginfo_class_InternalIterator_current, ZEND_ACC_PUBLIC)
ZEND_ME(InternalIterator, key, arginfo_class_InternalIterator_key, ZEND_ACC_PUBLIC)
ZEND_ME(InternalIterator, next, arginfo_class_InternalIterator_next, ZEND_ACC_PUBLIC)
ZEND_ME(InternalIterator, valid, arginfo_class_InternalIterator_valid, ZEND_ACC_PUBLIC)
ZEND_ME(InternalIterator, rewind, arginfo_class_InternalIterator_rewind, ZEND_ACC_PUBLIC)
ZEND_FE_END
};

View File

@ -576,6 +576,15 @@ ZEND_METHOD(WeakMap, count)
RETURN_LONG(count);
}
ZEND_METHOD(WeakMap, getIterator)
{
if (zend_parse_parameters_none() == FAILURE) {
return;
}
zend_create_internal_iterator_zval(return_value, ZEND_THIS);
}
void zend_register_weakref_ce(void) /* {{{ */
{
zend_class_entry ce;
@ -597,16 +606,14 @@ void zend_register_weakref_ce(void) /* {{{ */
INIT_CLASS_ENTRY(ce, "WeakMap", class_WeakMap_methods);
zend_ce_weakmap = zend_register_internal_class(&ce);
zend_ce_weakmap->ce_flags |= ZEND_ACC_FINAL | ZEND_ACC_NO_DYNAMIC_PROPERTIES;
zend_class_implements(
zend_ce_weakmap, 3, zend_ce_arrayaccess, zend_ce_countable, zend_ce_aggregate);
zend_ce_weakmap->create_object = zend_weakmap_create_object;
zend_ce_weakmap->get_iterator = zend_weakmap_get_iterator;
zend_ce_weakmap->serialize = zend_class_serialize_deny;
zend_ce_weakmap->unserialize = zend_class_unserialize_deny;
/* Must happen after get_iterator is assigned. */
zend_class_implements(
zend_ce_weakmap, 3, zend_ce_arrayaccess, zend_ce_countable, zend_ce_traversable);
memcpy(&zend_weakmap_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
zend_weakmap_handlers.offset = XtOffsetOf(zend_weakmap, std);
zend_weakmap_handlers.free_obj = zend_weakmap_free_obj;

View File

@ -11,7 +11,7 @@ final class WeakReference
public function get(): ?object {}
}
final class WeakMap implements ArrayAccess, Countable, Traversable
final class WeakMap implements ArrayAccess, Countable, IteratorAggregate
{
/**
* @param object $object
@ -32,4 +32,6 @@ final class WeakMap implements ArrayAccess, Countable, Traversable
public function offsetUnset($object): void {}
public function count(): int {}
public function getIterator(): Iterator {}
}

View File

@ -30,6 +30,9 @@ ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_WeakMap_count, 0, 0, IS_LONG, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_WeakMap_getIterator, 0, 0, Iterator, 0)
ZEND_END_ARG_INFO()
ZEND_METHOD(WeakReference, __construct);
ZEND_METHOD(WeakReference, create);
@ -39,6 +42,7 @@ ZEND_METHOD(WeakMap, offsetSet);
ZEND_METHOD(WeakMap, offsetExists);
ZEND_METHOD(WeakMap, offsetUnset);
ZEND_METHOD(WeakMap, count);
ZEND_METHOD(WeakMap, getIterator);
static const zend_function_entry class_WeakReference_methods[] = {
@ -55,5 +59,6 @@ static const zend_function_entry class_WeakMap_methods[] = {
ZEND_ME(WeakMap, offsetExists, arginfo_class_WeakMap_offsetExists, ZEND_ACC_PUBLIC)
ZEND_ME(WeakMap, offsetUnset, arginfo_class_WeakMap_offsetUnset, ZEND_ACC_PUBLIC)
ZEND_ME(WeakMap, count, arginfo_class_WeakMap_count, ZEND_ACC_PUBLIC)
ZEND_ME(WeakMap, getIterator, arginfo_class_WeakMap_getIterator, ZEND_ACC_PUBLIC)
ZEND_FE_END
};

View File

@ -1691,7 +1691,7 @@ static void date_register_classes(void) /* {{{ */
ce_period.create_object = date_object_new_period;
date_ce_period = zend_register_internal_class_ex(&ce_period, NULL);
date_ce_period->get_iterator = date_object_period_get_iterator;
zend_class_implements(date_ce_period, 1, zend_ce_traversable);
zend_class_implements(date_ce_period, 1, zend_ce_aggregate);
memcpy(&date_object_handlers_period, &std_object_handlers, sizeof(zend_object_handlers));
date_object_handlers_period.offset = XtOffsetOf(php_period_obj, std);
date_object_handlers_period.free_obj = date_object_free_storage_period;
@ -4372,6 +4372,13 @@ PHP_METHOD(DatePeriod, getRecurrences)
}
/* }}} */
PHP_METHOD(DatePeriod, getIterator)
{
ZEND_PARSE_PARAMETERS_NONE();
zend_create_internal_iterator_zval(return_value, ZEND_THIS);
}
static int check_id_allowed(char *id, zend_long what) /* {{{ */
{
if ((what & PHP_DATE_TIMEZONE_GROUP_AFRICA) && strncasecmp(id, "Africa/", 7) == 0) return 1;

View File

@ -391,7 +391,7 @@ class DateInterval
public static function __set_state(array $array) {}
}
class DatePeriod implements Traversable
class DatePeriod implements IteratorAggregate
{
/* Has an overloaded signature */
public function __construct($start, $interval = UNKNOWN, $end = UNKNOWN) {}
@ -412,4 +412,6 @@ class DatePeriod implements Traversable
/** @return DatePeriod */
public static function __set_state(array $array) {}
public function getIterator(): Iterator {}
}

View File

@ -423,6 +423,9 @@ ZEND_END_ARG_INFO()
#define arginfo_class_DatePeriod___set_state arginfo_class_DateTime___set_state
ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_DatePeriod_getIterator, 0, 0, Iterator, 0)
ZEND_END_ARG_INFO()
ZEND_FUNCTION(strtotime);
ZEND_FUNCTION(date);
@ -503,6 +506,7 @@ ZEND_METHOD(DatePeriod, getDateInterval);
ZEND_METHOD(DatePeriod, getRecurrences);
ZEND_METHOD(DatePeriod, __wakeup);
ZEND_METHOD(DatePeriod, __set_state);
ZEND_METHOD(DatePeriod, getIterator);
static const zend_function_entry ext_functions[] = {
@ -651,5 +655,6 @@ static const zend_function_entry class_DatePeriod_methods[] = {
ZEND_ME(DatePeriod, getRecurrences, arginfo_class_DatePeriod_getRecurrences, ZEND_ACC_PUBLIC)
ZEND_ME(DatePeriod, __wakeup, arginfo_class_DatePeriod___wakeup, ZEND_ACC_PUBLIC)
ZEND_ME(DatePeriod, __set_state, arginfo_class_DatePeriod___set_state, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
ZEND_ME(DatePeriod, getIterator, arginfo_class_DatePeriod_getIterator, ZEND_ACC_PUBLIC)
ZEND_FE_END
};

View File

@ -0,0 +1,88 @@
--TEST--
DatePeriod can be used as an IteratorAggregate
--FILE--
<?php
$period = new DatePeriod('R2/2012-07-01T00:00:00Z/P7D');
foreach ($period as $i => $date) {
echo "$i: ", $date->format('Y-m-d'), "\n";
}
echo "\n";
foreach ($period->getIterator() as $i => $date) {
echo "$i: ", $date->format('Y-m-d'), "\n";
}
echo "\n";
$iter = $period->getIterator();
for (; $iter->valid(); $iter->next()) {
$i = $iter->key();
$date = $iter->current();
echo "$i: ", $date->format('Y-m-d'), "\n";
}
echo "\n";
$iter->rewind();
for (; $iter->valid(); $iter->next()) {
$i = $iter->key();
$date = $iter->current();
echo "$i: ", $date->format('Y-m-d'), "\n";
}
echo "\n";
foreach (new IteratorIterator($period) as $i => $date) {
echo "$i: ", $date->format('Y-m-d'), "\n";
}
// Extension that does not overwrite getIterator().
class MyDatePeriod1 extends DatePeriod {
}
echo "\n";
$period = new MyDatePeriod1('R2/2012-07-01T00:00:00Z/P7D');
foreach ($period as $i => $date) {
echo "$i: ", $date->format('Y-m-d'), "\n";
}
// Extension that does overwrite getIterator().
class MyDatePeriod2 extends DatePeriod {
public function getIterator(): Iterator {
return new ArrayIterator([1, 2, 3]);
}
}
echo "\n";
$period = new MyDatePeriod2('R2/2012-07-01T00:00:00Z/P7D');
foreach ($period as $i => $notDate) {
echo "$i: $notDate\n";
}
?>
--EXPECT--
0: 2012-07-01
1: 2012-07-08
2: 2012-07-15
0: 2012-07-01
1: 2012-07-08
2: 2012-07-15
0: 2012-07-01
1: 2012-07-08
2: 2012-07-15
0: 2012-07-01
1: 2012-07-08
2: 2012-07-15
0: 2012-07-01
1: 2012-07-08
2: 2012-07-15
0: 2012-07-01
1: 2012-07-08
2: 2012-07-15
0: 1
1: 2
2: 3

View File

@ -22,6 +22,7 @@
#include "php.h"
#if defined(HAVE_LIBXML) && defined(HAVE_DOM)
#include "php_dom.h"
#include "zend_interfaces.h"
/*
* class DOMNamedNodeMap
@ -266,4 +267,13 @@ PHP_METHOD(DOMNamedNodeMap, count)
}
/* }}} end dom_namednodemap_count */
PHP_METHOD(DOMNamedNodeMap, getIterator)
{
if (zend_parse_parameters_none() == FAILURE) {
return;
}
zend_create_internal_iterator_zval(return_value, ZEND_THIS);
}
#endif

View File

@ -22,6 +22,7 @@
#include "php.h"
#if defined(HAVE_LIBXML) && defined(HAVE_DOM)
#include "php_dom.h"
#include "zend_interfaces.h"
/*
* class DOMNodeList
@ -175,5 +176,13 @@ PHP_METHOD(DOMNodeList, item)
}
/* }}} end dom_nodelist_item */
ZEND_METHOD(DOMNodeList, getIterator)
{
if (zend_parse_parameters_none() == FAILURE) {
return;
}
zend_create_internal_iterator_zval(return_value, ZEND_THIS);
}
#endif

View File

@ -670,7 +670,7 @@ PHP_MINIT_FUNCTION(dom)
ce.create_object = dom_nnodemap_objects_new;
dom_nodelist_class_entry = zend_register_internal_class_ex(&ce, NULL);
dom_nodelist_class_entry->get_iterator = php_dom_get_iterator;
zend_class_implements(dom_nodelist_class_entry, 2, zend_ce_traversable, zend_ce_countable);
zend_class_implements(dom_nodelist_class_entry, 2, zend_ce_aggregate, zend_ce_countable);
zend_hash_init(&dom_nodelist_prop_handlers, 0, NULL, dom_dtor_prop_handler, 1);
dom_register_prop_handler(&dom_nodelist_prop_handlers, "length", sizeof("length")-1, dom_nodelist_length_read, NULL);
@ -680,7 +680,7 @@ PHP_MINIT_FUNCTION(dom)
ce.create_object = dom_nnodemap_objects_new;
dom_namednodemap_class_entry = zend_register_internal_class_ex(&ce, NULL);
dom_namednodemap_class_entry->get_iterator = php_dom_get_iterator;
zend_class_implements(dom_namednodemap_class_entry, 2, zend_ce_traversable, zend_ce_countable);
zend_class_implements(dom_namednodemap_class_entry, 2, zend_ce_aggregate, zend_ce_countable);
zend_hash_init(&dom_namednodemap_prop_handlers, 0, NULL, dom_dtor_prop_handler, 1);
dom_register_prop_handler(&dom_namednodemap_prop_handlers, "length", sizeof("length")-1, dom_namednodemap_length_read, NULL);

View File

@ -123,12 +123,14 @@ class DOMDocumentFragment implements DOMParentNode
public function prepend(...$nodes): void {}
}
class DOMNodeList
class DOMNodeList implements IteratorAggregate, Countable
{
/** @return int|false */
public function count() {}
/** @return DOMNode|null */
public function getIterator(): Iterator {}
/** @return ?DOMNode */
public function item(int $index) {}
}
@ -374,7 +376,7 @@ class DOMText
public function splitText(int $offset) {}
}
class DOMNamedNodeMap
class DOMNamedNodeMap implements IteratorAggregate, Countable
{
/** @return DOMNode|null */
public function getNamedItem(string $name) {}
@ -387,6 +389,8 @@ class DOMNamedNodeMap
/** @return int|false */
public function count() {}
public function getIterator(): Iterator {}
}
class DOMEntity extends DOMNode

View File

@ -123,6 +123,9 @@ ZEND_END_ARG_INFO()
#define arginfo_class_DOMNodeList_count arginfo_class_DOMNode_getLineNo
ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_DOMNodeList_getIterator, 0, 0, Iterator, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_DOMNodeList_item, 0, 0, 1)
ZEND_ARG_TYPE_INFO(0, index, IS_LONG, 0)
ZEND_END_ARG_INFO()
@ -404,6 +407,8 @@ ZEND_END_ARG_INFO()
#define arginfo_class_DOMNamedNodeMap_count arginfo_class_DOMNode_getLineNo
#define arginfo_class_DOMNamedNodeMap_getIterator arginfo_class_DOMNodeList_getIterator
#define arginfo_class_DOMEntityReference___construct arginfo_class_DOMElement_getAttribute
#define arginfo_class_DOMProcessingInstruction___construct arginfo_class_DOMAttr___construct
@ -470,6 +475,7 @@ ZEND_METHOD(DOMDocumentFragment, appendXML);
ZEND_METHOD(DOMDocumentFragment, append);
ZEND_METHOD(DOMDocumentFragment, prepend);
ZEND_METHOD(DOMNodeList, count);
ZEND_METHOD(DOMNodeList, getIterator);
ZEND_METHOD(DOMNodeList, item);
ZEND_METHOD(DOMCharacterData, appendData);
ZEND_METHOD(DOMCharacterData, substringData);
@ -564,6 +570,7 @@ ZEND_METHOD(DOMNamedNodeMap, getNamedItem);
ZEND_METHOD(DOMNamedNodeMap, getNamedItemNS);
ZEND_METHOD(DOMNamedNodeMap, item);
ZEND_METHOD(DOMNamedNodeMap, count);
ZEND_METHOD(DOMNamedNodeMap, getIterator);
ZEND_METHOD(DOMEntityReference, __construct);
ZEND_METHOD(DOMProcessingInstruction, __construct);
#if defined(LIBXML_XPATH_ENABLED)
@ -664,6 +671,7 @@ static const zend_function_entry class_DOMDocumentFragment_methods[] = {
static const zend_function_entry class_DOMNodeList_methods[] = {
ZEND_ME(DOMNodeList, count, arginfo_class_DOMNodeList_count, ZEND_ACC_PUBLIC)
ZEND_ME(DOMNodeList, getIterator, arginfo_class_DOMNodeList_getIterator, ZEND_ACC_PUBLIC)
ZEND_ME(DOMNodeList, item, arginfo_class_DOMNodeList_item, ZEND_ACC_PUBLIC)
ZEND_FE_END
};
@ -794,6 +802,7 @@ static const zend_function_entry class_DOMNamedNodeMap_methods[] = {
ZEND_ME(DOMNamedNodeMap, getNamedItemNS, arginfo_class_DOMNamedNodeMap_getNamedItemNS, ZEND_ACC_PUBLIC)
ZEND_ME(DOMNamedNodeMap, item, arginfo_class_DOMNamedNodeMap_item, ZEND_ACC_PUBLIC)
ZEND_ME(DOMNamedNodeMap, count, arginfo_class_DOMNamedNodeMap_count, ZEND_ACC_PUBLIC)
ZEND_ME(DOMNamedNodeMap, getIterator, arginfo_class_DOMNamedNodeMap_getIterator, ZEND_ACC_PUBLIC)
ZEND_FE_END
};

View File

@ -2,7 +2,7 @@
/** @generate-function-entries */
class IntlBreakIterator implements Traversable
class IntlBreakIterator implements IteratorAggregate
{
/** @return IntlBreakIterator|null */
public static function createCharacterInstance(?string $locale = null) {}
@ -65,6 +65,8 @@ class IntlBreakIterator implements Traversable
/** @return bool|null */
public function setText(string $text) {}
public function getIterator(): Iterator {}
}
class IntlRuleBasedBreakIterator extends IntlBreakIterator

View File

@ -56,6 +56,9 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_IntlBreakIterator_setText, 0, 0, 1)
ZEND_ARG_TYPE_INFO(0, text, IS_STRING, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_IntlBreakIterator_getIterator, 0, 0, Iterator, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_IntlRuleBasedBreakIterator___construct, 0, 0, 1)
ZEND_ARG_TYPE_INFO(0, rules, IS_STRING, 0)
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, areCompiled, _IS_BOOL, 0, "false")
@ -95,6 +98,7 @@ ZEND_METHOD(IntlBreakIterator, next);
ZEND_METHOD(IntlBreakIterator, preceding);
ZEND_METHOD(IntlBreakIterator, previous);
ZEND_METHOD(IntlBreakIterator, setText);
ZEND_METHOD(IntlBreakIterator, getIterator);
ZEND_METHOD(IntlRuleBasedBreakIterator, __construct);
ZEND_METHOD(IntlRuleBasedBreakIterator, getBinaryRules);
ZEND_METHOD(IntlRuleBasedBreakIterator, getRules);
@ -126,6 +130,7 @@ static const zend_function_entry class_IntlBreakIterator_methods[] = {
ZEND_ME(IntlBreakIterator, preceding, arginfo_class_IntlBreakIterator_preceding, ZEND_ACC_PUBLIC)
ZEND_ME(IntlBreakIterator, previous, arginfo_class_IntlBreakIterator_previous, ZEND_ACC_PUBLIC)
ZEND_ME(IntlBreakIterator, setText, arginfo_class_IntlBreakIterator_setText, ZEND_ACC_PUBLIC)
ZEND_ME(IntlBreakIterator, getIterator, arginfo_class_IntlBreakIterator_getIterator, ZEND_ACC_PUBLIC)
ZEND_FE_END
};

View File

@ -236,8 +236,7 @@ U_CFUNC void breakiterator_register_BreakIterator_class(void)
BreakIterator_handlers.get_debug_info = BreakIterator_get_debug_info;
BreakIterator_handlers.free_obj = BreakIterator_objects_free;
zend_class_implements(BreakIterator_ce_ptr, 1,
zend_ce_traversable);
zend_class_implements(BreakIterator_ce_ptr, 1, zend_ce_aggregate);
zend_declare_class_constant_long(BreakIterator_ce_ptr,
"DONE", sizeof("DONE") - 1, BreakIterator::DONE );

View File

@ -27,6 +27,7 @@ extern "C" {
#include "breakiterator_class.h"
#include "../locale/locale.h"
#include <zend_exceptions.h>
#include <zend_interfaces.h>
}
using PHP::CodePointBreakIterator;
@ -399,3 +400,12 @@ U_CFUNC PHP_METHOD(IntlBreakIterator, getErrorMessage)
message = intl_error_get_message(BREAKITER_ERROR_P(bio));
RETURN_STR(message);
}
U_CFUNC PHP_METHOD(IntlBreakIterator, getIterator)
{
if (zend_parse_parameters_none() == FAILURE) {
return;
}
zend_create_internal_iterator_zval(return_value, ZEND_THIS);
}

View File

@ -2,7 +2,7 @@
/** @generate-function-entries */
class ResourceBundle implements Traversable
class ResourceBundle implements IteratorAggregate, Countable
{
public function __construct(?string $locale, ?string $bundlename, bool $fallback = true) {}
@ -42,4 +42,6 @@ class ResourceBundle implements Traversable
* @alias resourcebundle_get_error_message
*/
public function getErrorMessage() {}
public function getIterator(): Iterator {}
}

View File

@ -25,6 +25,9 @@ ZEND_END_ARG_INFO()
#define arginfo_class_ResourceBundle_getErrorMessage arginfo_class_ResourceBundle_count
ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_ResourceBundle_getIterator, 0, 0, Iterator, 0)
ZEND_END_ARG_INFO()
ZEND_METHOD(ResourceBundle, __construct);
ZEND_FUNCTION(resourcebundle_create);
@ -33,6 +36,7 @@ ZEND_FUNCTION(resourcebundle_count);
ZEND_FUNCTION(resourcebundle_locales);
ZEND_FUNCTION(resourcebundle_get_error_code);
ZEND_FUNCTION(resourcebundle_get_error_message);
ZEND_METHOD(ResourceBundle, getIterator);
static const zend_function_entry class_ResourceBundle_methods[] = {
@ -43,5 +47,6 @@ static const zend_function_entry class_ResourceBundle_methods[] = {
ZEND_ME_MAPPING(getLocales, resourcebundle_locales, arginfo_class_ResourceBundle_getLocales, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
ZEND_ME_MAPPING(getErrorCode, resourcebundle_get_error_code, arginfo_class_ResourceBundle_getErrorCode, ZEND_ACC_PUBLIC)
ZEND_ME_MAPPING(getErrorMessage, resourcebundle_get_error_message, arginfo_class_ResourceBundle_getErrorMessage, ZEND_ACC_PUBLIC)
ZEND_ME(ResourceBundle, getIterator, arginfo_class_ResourceBundle_getIterator, ZEND_ACC_PUBLIC)
ZEND_FE_END
};

View File

@ -368,6 +368,14 @@ PHP_FUNCTION( resourcebundle_get_error_message )
}
/* }}} */
PHP_METHOD(ResourceBundle, getIterator) {
if (zend_parse_parameters_none() == FAILURE) {
return;
}
zend_create_internal_iterator_zval(return_value, ZEND_THIS);
}
/* {{{ resourcebundle_register_class
* Initialize 'ResourceBundle' class
*/
@ -389,6 +397,6 @@ void resourcebundle_register_class( void )
ResourceBundle_object_handlers.read_dimension = resourcebundle_array_get;
ResourceBundle_object_handlers.count_elements = resourcebundle_array_count;
zend_class_implements(ResourceBundle_ce_ptr, 2, zend_ce_traversable, zend_ce_countable);
zend_class_implements(ResourceBundle_ce_ptr, 2, zend_ce_aggregate, zend_ce_countable);
}
/* }}} */

View File

@ -636,7 +636,7 @@ PHP_MINIT_FUNCTION(mysqli)
zend_declare_property_null(ce, "num_rows", sizeof("num_rows") - 1, ZEND_ACC_PUBLIC);
zend_declare_property_null(ce, "type", sizeof("type") - 1, ZEND_ACC_PUBLIC);
mysqli_result_class_entry->get_iterator = php_mysqli_result_get_iterator;
zend_class_implements(mysqli_result_class_entry, 1, zend_ce_traversable);
zend_class_implements(mysqli_result_class_entry, 1, zend_ce_aggregate);
zend_hash_add_ptr(&classes, ce->name, &mysqli_result_properties);
REGISTER_MYSQLI_CLASS_ENTRY("mysqli_stmt", mysqli_stmt_class_entry, class_mysqli_stmt_methods);
@ -1087,6 +1087,15 @@ PHP_FUNCTION(mysqli_result_construct)
}
/* }}} */
PHP_METHOD(mysqli_result, getIterator)
{
if (zend_parse_parameters_none() == FAILURE) {
return;
}
zend_create_internal_iterator_zval(return_value, ZEND_THIS);
}
/* {{{ php_mysqli_fetch_into_hash_aux
*/
void php_mysqli_fetch_into_hash_aux(zval *return_value, MYSQL_RES * result, zend_long fetchtype)

View File

@ -300,7 +300,7 @@ class mysqli
public function refresh(int $options) {}
}
class mysqli_result
class mysqli_result implements IteratorAggregate
{
/** @alias mysqli_result_construct */
public function __construct(object $mysqli_link, int $resmode = MYSQLI_STORE_RESULT) {}
@ -384,6 +384,8 @@ class mysqli_result
* @alias mysqli_free_result
*/
public function free_result() {}
public function getIterator(): Iterator;
}
class mysqli_stmt

View File

@ -618,6 +618,9 @@ ZEND_END_ARG_INFO()
#define arginfo_class_mysqli_result_free_result arginfo_class_mysqli_character_set_name
ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_mysqli_result_getIterator, 0, 0, Iterator, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_mysqli_stmt___construct, 0, 0, 1)
ZEND_ARG_OBJ_INFO(0, mysqli_link, mysqli, 0)
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, statement, IS_STRING, 1, "null")
@ -803,6 +806,7 @@ ZEND_FUNCTION(mysqli_result_construct);
#if defined(MYSQLI_USE_MYSQLND)
ZEND_FUNCTION(mysqli_fetch_all);
#endif
ZEND_METHOD(mysqli_result, getIterator);
ZEND_FUNCTION(mysqli_stmt_construct);
#if defined(MYSQLI_USE_MYSQLND)
ZEND_FUNCTION(mysqli_stmt_more_results);
@ -1002,6 +1006,7 @@ static const zend_function_entry class_mysqli_result_methods[] = {
ZEND_ME_MAPPING(fetch_row, mysqli_fetch_row, arginfo_class_mysqli_result_fetch_row, ZEND_ACC_PUBLIC)
ZEND_ME_MAPPING(field_seek, mysqli_field_seek, arginfo_class_mysqli_result_field_seek, ZEND_ACC_PUBLIC)
ZEND_ME_MAPPING(free_result, mysqli_free_result, arginfo_class_mysqli_result_free_result, ZEND_ACC_PUBLIC)
ZEND_ME(mysqli_result, getIterator, arginfo_class_mysqli_result_getIterator, ZEND_ACC_PUBLIC)
ZEND_FE_END
};

View File

@ -38,6 +38,7 @@ require_once('skipifconnectfailure.inc');
'field_seek' => true,
'free' => true,
'free_result' => true,
'getIterator' => true,
);
if ($IS_MYSQLND)
$expected_methods['fetch_all'] = true;

View File

@ -2072,6 +2072,15 @@ PHP_METHOD(PDOStatement, debugDumpParams)
}
/* }}} */
PHP_METHOD(PDOStatement, getIterator)
{
if (zend_parse_parameters_none() == FAILURE) {
return;
}
zend_create_internal_iterator_zval(return_value, ZEND_THIS);
}
/* {{{ overloaded handlers for PDOStatement class */
static zval *dbstmt_prop_write(zend_object *object, zend_string *name, zval *value, void **cache_slot)
{
@ -2583,7 +2592,7 @@ void pdo_stmt_init(void)
pdo_dbstmt_ce->create_object = pdo_dbstmt_new;
pdo_dbstmt_ce->serialize = zend_class_serialize_deny;
pdo_dbstmt_ce->unserialize = zend_class_unserialize_deny;
zend_class_implements(pdo_dbstmt_ce, 1, zend_ce_traversable);
zend_class_implements(pdo_dbstmt_ce, 1, zend_ce_aggregate);
zend_declare_property_null(pdo_dbstmt_ce, "queryString", sizeof("queryString")-1, ZEND_ACC_PUBLIC);
memcpy(&pdo_dbstmt_object_handlers, &std_object_handlers, sizeof(zend_object_handlers));

View File

@ -2,7 +2,7 @@
/** @generate-function-entries */
class PDOStatement implements Traversable
class PDOStatement implements IteratorAggregate
{
/**
* @param mixed $driverdata
@ -76,6 +76,8 @@ class PDOStatement implements Traversable
/** @return bool */
public function setFetchMode(int $mode, $param1 = UNKNOWN, $param2 = UNKNOWN) {}
public function getIterator(): Iterator {}
}
final class PDORow

View File

@ -82,6 +82,9 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_PDOStatement_setFetchMode, 0, 0, 1)
ZEND_ARG_INFO(0, param2)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_PDOStatement_getIterator, 0, 0, Iterator, 0)
ZEND_END_ARG_INFO()
ZEND_METHOD(PDOStatement, bindColumn);
ZEND_METHOD(PDOStatement, bindParam);
@ -102,6 +105,7 @@ ZEND_METHOD(PDOStatement, nextRowset);
ZEND_METHOD(PDOStatement, rowCount);
ZEND_METHOD(PDOStatement, setAttribute);
ZEND_METHOD(PDOStatement, setFetchMode);
ZEND_METHOD(PDOStatement, getIterator);
static const zend_function_entry class_PDOStatement_methods[] = {
@ -124,6 +128,7 @@ static const zend_function_entry class_PDOStatement_methods[] = {
ZEND_ME(PDOStatement, rowCount, arginfo_class_PDOStatement_rowCount, ZEND_ACC_PUBLIC)
ZEND_ME(PDOStatement, setAttribute, arginfo_class_PDOStatement_setAttribute, ZEND_ACC_PUBLIC)
ZEND_ME(PDOStatement, setFetchMode, arginfo_class_PDOStatement_setFetchMode, ZEND_ACC_PUBLIC)
ZEND_ME(PDOStatement, getIterator, arginfo_class_PDOStatement_getIterator, ZEND_ACC_PUBLIC)
ZEND_FE_END
};

View File

@ -51,7 +51,7 @@ class PDOStatementAggregate extends PDOStatement implements IteratorAggregate
/* default fetch mode is BOTH, so we see if the ctor can overwrite that */
}
function getIterator()
function getIterator(): Iterator
{
echo __METHOD__ . "\n";
$this->execute();