diff --git a/UPGRADING b/UPGRADING index 744fead12a7..baad2ce1f88 100644 --- a/UPGRADING +++ b/UPGRADING @@ -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 9c43c423772..60b1323b24e 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 6ac2721f453..6d35e7159c9 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 fd3275b2a48..3f3f39b0877 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 2865aace08f..e10a343d6ed 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 1c197d73db7..ff8f0be9f6f 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 0e94617dbfa..5ad84193fa5 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 4cf189b064b..8cf0df5cf6d 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 3f668b4e086..1f077dea2d0 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 94e8ab50c71..0b4520566fe 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 f360eeee0ac..c6a03ac7152 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 5c002c2b374..9c1e58926ba 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 00000000000..f3221822b2b --- /dev/null +++ b/ext/date/tests/DatePeriod_IteratorAggregate.phpt @@ -0,0 +1,88 @@ +--TEST-- +DatePeriod can be used as an IteratorAggregate +--FILE-- + $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 18775d7ef13..943a3e11560 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 502c75d0aac..723024ba965 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 c90475df1af..218bf1972f1 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 a254df125c3..5b553b80354 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 5e305111188..265782b50a0 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 fc26215f7d2..341e9df1f35 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 6c9f574d1e4..0f9b57e8c74 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 6267eb0fa2f..2114ada5588 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 7e920fa39c9..393290b66e5 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 +#include } 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 da3bb186faf..6f91893bf37 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 4060cdea594..44238d2a1de 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 07bd59b4885..d2c07d06710 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 eb3c79b7985..ec7d296b244 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 24224b26e7d..e8af0485dc0 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 cb307a6743d..3d1fb92aeea 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 3dc73a0819d..6e673984f41 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 8bdd52d9068..d3678841480 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 fe87ffc40da..1407fcfc623 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 f1b0f2fe06a..fc97fca039e 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 ef47fd89d09..c544e8a9efb 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();