diff options
author | Nikita Popov <nikita.ppv@gmail.com> | 2020-02-26 16:42:49 +0100 |
---|---|---|
committer | Nikita Popov <nikita.ppv@gmail.com> | 2020-06-24 15:31:41 +0200 |
commit | ff19ec2df3ef0dca8c29be83eddcde58234f4095 (patch) | |
tree | bc6477f4f4e696cf599ec82a0a6dbf221bc5bc10 | |
parent | 4730b06f1d026047c63980298d358e28e2183de6 (diff) | |
download | php-git-ff19ec2df3ef0dca8c29be83eddcde58234f4095.tar.gz |
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.
33 files changed, 488 insertions, 30 deletions
@@ -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. diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS index 9c43c42377..60b1323b24 100644 --- a/UPGRADING.INTERNALS +++ b/UPGRADING.INTERNALS @@ -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 ======================== diff --git a/Zend/zend_interfaces.c b/Zend/zend_interfaces.c index 6ac2721f45..6d35e7159c 100644 --- a/Zend/zend_interfaces.c +++ b/Zend/zend_interfaces.c @@ -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; } /* }}} */ diff --git a/Zend/zend_interfaces.h b/Zend/zend_interfaces.h index fd3275b2a4..3f3f39b087 100644 --- a/Zend/zend_interfaces.h +++ b/Zend/zend_interfaces.h @@ -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 */ diff --git a/Zend/zend_interfaces.stub.php b/Zend/zend_interfaces.stub.php index 2865aace08..e10a343d6e 100644 --- a/Zend/zend_interfaces.stub.php +++ b/Zend/zend_interfaces.stub.php @@ -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; +} diff --git a/Zend/zend_interfaces_arginfo.h b/Zend/zend_interfaces_arginfo.h index 1c197d73db..ff8f0be9f6 100644 --- a/Zend/zend_interfaces_arginfo.h +++ b/Zend/zend_interfaces_arginfo.h @@ -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 +}; diff --git a/Zend/zend_weakrefs.c b/Zend/zend_weakrefs.c index 0e94617dbf..5ad84193fa 100644 --- a/Zend/zend_weakrefs.c +++ b/Zend/zend_weakrefs.c @@ -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; diff --git a/Zend/zend_weakrefs.stub.php b/Zend/zend_weakrefs.stub.php index 4cf189b064..8cf0df5cf6 100644 --- a/Zend/zend_weakrefs.stub.php +++ b/Zend/zend_weakrefs.stub.php @@ -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 {} } diff --git a/Zend/zend_weakrefs_arginfo.h b/Zend/zend_weakrefs_arginfo.h index 3f668b4e08..1f077dea2d 100644 --- a/Zend/zend_weakrefs_arginfo.h +++ b/Zend/zend_weakrefs_arginfo.h @@ -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 }; diff --git a/ext/date/php_date.c b/ext/date/php_date.c index 94e8ab50c7..0b4520566f 100644 --- a/ext/date/php_date.c +++ b/ext/date/php_date.c @@ -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; diff --git a/ext/date/php_date.stub.php b/ext/date/php_date.stub.php index f360eeee0a..c6a03ac715 100644 --- a/ext/date/php_date.stub.php +++ b/ext/date/php_date.stub.php @@ -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 {} } diff --git a/ext/date/php_date_arginfo.h b/ext/date/php_date_arginfo.h index 5c002c2b37..9c1e58926b 100644 --- a/ext/date/php_date_arginfo.h +++ b/ext/date/php_date_arginfo.h @@ -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 }; diff --git a/ext/date/tests/DatePeriod_IteratorAggregate.phpt b/ext/date/tests/DatePeriod_IteratorAggregate.phpt new file mode 100644 index 0000000000..f3221822b2 --- /dev/null +++ b/ext/date/tests/DatePeriod_IteratorAggregate.phpt @@ -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 diff --git a/ext/dom/namednodemap.c b/ext/dom/namednodemap.c index 18775d7ef1..943a3e1156 100644 --- a/ext/dom/namednodemap.c +++ b/ext/dom/namednodemap.c @@ -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 diff --git a/ext/dom/nodelist.c b/ext/dom/nodelist.c index 502c75d0aa..723024ba96 100644 --- a/ext/dom/nodelist.c +++ b/ext/dom/nodelist.c @@ -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 diff --git a/ext/dom/php_dom.c b/ext/dom/php_dom.c index c90475df1a..218bf1972f 100644 --- a/ext/dom/php_dom.c +++ b/ext/dom/php_dom.c @@ -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); diff --git a/ext/dom/php_dom.stub.php b/ext/dom/php_dom.stub.php index a254df125c..5b553b8035 100644 --- a/ext/dom/php_dom.stub.php +++ b/ext/dom/php_dom.stub.php @@ -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 diff --git a/ext/dom/php_dom_arginfo.h b/ext/dom/php_dom_arginfo.h index 5e30511118..265782b50a 100644 --- a/ext/dom/php_dom_arginfo.h +++ b/ext/dom/php_dom_arginfo.h @@ -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 }; diff --git a/ext/intl/breakiterator/breakiterator.stub.php b/ext/intl/breakiterator/breakiterator.stub.php index fc26215f7d..341e9df1f3 100644 --- a/ext/intl/breakiterator/breakiterator.stub.php +++ b/ext/intl/breakiterator/breakiterator.stub.php @@ -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 diff --git a/ext/intl/breakiterator/breakiterator_arginfo.h b/ext/intl/breakiterator/breakiterator_arginfo.h index 6c9f574d1e..0f9b57e8c7 100644 --- a/ext/intl/breakiterator/breakiterator_arginfo.h +++ b/ext/intl/breakiterator/breakiterator_arginfo.h @@ -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 }; diff --git a/ext/intl/breakiterator/breakiterator_class.cpp b/ext/intl/breakiterator/breakiterator_class.cpp index 6267eb0fa2..2114ada558 100644 --- a/ext/intl/breakiterator/breakiterator_class.cpp +++ b/ext/intl/breakiterator/breakiterator_class.cpp @@ -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 ); diff --git a/ext/intl/breakiterator/breakiterator_methods.cpp b/ext/intl/breakiterator/breakiterator_methods.cpp index 7e920fa39c..393290b66e 100644 --- a/ext/intl/breakiterator/breakiterator_methods.cpp +++ b/ext/intl/breakiterator/breakiterator_methods.cpp @@ -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); +} diff --git a/ext/intl/resourcebundle/resourcebundle.stub.php b/ext/intl/resourcebundle/resourcebundle.stub.php index da3bb186fa..6f91893bf3 100644 --- a/ext/intl/resourcebundle/resourcebundle.stub.php +++ b/ext/intl/resourcebundle/resourcebundle.stub.php @@ -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 {} } diff --git a/ext/intl/resourcebundle/resourcebundle_arginfo.h b/ext/intl/resourcebundle/resourcebundle_arginfo.h index 4060cdea59..44238d2a1d 100644 --- a/ext/intl/resourcebundle/resourcebundle_arginfo.h +++ b/ext/intl/resourcebundle/resourcebundle_arginfo.h @@ -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 }; diff --git a/ext/intl/resourcebundle/resourcebundle_class.c b/ext/intl/resourcebundle/resourcebundle_class.c index 07bd59b488..d2c07d0671 100644 --- a/ext/intl/resourcebundle/resourcebundle_class.c +++ b/ext/intl/resourcebundle/resourcebundle_class.c @@ -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); } /* }}} */ diff --git a/ext/mysqli/mysqli.c b/ext/mysqli/mysqli.c index eb3c79b798..ec7d296b24 100644 --- a/ext/mysqli/mysqli.c +++ b/ext/mysqli/mysqli.c @@ -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) diff --git a/ext/mysqli/mysqli.stub.php b/ext/mysqli/mysqli.stub.php index 24224b26e7..e8af0485dc 100644 --- a/ext/mysqli/mysqli.stub.php +++ b/ext/mysqli/mysqli.stub.php @@ -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 diff --git a/ext/mysqli/mysqli_arginfo.h b/ext/mysqli/mysqli_arginfo.h index cb307a6743..3d1fb92aee 100644 --- a/ext/mysqli/mysqli_arginfo.h +++ b/ext/mysqli/mysqli_arginfo.h @@ -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 }; diff --git a/ext/mysqli/tests/mysqli_class_mysqli_result_interface.phpt b/ext/mysqli/tests/mysqli_class_mysqli_result_interface.phpt index 3dc73a0819..6e673984f4 100644 --- a/ext/mysqli/tests/mysqli_class_mysqli_result_interface.phpt +++ b/ext/mysqli/tests/mysqli_class_mysqli_result_interface.phpt @@ -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; diff --git a/ext/pdo/pdo_stmt.c b/ext/pdo/pdo_stmt.c index 8bdd52d906..d367884148 100644 --- a/ext/pdo/pdo_stmt.c +++ b/ext/pdo/pdo_stmt.c @@ -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)); diff --git a/ext/pdo/pdo_stmt.stub.php b/ext/pdo/pdo_stmt.stub.php index fe87ffc40d..1407fcfc62 100644 --- a/ext/pdo/pdo_stmt.stub.php +++ b/ext/pdo/pdo_stmt.stub.php @@ -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 diff --git a/ext/pdo/pdo_stmt_arginfo.h b/ext/pdo/pdo_stmt_arginfo.h index f1b0f2fe06..fc97fca039 100644 --- a/ext/pdo/pdo_stmt_arginfo.h +++ b/ext/pdo/pdo_stmt_arginfo.h @@ -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 }; diff --git a/ext/pdo/tests/pdo_014.phpt b/ext/pdo/tests/pdo_014.phpt index ef47fd89d0..c544e8a9ef 100644 --- a/ext/pdo/tests/pdo_014.phpt +++ b/ext/pdo/tests/pdo_014.phpt @@ -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(); |