]> granicus.if.org Git - php/commitdiff
- Add MultipleIterator (Arnaud, Marcus)
authorMarcus Boerger <helly@php.net>
Tue, 22 Jul 2008 22:54:15 +0000 (22:54 +0000)
committerMarcus Boerger <helly@php.net>
Tue, 22 Jul 2008 22:54:15 +0000 (22:54 +0000)
ext/spl/internal/multipleiterator.inc [moved from ext/spl/examples/multipleiterator.inc with 99% similarity]
ext/spl/internal/recursivetreeiterator.inc
ext/spl/php_spl.c
ext/spl/spl_iterators.c
ext/spl/spl_iterators.h
ext/spl/spl_observer.c
ext/spl/spl_observer.h
ext/spl/tests/multiple_iterator_001.phpt [new file with mode: 0755]

similarity index 99%
rename from ext/spl/examples/multipleiterator.inc
rename to ext/spl/internal/multipleiterator.inc
index cb0c1553d9ae8df4384862e460e1ac138d109555..e977ca369c22c451e9db3c7d1446a8686b85cb2a 100755 (executable)
@@ -1,6 +1,6 @@
 <?php
 /** @file multipleiterator.inc
- * @ingroup Examples
+ * @ingroup SPL
  * @brief class MultipleIterator
  * @author  Johannes Schlueter
  * @author  Marcus Boerger
@@ -9,7 +9,7 @@
  * SPL - Standard PHP Library
  */
 
