]> granicus.if.org Git - php/commitdiff
Introduce InternalIterator
authorNikita Popov <nikita.ppv@gmail.com>
Wed, 26 Feb 2020 15:42:49 +0000 (16:42 +0100)
committerNikita Popov <nikita.ppv@gmail.com>
Wed, 24 Jun 2020 13:31:41 +0000 (15:31 +0200)
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:
UPGRADING
UPGRADING.INTERNALS
Zend/zend_interfaces.c
Zend/zend_interfaces.h
Zend/zend_interfaces.stub.php
Zend/zend_interfaces_arginfo.h
Zend/zend_weakrefs.c
Zend/zend_weakrefs.stub.php
Zend/zend_weakrefs_arginfo.h
ext/date/php_date.c
ext/date/php_date.stub.php
ext/date/php_date_arginfo.h
ext/date/tests/DatePeriod_IteratorAggregate.phpt [new file with mode: 0644]
ext/dom/namednodemap.c
ext/dom/nodelist.c
ext/dom/php_dom.c
ext/dom/php_dom.stub.php
ext/dom/php_dom_arginfo.h
ext/intl/breakiterator/breakiterator.stub.php
ext/intl/breakiterator/breakiterator_arginfo.h
ext/intl/breakiterator/breakiterator_class.cpp
ext/intl/breakiterator/breakiterator_methods.cpp
ext/intl/resourcebundle/resourcebundle.stub.php
ext/intl/resourcebundle/resourcebundle_arginfo.h
ext/intl/resourcebundle/resourcebundle_class.c
ext/mysqli/mysqli.c
ext/mysqli/mysqli.stub.php
ext/mysqli/mysqli_arginfo.h
ext/mysqli/tests/mysqli_class_mysqli_result_interface.phpt
ext/pdo/pdo_stmt.c
ext/pdo/pdo_stmt.stub.php
ext/pdo/pdo_stmt_arginfo.h
ext/pdo/tests/pdo_014.phpt

index 744fead12a74f33b42bd01acdf473bf97d9d8e41..baad2ce1f886f1d44abd7c7267f019619b5af19e 100644 (file)
--- 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.
index 9c43c4237726a2a3814b67b40735e8aadf9f1e10..60b1323b24e373fe8ef2f8f30580201888f14e32 100644 (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
 ========================
index 6ac2721f453d5b978c42cf49563eedce87529a21..6d35e7159c936b7059b7b3917b4e4187856d9cce 100644 (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;
 }
 /* }}} */
index fd3275b2a48e1ad21da0826bd11dbed5fcfe6a24..3f3f39b0877f471ebac9ad34a29b440b530c2f4e 100644 (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 */
index 2865aace08fae05bd9773732aea28a3b0f795e7f..e10a343d6ed7cfe4862d61a1a06ade7af7bb62b7 100644 (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;
+}
index 1c197d73db7d26ebe8a1914533f1c67ec6d00a9b..ff8f0be9f6f2b9a335926a5bdebc618009e3a5e9 100644 (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
+};
index 0e94617dbfa5f83ccffd3da953499bf550b3b632..5ad84193fa5fd4fdec9a3f48e6f6b05365e3e7be 100644 (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;
index 4cf189b064b2ff83ce075a9555c38a4c8de66af2..8cf0df5cf6d8ea5878e8e66bcca884f21ad9e291 100644 (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 {}
 }
index 3f668b4e08672991f66e107577b8fd12aa3a9a1d..1f077dea2d0d6d18f67651ea66fd60ac115cde6e 100644 (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
 };
index 94e8ab50c7124e479befa2f5436e7017ac7fc095..0b4520566fe0dba109f8335295094f6fc3058398 100644 (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;
index f360eeee0ac9040e4dd356aebcdc5a29251545ec..c6a03ac7152515cc17f39861d1eeba1c1ad12e17 100644 (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 {}
 }
index 5c002c2b37495e6b5d5208374163b01cdece87db..9c1e58926baa2bfd535fd13ccfedcac5f64d36a9 100644 (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
 };
diff --git a/ext/date/tests/DatePeriod_IteratorAggregate.phpt b/ext/date/tests/DatePeriod_IteratorAggregate.phpt
new file mode 100644 (file)
index 0000000..f322182
--- /dev/null
@@ -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
index 18775d7ef13449c79a8b8b2256dcdd7271199d5a..943a3e11560fed8157c071f696af422ec46859f0 100644 (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
index 502c75d0aac98ee5fa8f60748c3e9dc687bb5b38..723024ba965b4fc168cb85cb88d1b2cb6589e252 100644 (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
index c90475df1af2d64750e0537493dc95edbe3f31ca..218bf1972f1b13bd8ef4ac2add84cae5df54944e 100644 (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);
index a254df125c3c4a9deb89a8e1c3ef066dd17ef63c..5b553b803544d265b187dcf4931855a0f36d0ce9 100644 (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
index 5e3051111887bbf225cabaa287e70d5db9bc3c72..265782b50a0f9a7aa895061a1766f34bacfbc736 100644 (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
 };
 
index fc26215f7d230f1c04c56ba252f958f8c8633a06..341e9df1f3569c10ef4b6c1e57fdd85af4c1eb8f 100644 (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
index 6c9f574d1e48a5b21fe4e1a98cbe16bae5764c40..0f9b57e8c74234e151facf68d34a565ae436870b 100644 (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
 };
 
index 6267eb0fa2f5778488521faa652fc15e94a3d233..2114ada5588254ef00e15c1e6a9c7c643cc712b8 100644 (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 );
index 7e920fa39c954acfb0bd125036c1f6032b7c8d89..393290b66e5d94da11fb83c5229fa5ab0c056f68 100644 (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);
+}
index da3bb186faf59bb65f3302919a4f5cd0250b5bac..6f91893bf37a472b00436db18b610247667219e8 100644 (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 {}
 }
index 4060cdea5948fe8e0138f84e1fe9ae3d0d394d5d..44238d2a1de04e24ffed4e69636d9d50323139be 100644 (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
 };
index 07bd59b488532fadb7a4c52cb44b96747efe0151..d2c07d06710620fac1cb0fb116c1cc6a72b0b765 100644 (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);
 }
 /* }}} */
index eb3c79b7985a11686aaf9a2d6095466e035aef58..ec7d296b24413d0a43b205e9b3dce9a9e09dbf46 100644 (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)
index 24224b26e7d58ce930a3eb01bd31c782a382621c..e8af0485dc02edc5168250a0e5396f98015c3505 100644 (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
index cb307a6743dfca8b656db343a3e07b686237aaca..3d1fb92aeeae641b14923d79a664b611492bdec0 100644 (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
 };
 
index 3dc73a0819d6b992e4d45c03b89fe29036c66983..6e673984f41d4a02653ebfd3dc26240909561e9d 100644 (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;
index 8bdd52d906855f31023fc339534d7f2aa0f1968c..d36788414800fd730da5472d1970fe7c2cfa26b6 100644 (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));
index fe87ffc40daa3872c223ce9ded8e0ecdededbb52..1407fcfc6232b0507cc63e40f24016dba878f3ac 100644 (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
index f1b0f2fe06ad8488b8830ba6e93c62595bc7c28f..fc97fca039efe1696250f8f8691111584c100d90 100644 (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
 };
 
index ef47fd89d09f586fdbc8591200f6492da8e12a9b..c544e8a9efbc72de3b6836593e01944d2052de63 100644 (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();