-/** @ingroup Examples
+/** @ingroup SPL
  * @brief   Iterator that iterates over several iterators one after the other
  * @author  Johannes Schlueter
  * @author  Marcus Boerger
index 3f991580e455d1f5053f6214c701d3ccda0bde55..b35718ee817c9e92f93b1d33a4cb4afb9cbf38ae 100755 (executable)
@@ -1,7 +1,7 @@
 <?php
 
 /** @file recursivetreeiterator.inc
- * @ingroup Examples
+ * @ingroup SPL
  * @brief   class RecursiveTreeIterator
  * @author  Marcus Boerger, Johannes Schlueter
  * @date    2005
index d2df6530ffdd76f4402101fe4e61ca8d114298fe..eea5430ed28a9b73d6f044fbb337a06de4ea32ef 100755 (executable)
@@ -150,14 +150,6 @@ PHP_FUNCTION(class_implements)
        SPL_ADD_CLASS(AppendIterator, z_list, sub, allow, ce_flags); \
        SPL_ADD_CLASS(ArrayIterator, z_list, sub, allow, ce_flags); \
        SPL_ADD_CLASS(ArrayObject, z_list, sub, allow, ce_flags); \
-       SPL_ADD_CLASS(SplDoublyLinkedList, z_list, sub, allow, ce_flags); \
-       SPL_ADD_CLASS(SplQueue, z_list, sub, allow, ce_flags); \
-       SPL_ADD_CLASS(SplStack, z_list, sub, allow, ce_flags); \
-       SPL_ADD_CLASS(SplHeap, z_list, sub, allow, ce_flags); \
-       SPL_ADD_CLASS(SplMinHeap, z_list, sub, allow, ce_flags); \
-       SPL_ADD_CLASS(SplMaxHeap, z_list, sub, allow, ce_flags); \
-       SPL_ADD_CLASS(SplPriorityQueue, z_list, sub, allow, ce_flags); \
-       SPL_ADD_CLASS(SplFixedArray, z_list, sub, allow, ce_flags); \
        SPL_ADD_CLASS(BadFunctionCallException, z_list, sub, allow, ce_flags); \
        SPL_ADD_CLASS(BadMethodCallException, z_list, sub, allow, ce_flags); \
        SPL_ADD_CLASS(CachingIterator, z_list, sub, allow, ce_flags); \
@@ -174,6 +166,7 @@ PHP_FUNCTION(class_implements)
        SPL_ADD_CLASS(LengthException, z_list, sub, allow, ce_flags); \
        SPL_ADD_CLASS(LimitIterator, z_list, sub, allow, ce_flags); \
        SPL_ADD_CLASS(LogicException, z_list, sub, allow, ce_flags); \
+       SPL_ADD_CLASS(MultipleIterator, z_list, sub, allow, ce_flags); \
        SPL_ADD_CLASS(NoRewindIterator, z_list, sub, allow, ce_flags); \
        SPL_ADD_CLASS(OuterIterator, z_list, sub, allow, ce_flags); \
        SPL_ADD_CLASS(OutOfBoundsException, z_list, sub, allow, ce_flags); \
@@ -188,14 +181,23 @@ PHP_FUNCTION(class_implements)
        SPL_ADD_CLASS(RecursiveIterator, z_list, sub, allow, ce_flags); \
        SPL_ADD_CLASS(RecursiveIteratorIterator, z_list, sub, allow, ce_flags); \
        SPL_ADD_CLASS(RecursiveRegexIterator, z_list, sub, allow, ce_flags); \
+       SPL_ADD_CLASS(RecursiveTreeIterator, z_list, sub, allow, ce_flags); \
        SPL_ADD_CLASS(RegexIterator, z_list, sub, allow, ce_flags); \
        SPL_ADD_CLASS(RuntimeException, z_list, sub, allow, ce_flags); \
        SPL_ADD_CLASS(SeekableIterator, z_list, sub, allow, ce_flags); \
        SPL_ADD_CLASS(SimpleXMLIterator, z_list, sub, allow, ce_flags); \
+       SPL_ADD_CLASS(SplDoublyLinkedList, z_list, sub, allow, ce_flags); \
        SPL_ADD_CLASS(SplFileInfo, z_list, sub, allow, ce_flags); \
        SPL_ADD_CLASS(SplFileObject, z_list, sub, allow, ce_flags); \
+       SPL_ADD_CLASS(SplFixedArray, z_list, sub, allow, ce_flags); \
+       SPL_ADD_CLASS(SplHeap, z_list, sub, allow, ce_flags); \
+       SPL_ADD_CLASS(SplMinHeap, z_list, sub, allow, ce_flags); \
+       SPL_ADD_CLASS(SplMaxHeap, z_list, sub, allow, ce_flags); \
        SPL_ADD_CLASS(SplObjectStorage, z_list, sub, allow, ce_flags); \
        SPL_ADD_CLASS(SplObserver, z_list, sub, allow, ce_flags); \
+       SPL_ADD_CLASS(SplPriorityQueue, z_list, sub, allow, ce_flags); \
+       SPL_ADD_CLASS(SplQueue, z_list, sub, allow, ce_flags); \
+       SPL_ADD_CLASS(SplStack, z_list, sub, allow, ce_flags); \
        SPL_ADD_CLASS(SplSubject, z_list, sub, allow, ce_flags); \
        SPL_ADD_CLASS(SplTempFileObject, z_list, sub, allow, ce_flags); \
        SPL_ADD_CLASS(UnderflowException, z_list, sub, allow, ce_flags); \
index 8302380c12ee37e3285b3e63fa5ea176c9393384..275a34426433bbf94e299a8976b84c7908c10e0e 100755 (executable)
@@ -35,6 +35,7 @@
 #include "spl_directory.h"
 #include "spl_array.h"
 #include "spl_exceptions.h"
+#include "spl_observer.h"
 #include "ext/standard/php_smart_str.h"
 
 #ifdef accept
index 26ae63f061660f7e717dde9902b0bf67494a07e8..0d6858c1fd584a972f13152c5d37b735ec116efd 100755 (executable)
@@ -35,6 +35,7 @@
 
 extern PHPAPI zend_class_entry *spl_ce_RecursiveIterator;
 extern PHPAPI zend_class_entry *spl_ce_RecursiveIteratorIterator;
+extern PHPAPI zend_class_entry *spl_ce_RecursiveTreeIterator;
 extern PHPAPI zend_class_entry *spl_ce_FilterIterator;
 extern PHPAPI zend_class_entry *spl_ce_RecursiveFilterIterator;
 extern PHPAPI zend_class_entry *spl_ce_ParentIterator;
index 8fc1950abc1cc9f1ab075804ade781f6b0841e08..7fb6cd83a7bb2c2c38efbd7ca8283ffc095b8754 100755 (executable)
@@ -73,6 +73,8 @@ static const zend_function_entry spl_funcs_SplSubject[] = {
 PHPAPI zend_class_entry     *spl_ce_SplObserver;
 PHPAPI zend_class_entry     *spl_ce_SplSubject;
 PHPAPI zend_class_entry     *spl_ce_SplObjectStorage;
+PHPAPI zend_class_entry     *spl_ce_MultipleIterator;
+
 PHPAPI zend_object_handlers spl_handler_SplObjectStorage;
 
 typedef struct _spl_SplObjectStorage { /* {{{ */
@@ -80,6 +82,7 @@ typedef struct _spl_SplObjectStorage { /* {{{ */
        HashTable         storage;
        long              index;
        HashPosition      pos;
+       long              flags;
 } spl_SplObjectStorage; /* }}} */
 
 /* {{{ storage is an assoc aray of [zend_object_value]=>[zval *obj, zval *inf] */
@@ -595,6 +598,298 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_offsetSet, 0, 0, 2)
        ZEND_ARG_INFO(0, info)
 ZEND_END_ARG_INFO()
 
+typedef enum {
+       MIT_NEED_ANY     = 0,
+       MIT_NEED_ALL     = 1,
+       MIT_KEYS_NUMERIC = 0,
+       MIT_KEYS_ASSOC   = 2
+} MultipleIteratorFlags;
+
+#define SPL_MULTIPLE_ITERATOR_GET_ALL_CURRENT   1
+#define SPL_MULTIPLE_ITERATOR_GET_ALL_KEY       2
+
+/* {{{ proto void MultipleIterator::__construct([int flags = MIT_NEED_ALL|MIT_KEYS_NUMERIC]) U
+   Iterator that iterates over several iterators one after the other */
+SPL_METHOD(MultipleIterator, __construct)
+{
+       spl_SplObjectStorage   *intern;
+       long                    flags = MIT_NEED_ALL|MIT_KEYS_NUMERIC;
+
+       php_set_error_handling(EH_THROW, spl_ce_InvalidArgumentException TSRMLS_CC);
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &flags) == FAILURE) {
+               php_set_error_handling(EH_NORMAL, NULL TSRMLS_CC);
+               return;
+       }
+
+       php_set_error_handling(EH_NORMAL, NULL TSRMLS_CC);
+
+       intern = (spl_SplObjectStorage*)zend_object_store_get_object(getThis() TSRMLS_CC);
+       intern->flags = flags;
+}
+/* }}} */
+
+/* {{{ proto int MultipleIterator::getFlags() U
+   Return current flags */
+SPL_METHOD(MultipleIterator, getFlags)
+{
+       spl_SplObjectStorage *intern = (spl_SplObjectStorage*)zend_object_store_get_object(getThis() TSRMLS_CC);
+       RETURN_LONG(intern->flags);
+}
+/* }}} */
+
+/* {{{ proto int MultipleIterator::setFlags(int flags) U
+   Set flags */
+SPL_METHOD(MultipleIterator, setFlags)
+{
+       spl_SplObjectStorage *intern;
+       intern = (spl_SplObjectStorage*)zend_object_store_get_object(getThis() TSRMLS_CC);
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &intern->flags) == FAILURE) {
+               return;
+       }
+}
+/* }}} */
+
+/* {{{ proto void attachIterator(Iterator iterator[, mixed info]) throws InvalidArgumentException U
+   Attach a new iterator */
+SPL_METHOD(MultipleIterator, attachIterator)
+{
+       spl_SplObjectStorage        *intern;
+       zval                        *iterator = NULL, *info = NULL;
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O|z!", &iterator, zend_ce_iterator, &info) == FAILURE) {
+               return;
+       }
+
+       intern = (spl_SplObjectStorage*)zend_object_store_get_object(getThis() TSRMLS_CC);
+
+       if (info != NULL) {
+               spl_SplObjectStorageElement *element;
+               zval                         compare_result;
+
+               if (Z_TYPE_P(info) != IS_LONG && Z_TYPE_P(info) != IS_STRING && Z_TYPE_P(info) != IS_UNICODE) {
+                       zend_throw_exception(spl_ce_InvalidArgumentException, "Info must be NULL, integer or string", 0 TSRMLS_CC);
+                       return;
+               }
+
+               zend_hash_internal_pointer_reset_ex(&intern->storage, &intern->pos);
+               while (zend_hash_get_current_data_ex(&intern->storage, (void**)&element, &intern->pos) == SUCCESS) {
+                       is_identical_function(&compare_result, info, element->inf TSRMLS_CC);
+                       if (Z_LVAL(compare_result)) {
+                               zend_throw_exception(spl_ce_InvalidArgumentException, "Key duplication error", 0 TSRMLS_CC);
+                               return;
+                       }
+                       zend_hash_move_forward_ex(&intern->storage, &intern->pos);
+               }
+       }
+
+       spl_object_storage_attach(intern, iterator, info TSRMLS_CC);
+}
+/* }}} */
+
+/* {{{ proto void MultipleIterator::rewind() U
+   Rewind all attached iterator instances */
+SPL_METHOD(MultipleIterator, rewind)
+{
+       spl_SplObjectStorage        *intern;
+       spl_SplObjectStorageElement *element;
+       zval                        *it;
+
+       intern = (spl_SplObjectStorage*)zend_object_store_get_object(getThis() TSRMLS_CC);
+
+       zend_hash_internal_pointer_reset_ex(&intern->storage, &intern->pos);
+       while (zend_hash_get_current_data_ex(&intern->storage, (void**)&element, &intern->pos) == SUCCESS && !EG(exception)) {
+               it = element->obj;
+               zend_call_method_with_0_params(&it, Z_OBJCE_P(it), &Z_OBJCE_P(it)->iterator_funcs.zf_rewind, "rewind", NULL);
+               zend_hash_move_forward_ex(&intern->storage, &intern->pos);
+       }
+}
+/* }}} */
+
+/* {{{ proto void MultipleIterator::next() U
+   Move all attached iterator instances forward */
+SPL_METHOD(MultipleIterator, next)
+{
+       spl_SplObjectStorage        *intern;
+       spl_SplObjectStorageElement *element;
+       zval                        *it;
+
+       intern = (spl_SplObjectStorage*)zend_object_store_get_object(getThis() TSRMLS_CC);
+
+       zend_hash_internal_pointer_reset_ex(&intern->storage, &intern->pos);
+       while (zend_hash_get_current_data_ex(&intern->storage, (void**)&element, &intern->pos) == SUCCESS && !EG(exception)) {
+               it = element->obj;
+               zend_call_method_with_0_params(&it, Z_OBJCE_P(it), &Z_OBJCE_P(it)->iterator_funcs.zf_next, "next", NULL);
+               zend_hash_move_forward_ex(&intern->storage, &intern->pos);
+       }
+}
+/* }}} */
+
+/* {{{ proto bool MultipleIterator::valid() U
+   Return whether all or one sub iterator is valid depending on flags */
+SPL_METHOD(MultipleIterator, valid)
+{
+       spl_SplObjectStorage        *intern;
+       spl_SplObjectStorageElement *element;
+       zval                        *it, *retval = NULL;
+       long                         expect, valid;
+
+       intern = (spl_SplObjectStorage*)zend_object_store_get_object(getThis() TSRMLS_CC);
+
+       if (!zend_hash_num_elements(&intern->storage)) {
+               RETURN_FALSE;
+       }
+
+       expect = (intern->flags & MIT_NEED_ALL) ? 1 : 0;
+
+       zend_hash_internal_pointer_reset_ex(&intern->storage, &intern->pos);
+       while (zend_hash_get_current_data_ex(&intern->storage, (void**)&element, &intern->pos) == SUCCESS && !EG(exception)) {
+               it = element->obj;
+               zend_call_method_with_0_params(&it, Z_OBJCE_P(it), &Z_OBJCE_P(it)->iterator_funcs.zf_valid, "valid", &retval);
+
+               if (retval) {
+                       valid = Z_LVAL_P(retval);
+                       zval_ptr_dtor(&retval);
+               } else {
+                       valid = 0;
+               }
+
+               if (expect != valid) {
+                       RETURN_BOOL(!expect);
+               }
+
+               zend_hash_move_forward_ex(&intern->storage, &intern->pos);
+       }
+
+       RETURN_BOOL(expect);
+}
+/* }}} */
+
+static void spl_multiple_iterator_get_all(spl_SplObjectStorage *intern, int get_type, zval *return_value TSRMLS_DC) /* {{{ */
+{
+       spl_SplObjectStorageElement *element;
+       zval                        *it, *retval = NULL;
+       int                          valid = 1, num_elements;
+
+       num_elements = zend_hash_num_elements(&intern->storage);
+       if (num_elements < 1) {
+               RETURN_FALSE;
+       }
+
+       array_init_size(return_value, num_elements);
+       
+       zend_hash_internal_pointer_reset_ex(&intern->storage, &intern->pos);
+       while (zend_hash_get_current_data_ex(&intern->storage, (void**)&element, &intern->pos) == SUCCESS && !EG(exception)) {
+               it = element->obj;
+               zend_call_method_with_0_params(&it, Z_OBJCE_P(it), &Z_OBJCE_P(it)->iterator_funcs.zf_valid, "valid", &retval);
+
+               if (retval) {
+                       valid = Z_LVAL_P(retval);
+                       zval_ptr_dtor(&retval);
+               } else {
+                       valid = 0;
+               }
+
+               if (valid) {
+                       if (SPL_MULTIPLE_ITERATOR_GET_ALL_CURRENT == get_type) {
+                               zend_call_method_with_0_params(&it, Z_OBJCE_P(it), &Z_OBJCE_P(it)->iterator_funcs.zf_current, "current", &retval);
+                       } else {
+                               zend_call_method_with_0_params(&it, Z_OBJCE_P(it), &Z_OBJCE_P(it)->iterator_funcs.zf_key,     "key",     &retval);
+                       }
+                       if (!retval) {
+                               zend_throw_exception(spl_ce_RuntimeException, "Failed to call sub iterator method", 0 TSRMLS_CC);
+                               return;
+                       }
+               } else if (intern->flags & MIT_NEED_ALL) {
+                       if (SPL_MULTIPLE_ITERATOR_GET_ALL_CURRENT == get_type) {
+                               zend_throw_exception(spl_ce_RuntimeException, "Called current() with non valid sub iterator", 0 TSRMLS_CC);
+                       } else {
+                               zend_throw_exception(spl_ce_RuntimeException, "Called key() with non valid sub iterator", 0 TSRMLS_CC);
+                       }
+                       return;
+               } else {
+                       ALLOC_INIT_ZVAL(retval);
+               }
+
+               if (intern->flags & MIT_KEYS_ASSOC) {
+                       switch (Z_TYPE_P(element->inf)) {
+                               case IS_LONG:
+                                       add_index_zval(return_value, Z_LVAL_P(element->inf), retval);
+                                       break;
+                               case IS_STRING:
+                               case IS_UNICODE:
+                                       add_u_assoc_zval_ex(return_value, Z_TYPE_P(element->inf), Z_UNIVAL_P(element->inf), Z_UNILEN_P(element->inf)+1U, retval);
+                                       break;
+                               default:
+                                       zval_ptr_dtor(&retval);
+                                       zend_throw_exception(spl_ce_InvalidArgumentException, "Sub-Iterator is associated with NULL", 0 TSRMLS_CC);
+                                       return;
+                       }
+               } else {
+                       add_next_index_zval(return_value, retval);
+               }
+
+               zend_hash_move_forward_ex(&intern->storage, &intern->pos);
+       }
+}
+/* }}} */
+
+/* {{{ proto array current() throws RuntimeException throws InvalidArgumentException U
+   Return an array of all registered Iterator instances current() result */
+SPL_METHOD(MultipleIterator, current)
+{
+       spl_SplObjectStorage        *intern;
+       intern = (spl_SplObjectStorage*)zend_object_store_get_object(getThis() TSRMLS_CC);
+
+       spl_multiple_iterator_get_all(intern, SPL_MULTIPLE_ITERATOR_GET_ALL_CURRENT, return_value TSRMLS_CC);
+}
+/* }}} */
+
+/* {{{ proto array MultipleIterator::key() U
+   Return an array of all registered Iterator instances key() result */
+SPL_METHOD(MultipleIterator, key)
+{
+       spl_SplObjectStorage        *intern;
+       intern = (spl_SplObjectStorage*)zend_object_store_get_object(getThis() TSRMLS_CC);
+
+       spl_multiple_iterator_get_all(intern, SPL_MULTIPLE_ITERATOR_GET_ALL_KEY, return_value TSRMLS_CC);
+}
+/* }}} */
+
+static
+ZEND_BEGIN_ARG_INFO_EX(arginfo_MultipleIterator_attachIterator, 0, 0, 1)
+       ZEND_ARG_OBJ_INFO(0, iterator, Iterator, 0)
+       ZEND_ARG_INFO(0, infos)
+ZEND_END_ARG_INFO();
+
+static
+ZEND_BEGIN_ARG_INFO_EX(arginfo_MultipleIterator_detachIterator, 0, 0, 1)
+       ZEND_ARG_OBJ_INFO(0, iterator, Iterator, 0)
+ZEND_END_ARG_INFO();
+
+static
+ZEND_BEGIN_ARG_INFO_EX(arginfo_MultipleIterator_containsIterator, 0, 0, 1)
+       ZEND_ARG_OBJ_INFO(0, iterator, Iterator, 0)
+ZEND_END_ARG_INFO();
+
+static const zend_function_entry spl_funcs_MultipleIterator[] = {
+       SPL_ME(MultipleIterator,  __construct,            NULL,                                       0)
+       SPL_ME(MultipleIterator,  getFlags,               NULL,                                       0)
+       SPL_ME(MultipleIterator,  setFlags,               NULL,                                       0)
+       SPL_ME(MultipleIterator,  attachIterator,         arginfo_MultipleIterator_attachIterator,    0)
+       SPL_MA(MultipleIterator,  detachIterator,         SplObjectStorage, detach,   arginfo_MultipleIterator_detachIterator,   0)
+       SPL_MA(MultipleIterator,  containsIterator,       SplObjectStorage, contains, arginfo_MultipleIterator_containsIterator, 0)
+       SPL_MA(MultipleIterator,  countIterators,         SplObjectStorage, count,    NULL,                                      0)
+       /* Iterator */
+       SPL_ME(MultipleIterator,  rewind,                 NULL,                                       0)
+       SPL_ME(MultipleIterator,  valid,                  NULL,                                       0)
+       SPL_ME(MultipleIterator,  key,                    NULL,                                       0)
+       SPL_ME(MultipleIterator,  current,                NULL,                                       0)
+       SPL_ME(MultipleIterator,  next,                   NULL,                                       0)
+       {NULL, NULL, NULL}
+};
+
 static const zend_function_entry spl_funcs_SplObjectStorage[] = {
        SPL_ME(SplObjectStorage,  attach,      arginfo_attach,        0)
        SPL_ME(SplObjectStorage,  detach,      arginfo_Object,        0)
@@ -635,6 +930,14 @@ PHP_MINIT_FUNCTION(spl_observer)
        REGISTER_SPL_IMPLEMENTS(SplObjectStorage, Serializable);
        REGISTER_SPL_IMPLEMENTS(SplObjectStorage, ArrayAccess);
 
+       REGISTER_SPL_STD_CLASS_EX(MultipleIterator, spl_SplObjectStorage_new, spl_funcs_MultipleIterator);
+       REGISTER_SPL_ITERATOR(MultipleIterator);
+
+       REGISTER_SPL_CLASS_CONST_LONG(MultipleIterator, "MIT_NEED_ANY",     MIT_NEED_ANY);
+       REGISTER_SPL_CLASS_CONST_LONG(MultipleIterator, "MIT_NEED_ALL",     MIT_NEED_ALL);
+       REGISTER_SPL_CLASS_CONST_LONG(MultipleIterator, "MIT_KEYS_NUMERIC", MIT_KEYS_NUMERIC);
+       REGISTER_SPL_CLASS_CONST_LONG(MultipleIterator, "MIT_KEYS_ASSOC",   MIT_KEYS_ASSOC);
+
        return SUCCESS;
 }
 /* }}} */
index e3ada6d30123b4304350059ac7e38c1e25105d66..23d0f2fd364c30371fcd331e68ccc446e1708108 100755 (executable)
@@ -27,6 +27,7 @@
 extern PHPAPI zend_class_entry *spl_ce_SplObserver;
 extern PHPAPI zend_class_entry *spl_ce_SplSubject;
 extern PHPAPI zend_class_entry *spl_ce_SplObjectStorage;
+extern PHPAPI zend_class_entry *spl_ce_MultipleIterator;
 
 PHP_MINIT_FUNCTION(spl_observer);
 
diff --git a/ext/spl/tests/multiple_iterator_001.phpt b/ext/spl/tests/multiple_iterator_001.phpt
new file mode 100755 (executable)
index 0000000..9e657be
--- /dev/null
@@ -0,0 +1,345 @@
+--TEST--
+SPL: MultipleIterator
+--FILE--
+<?php
+
+$iter1 = new ArrayIterator(array(1,2,3));
+$iter2 = new ArrayIterator(array(1,2));
+$iter3 = new ArrayIterator(array(new stdClass(),"string",3));
+
+$m = new MultipleIterator();
+
+echo "-- Default flags, no iterators --\n";
+foreach($m as $value) {
+       var_dump($value);
+}
+var_dump($m->current());
+
+$m->attachIterator($iter1);
+$m->attachIterator($iter2);
+$m->attachIterator($iter3);
+
+echo "-- Default flags, MultipleIterator::MIT_NEED_ALL | MultipleIterator::MIT_KEYS_NUMERIC --\n";
+
+var_dump($m->getFlags() === (MultipleIterator::MIT_NEED_ALL | MultipleIterator::MIT_KEYS_NUMERIC));
+
+foreach($m as $value) {
+       var_dump($m->key(), $value);
+}
+try {
+       $m->current();
+} catch(RuntimeException $e) {
+       echo "RuntimeException thrown: " . $e->getMessage() . "\n";
+}
+try {
+       $m->key();
+} catch(RuntimeException $e) {
+       echo "RuntimeException thrown: " . $e->getMessage() . "\n";
+}
+
+echo "-- Flags = MultipleIterator::MIT_NEED_ANY | MultipleIterator::MIT_KEYS_NUMERIC --\n";
+
+$m->setFlags(MultipleIterator::MIT_NEED_ANY | MultipleIterator::MIT_KEYS_NUMERIC);
+var_dump($m->getFlags() === (MultipleIterator::MIT_NEED_ANY | MultipleIterator::MIT_KEYS_NUMERIC));
+
+foreach($m as $value) {
+       var_dump($m->key(), $value);
+}
+
+echo "-- Default flags, added element --\n";
+
+$m->setFlags(MultipleIterator::MIT_NEED_ALL | MultipleIterator::MIT_KEYS_NUMERIC);
+
+$iter2[] = 3;
+foreach($m as $value) {
+       var_dump($m->key(), $value);
+}
+
+echo "-- Flags |= MultipleIterator::MIT_KEYS_ASSOC, with iterator associated with NULL --\n";
+
+$m->setFlags(MultipleIterator::MIT_NEED_ALL | MultipleIterator::MIT_KEYS_ASSOC);
+$m->rewind();
+try {
+       $m->current();
+} catch(InvalidArgumentException $e) {
+       echo "InvalidArgumentException thrown: " . $e->getMessage() . "\n";
+}
+
+echo "-- Flags |= MultipleIterator::MIT_KEYS_ASSOC --\n";
+
+$m->attachIterator($iter1, "iter1");
+$m->attachIterator($iter2, b"iter2");
+$m->attachIterator($iter3, 3);
+
+foreach($m as $value) {
+       var_dump($m->key(), $value);
+}
+
+echo "-- Associate with invalid value --\n";
+
+try {
+       $m->attachIterator($iter3, new stdClass());
+} catch(InvalidArgumentException $e) {
+       echo "InvalidArgumentException thrown: " . $e->getMessage() . "\n";
+}
+
+echo "-- Associate with duplicate value --\n";
+
+try {
+       $m->attachIterator($iter3, "iter1");
+} catch(InvalidArgumentException $e) {
+       echo "InvalidArgumentException thrown: " . $e->getMessage() . "\n";
+}
+
+echo "-- Count, contains, detach, count, contains, iterate --\n";
+
+var_dump($m->countIterators());
+var_dump($m->containsIterator($iter2));
+var_dump($m->detachIterator($iter2));
+var_dump($m->countIterators());
+var_dump($m->containsIterator($iter2));
+foreach($m as $value) {
+       var_dump($m->key(), $value);
+}
+
+?>
+--EXPECTF--
+-- Default flags, no iterators --
+bool(false)
+-- Default flags, MultipleIterator::MIT_NEED_ALL | MultipleIterator::MIT_KEYS_NUMERIC --
+bool(true)
+array(3) {
+  [0]=>
+  int(0)
+  [1]=>
+  int(0)
+  [2]=>
+  int(0)
+}
+array(3) {
+  [0]=>
+  int(1)
+  [1]=>
+  int(1)
+  [2]=>
+  object(stdClass)#%d (0) {
+  }
+}
+array(3) {
+  [0]=>
+  int(1)
+  [1]=>
+  int(1)
+  [2]=>
+  int(1)
+}
+array(3) {
+  [0]=>
+  int(2)
+  [1]=>
+  int(2)
+  [2]=>
+  unicode(6) "string"
+}
+RuntimeException thrown: Called current() with non valid sub iterator
+RuntimeException thrown: Called key() with non valid sub iterator
+-- Flags = MultipleIterator::MIT_NEED_ANY | MultipleIterator::MIT_KEYS_NUMERIC --
+bool(true)
+array(3) {
+  [0]=>
+  int(0)
+  [1]=>
+  int(0)
+  [2]=>
+  int(0)
+}
+array(3) {
+  [0]=>
+  int(1)
+  [1]=>
+  int(1)
+  [2]=>
+  object(stdClass)#%d (0) {
+  }
+}
+array(3) {
+  [0]=>
+  int(1)
+  [1]=>
+  int(1)
+  [2]=>
+  int(1)
+}
+array(3) {
+  [0]=>
+  int(2)
+  [1]=>
+  int(2)
+  [2]=>
+  unicode(6) "string"
+}
+array(3) {
+  [0]=>
+  int(2)
+  [1]=>
+  NULL
+  [2]=>
+  int(2)
+}
+array(3) {
+  [0]=>
+  int(3)
+  [1]=>
+  NULL
+  [2]=>
+  int(3)
+}
+-- Default flags, added element --
+array(3) {
+  [0]=>
+  int(0)
+  [1]=>
+  int(0)
+  [2]=>
+  int(0)
+}
+array(3) {
+  [0]=>
+  int(1)
+  [1]=>
+  int(1)
+  [2]=>
+  object(stdClass)#%d (0) {
+  }
+}
+array(3) {
+  [0]=>
+  int(1)
+  [1]=>
+  int(1)
+  [2]=>
+  int(1)
+}
+array(3) {
+  [0]=>
+  int(2)
+  [1]=>
+  int(2)
+  [2]=>
+  unicode(6) "string"
+}
+array(3) {
+  [0]=>
+  int(2)
+  [1]=>
+  int(2)
+  [2]=>
+  int(2)
+}
+array(3) {
+  [0]=>
+  int(3)
+  [1]=>
+  int(3)
+  [2]=>
+  int(3)
+}
+-- Flags |= MultipleIterator::MIT_KEYS_ASSOC, with iterator associated with NULL --
+InvalidArgumentException thrown: Sub-Iterator is associated with NULL
+-- Flags |= MultipleIterator::MIT_KEYS_ASSOC --
+array(3) {
+  [u"iter1"]=>
+  int(0)
+  ["iter2"]=>
+  int(0)
+  [3]=>
+  int(0)
+}
+array(3) {
+  [u"iter1"]=>
+  int(1)
+  ["iter2"]=>
+  int(1)
+  [3]=>
+  object(stdClass)#%d (0) {
+  }
+}
+array(3) {
+  [u"iter1"]=>
+  int(1)
+  ["iter2"]=>
+  int(1)
+  [3]=>
+  int(1)
+}
+array(3) {
+  [u"iter1"]=>
+  int(2)
+  ["iter2"]=>
+  int(2)
+  [3]=>
+  unicode(6) "string"
+}
+array(3) {
+  [u"iter1"]=>
+  int(2)
+  ["iter2"]=>
+  int(2)
+  [3]=>
+  int(2)
+}
+array(3) {
+  [u"iter1"]=>
+  int(3)
+  ["iter2"]=>
+  int(3)
+  [3]=>
+  int(3)
+}
+-- Associate with invalid value --
+InvalidArgumentException thrown: Info must be NULL, integer or string
+-- Associate with duplicate value --
+InvalidArgumentException thrown: Key duplication error
+-- Count, contains, detach, count, contains, iterate --
+int(3)
+bool(true)
+NULL
+int(2)
+bool(false)
+array(2) {
+  [u"iter1"]=>
+  int(0)
+  [3]=>
+  int(0)
+}
+array(2) {
+  [u"iter1"]=>
+  int(1)
+  [3]=>
+  object(stdClass)#%d (0) {
+  }
+}
+array(2) {
+  [u"iter1"]=>
+  int(1)
+  [3]=>
+  int(1)
+}
+array(2) {
+  [u"iter1"]=>
+  int(2)
+  [3]=>
+  unicode(6) "string"
+}
+array(2) {
+  [u"iter1"]=>
+  int(2)
+  [3]=>
+  int(2)
+}
+array(2) {
+  [u"iter1"]=>
+  int(3)
+  [3]=>
+  int(3)
+}