]> granicus.if.org Git - php/commitdiff
- Move RecursiveArrayIterator into .c
authorMarcus Boerger <helly@php.net>
Sun, 18 Sep 2005 11:34:36 +0000 (11:34 +0000)
committerMarcus Boerger <helly@php.net>
Sun, 18 Sep 2005 11:34:36 +0000 (11:34 +0000)
- Add ability to control iterator class of ArrayObject
- Implement option to make CachingIterator cache all read elements
- Implement ArrayAccess to CachingIterator to access cached elements
- Update docu
- Update tests

20 files changed:
ext/spl/internal/recursivearrayiterator.inc [moved from ext/spl/examples/recursivearrayiterator.inc with 98% similarity]
ext/spl/php_spl.c
ext/spl/spl.php
ext/spl/spl_array.c
ext/spl/spl_array.h
ext/spl/spl_iterators.c
ext/spl/spl_iterators.h
ext/spl/tests/array_009.phpt
ext/spl/tests/array_009a.phpt [new file with mode: 0755]
ext/spl/tests/bug31926.phpt
ext/spl/tests/iterator_014.phpt
ext/spl/tests/iterator_015.phpt
ext/spl/tests/iterator_016.phpt
ext/spl/tests/iterator_021.phpt
ext/spl/tests/iterator_022.phpt
ext/spl/tests/iterator_023.phpt
ext/spl/tests/iterator_024.phpt [new file with mode: 0755]
ext/spl/tests/iterator_025.phpt [new file with mode: 0755]
ext/spl/tests/iterator_026.phpt [new file with mode: 0755]
ext/spl/tests/iterator_027.phpt [new file with mode: 0755]

similarity index 98%
rename from ext/spl/examples/recursivearrayiterator.inc
rename to ext/spl/internal/recursivearrayiterator.inc
index 305e54cad929e4d96a0debd303c71dc5db773cf5..1b4497afd800c0bc6b584a49e3883bca636a3178 100755 (executable)
@@ -9,7 +9,7 @@
  * SPL - Standard PHP Library
  */
 
-/** @ingroup Examples
+/** @ingroup SPL
  * @brief   A recursive array iterator
  * @author  Marcus Boerger
  * @version 1.0
index 6ac52f3c81ea178a1abd9638860257a4102a0e91..4bb47ef790bc8a22caac077fdf2a867f4032fa6e 100755 (executable)
@@ -175,6 +175,7 @@ PHP_FUNCTION(class_implements)
        SPL_ADD_CLASS(RangeException, z_list, sub, allow, ce_flags); \
        SPL_ADD_CLASS(RecursiveDirectoryIterator, z_list, sub, allow, ce_flags); \
        SPL_ADD_CLASS(RecursiveFilterIterator, z_list, sub, allow, ce_flags); \
+       SPL_ADD_CLASS(RecursiveArrayIterator, z_list, sub, allow, ce_flags); \
        SPL_ADD_CLASS(RecursiveIterator, z_list, sub, allow, ce_flags); \
        SPL_ADD_CLASS(RecursiveIteratorIterator, z_list, sub, allow, ce_flags); \
        SPL_ADD_CLASS(RuntimeException, z_list, sub, allow, ce_flags); \
index 17c5f6f7f8110dec027735289f7ea0f21dbf4e9e..ae6d14bdd2a2a61273380506e223e6dc6f204d46 100755 (executable)
@@ -67,6 +67,7 @@
  * 
  * - class ArrayObject implements IteratorAggregate
  * - class ArrayIterator implements Iterator
+ * - class RecursiveArrayIterator extends ArrayIterator implements RecursiveIterator
  * 
  * As the above suggest an ArrayObject creates an ArrayIterator when it comes to
  * iteration (e.g. ArrayObject instance used inside foreach). 
@@ -511,9 +512,9 @@ class ArrayObject implements IteratorAggregate, ArrayAccess, Countable
 {
        /** Properties of the object have their normal functionality
         * when accessed as list (var_dump, foreach, etc.) */
-       const STD_PROP_LIST  = 0x00000001;
+       const STD_PROP_LIST     = 0x00000001;
        /** Array indices can be accessed as properties in read/write */
-       const ARRAY_AS_PROPS = 0x00000002;
+       const ARRAY_AS_PROPS    = 0x00000002;
 
        /** Construct a new array iterator from anything that has a hash table.
         * That is any Array or Object.
@@ -521,7 +522,7 @@ class ArrayObject implements IteratorAggregate, ArrayAccess, Countable
         * @param $array the array to use.
         * @param $flags see setFlags().
         */
-       function __construct($array, $flags = 0);
+       function __construct($array, $flags = 0, $iterator_class = "ArrayIterator");
 
        /** Set behavior flags.
         *      
index 9c80841afa4d864ad8438141b610a5e98510e82f..be51aa81c7aa7cb64151dbbc238e264092e5d85d 100755 (executable)
 #include "spl_array.h"
 #include "spl_exceptions.h"
 
-SPL_METHOD(Array, __construct);
-SPL_METHOD(Array, getIterator);
-SPL_METHOD(Array, rewind);
-SPL_METHOD(Array, current);
-SPL_METHOD(Array, key);
-SPL_METHOD(Array, next);
-SPL_METHOD(Array, valid);
-SPL_METHOD(Array, offsetExists);
-SPL_METHOD(Array, offsetGet);
-SPL_METHOD(Array, offsetSet);
-SPL_METHOD(Array, offsetUnset);
-SPL_METHOD(Array, append);
-SPL_METHOD(Array, getArrayCopy);
-SPL_METHOD(Array, exchangeArray);
-SPL_METHOD(Array, seek);
-SPL_METHOD(Array, count);
-SPL_METHOD(Array, getFlags);
-SPL_METHOD(Array, setFlags);
-
-static
-ZEND_BEGIN_ARG_INFO(arginfo_array___construct, 0)
-       ZEND_ARG_INFO(0, array)
-ZEND_END_ARG_INFO();
-
-static
-ZEND_BEGIN_ARG_INFO_EX(arginfo_array_offsetGet, 0, 0, 1)
-       ZEND_ARG_INFO(0, index)
-ZEND_END_ARG_INFO();
-
-static
-ZEND_BEGIN_ARG_INFO_EX(arginfo_array_offsetSet, 0, 0, 2)
-       ZEND_ARG_INFO(0, index)
-       ZEND_ARG_INFO(0, newval)
-ZEND_END_ARG_INFO();
-
-static
-ZEND_BEGIN_ARG_INFO(arginfo_array_append, 0)
-       ZEND_ARG_INFO(0, value)
-ZEND_END_ARG_INFO();
-
-static
-ZEND_BEGIN_ARG_INFO(arginfo_array_seek, 0)
-       ZEND_ARG_INFO(0, position)
-ZEND_END_ARG_INFO();
-
-static
-ZEND_BEGIN_ARG_INFO(arginfo_array_exchangeArray, 0)
-       ZEND_ARG_INFO(0, array)
-ZEND_END_ARG_INFO();
-
-static
-ZEND_BEGIN_ARG_INFO(arginfo_array_setFlags, 0)
-       ZEND_ARG_INFO(0, flags)
-ZEND_END_ARG_INFO();
-
-static zend_function_entry spl_funcs_ArrayObject[] = {
-       SPL_ME(Array, __construct,   arginfo_array___construct, ZEND_ACC_PUBLIC)
-       SPL_ME(Array, offsetExists,  arginfo_array_offsetGet, ZEND_ACC_PUBLIC)
-       SPL_ME(Array, offsetGet,     arginfo_array_offsetGet, ZEND_ACC_PUBLIC)
-       SPL_ME(Array, offsetSet,     arginfo_array_offsetSet, ZEND_ACC_PUBLIC)
-       SPL_ME(Array, offsetUnset,   arginfo_array_offsetGet, ZEND_ACC_PUBLIC)
-       SPL_ME(Array, append,        arginfo_array_append,    ZEND_ACC_PUBLIC)
-       SPL_ME(Array, getArrayCopy,  NULL, ZEND_ACC_PUBLIC)
-       SPL_ME(Array, count,         NULL, ZEND_ACC_PUBLIC)
-       SPL_ME(Array, getFlags,      NULL, ZEND_ACC_PUBLIC)
-       SPL_ME(Array, setFlags,      arginfo_array_setFlags,  ZEND_ACC_PUBLIC)
-       /* ArrayObject specific */
-       SPL_ME(Array, getIterator,   NULL, ZEND_ACC_PUBLIC)
-       SPL_ME(Array, exchangeArray, arginfo_array_exchangeArray, ZEND_ACC_PUBLIC)
-       {NULL, NULL, NULL}
-};
-
-static zend_function_entry spl_funcs_ArrayIterator[] = {
-       SPL_ME(Array, __construct,   arginfo_array___construct, ZEND_ACC_PUBLIC)
-       SPL_ME(Array, offsetExists,  arginfo_array_offsetGet, ZEND_ACC_PUBLIC)
-       SPL_ME(Array, offsetGet,     arginfo_array_offsetGet, ZEND_ACC_PUBLIC)
-       SPL_ME(Array, offsetSet,     arginfo_array_offsetSet, ZEND_ACC_PUBLIC)
-       SPL_ME(Array, offsetUnset,   arginfo_array_offsetGet, ZEND_ACC_PUBLIC)
-       SPL_ME(Array, append,        arginfo_array_append,    ZEND_ACC_PUBLIC)
-       SPL_ME(Array, getArrayCopy,  NULL, ZEND_ACC_PUBLIC)
-       SPL_ME(Array, count,         NULL, ZEND_ACC_PUBLIC)
-       SPL_ME(Array, getFlags,      NULL, ZEND_ACC_PUBLIC)
-       SPL_ME(Array, setFlags,      arginfo_array_setFlags,  ZEND_ACC_PUBLIC)
-       /* ArrayIterator specific */
-       SPL_ME(Array, rewind,        NULL, ZEND_ACC_PUBLIC)
-       SPL_ME(Array, current,       NULL, ZEND_ACC_PUBLIC)
-       SPL_ME(Array, key,           NULL, ZEND_ACC_PUBLIC)
-       SPL_ME(Array, next,          NULL, ZEND_ACC_PUBLIC)
-       SPL_ME(Array, valid,         NULL, ZEND_ACC_PUBLIC)
-       SPL_ME(Array, seek,          arginfo_array_seek,        ZEND_ACC_PUBLIC)
-       {NULL, NULL, NULL}
-};
-
-static zend_function_entry spl_funcs_Countable[] = {
-       SPL_ABSTRACT_ME(Countable, count,   NULL)
-       {NULL, NULL, NULL}
-};
-
 zend_object_handlers spl_handler_ArrayObject;
 PHPAPI zend_class_entry  *spl_ce_ArrayObject;
 
 zend_object_handlers spl_handler_ArrayIterator;
 PHPAPI zend_class_entry  *spl_ce_ArrayIterator;
+PHPAPI zend_class_entry  *spl_ce_RecursiveArrayIterator;
 
 PHPAPI zend_class_entry  *spl_ce_Countable;
 
-#define SPL_ARRAY_STD_PROP_LIST  0x00000001
-#define SPL_ARRAY_ARRAY_AS_PROPS 0x00000002
-#define SPL_ARRAY_IS_REF         0x01000000
-#define SPL_ARRAY_IS_SELF        0x02000000
-#define SPL_ARRAY_USE_OTHER      0x04000000
-#define SPL_ARRAY_INT_MASK       0xFF000000
-#define SPL_ARRAY_CLONE_MASK     0x03000003
+#define SPL_ARRAY_STD_PROP_LIST      0x00000001
+#define SPL_ARRAY_ARRAY_AS_PROPS     0x00000002
+#define SPL_ARRAY_IS_REF             0x01000000
+#define SPL_ARRAY_IS_SELF            0x02000000
+#define SPL_ARRAY_USE_OTHER          0x04000000
+#define SPL_ARRAY_INT_MASK           0xFF000000
+#define SPL_ARRAY_CLONE_MASK         0x03000007
 
 typedef struct _spl_array_object {
        zend_object       std;
@@ -160,6 +63,7 @@ typedef struct _spl_array_object {
        zend_function *   fptr_offset_set;
        zend_function *   fptr_offset_has;
        zend_function *   fptr_offset_del;
+       zend_class_entry* ce_get_iterator;
 } spl_array_object;
 
 static inline HashTable *spl_array_get_hash_table(spl_array_object* intern, int check_std_props TSRMLS_DC) {
@@ -275,6 +179,7 @@ static zend_object_value spl_array_object_new_ex(zend_class_entry *class_type, s
                        intern->fptr_offset_del = NULL;
                }
        }
+       intern->ce_get_iterator = spl_ce_ArrayIterator;
        zend_hash_internal_pointer_reset_ex(spl_array_get_hash_table(intern, 0 TSRMLS_CC), &intern->pos);
        return retval;
 }
@@ -551,7 +456,7 @@ SPL_METHOD(Array, offsetGet)
  Sets the value at the specified $index to $newval. */
 SPL_METHOD(Array, offsetSet)
 {
-       zval *index, *value = NULL;
+       zval *index, *value;
        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz", &index, &value) == FAILURE) {
                return;
        }
@@ -865,7 +770,7 @@ zend_object_iterator *spl_array_get_iterator(zend_class_entry *ce, zval *object
 }
 /* }}} */
 
-/* {{{ proto void ArrayObject::__construct(array|object ar = array())
+/* {{{ proto void ArrayObject::__construct(array|object ar = array() [, int flags = 0 [, string iterator_class = "ArrayIterator"]])
        proto void ArrayIterator::__construct(array|object ar = array() [, int flags = 0])
  Cronstructs a new array iterator from a path. */
 SPL_METHOD(Array, __construct)
@@ -874,6 +779,9 @@ SPL_METHOD(Array, __construct)
        spl_array_object *intern;
        zval *array;
        long ar_flags = 0;
+       char *class_name;
+       int class_name_len;
+       zend_class_entry ** pce_get_iterator;
 
        if (ZEND_NUM_ARGS() == 0) {
                return; /* nothing to do */
@@ -882,11 +790,20 @@ SPL_METHOD(Array, __construct)
 
        intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
 
-       if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|l", &array, &ar_flags) == FAILURE) {
+       if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|ls", &array, &ar_flags, &class_name, &class_name_len) == FAILURE) {
                php_set_error_handling(EH_NORMAL, NULL TSRMLS_CC);
                return;
        }
 
+       if (ZEND_NUM_ARGS() > 2) {
+               if (zend_lookup_class(class_name, class_name_len, &pce_get_iterator TSRMLS_CC) == FAILURE) {
+                       zend_throw_exception(U_CLASS_ENTRY(spl_ce_InvalidArgumentException), "A class that implements Iterator must be specified", 0 TSRMLS_CC);
+                       php_set_error_handling(EH_NORMAL, NULL TSRMLS_CC);
+                       return;
+               }
+               intern->ce_get_iterator = *pce_get_iterator;
+       }
+
        ar_flags &= ~SPL_ARRAY_INT_MASK;
 
        if (Z_TYPE_P(array) == IS_OBJECT && (Z_OBJ_HT_P(array) == &spl_handler_ArrayObject || Z_OBJ_HT_P(array) == &spl_handler_ArrayIterator)) {
@@ -914,13 +831,48 @@ SPL_METHOD(Array, __construct)
        }
        intern->ar_flags |= ar_flags;
        ZVAL_ADDREF(intern->array);
-
+       
        spl_array_rewind(intern TSRMLS_CC);
 
        php_set_error_handling(EH_NORMAL, NULL TSRMLS_CC);
 }
 /* }}} */
 
+/* {{{ proto void ArrayObject::setIteratorClass(string iterator_class)
+   Set the class used in getIterator. */
+SPL_METHOD(Array, setIteratorClass)
+{
+       zval *object = getThis();
+       spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
+       char *class_name;
+       int class_name_len;
+       zend_class_entry ** pce_get_iterator;
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &class_name, &class_name_len) == FAILURE) {
+               php_set_error_handling(EH_NORMAL, NULL TSRMLS_CC);
+               return;
+       }
+
+       if (zend_lookup_class(class_name, class_name_len, &pce_get_iterator TSRMLS_CC) == FAILURE) {
+               zend_throw_exception(U_CLASS_ENTRY(spl_ce_InvalidArgumentException), "A class that implements Iterator must be specified", 0 TSRMLS_CC);
+               php_set_error_handling(EH_NORMAL, NULL TSRMLS_CC);
+               return;
+       }
+       intern->ce_get_iterator = *pce_get_iterator;
+}
+/* }}} */
+
+/* {{{ proto string ArrayObject::getIteratorClass()
+   Get the class used in getIterator. */
+SPL_METHOD(Array, getIteratorClass)
+{
+       zval *object = getThis();
+       spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
+
+       RETURN_STRING(intern->ce_get_iterator->name, 1);
+}
+/* }}} */
+
 /* {{{ proto int ArrayObject::getFlags()
    Get flags */
 SPL_METHOD(Array, getFlags)
@@ -1004,7 +956,7 @@ SPL_METHOD(Array, getIterator)
        }
 
        return_value->type = IS_OBJECT;
-       return_value->value.obj = spl_array_object_new_ex(U_CLASS_ENTRY(spl_ce_ArrayIterator), &iterator, object TSRMLS_CC);
+       return_value->value.obj = spl_array_object_new_ex(intern->ce_get_iterator, &iterator, object TSRMLS_CC);
        return_value->refcount = 1;
        return_value->is_ref = 1;
 }
@@ -1203,6 +1155,150 @@ SPL_METHOD(Array, valid)
 }
 /* }}} */
 
+/* {{{ proto bool RecursiveArrayIterator::hasChildren()
+   Check whether current element has children (e.g. is an array) */
+SPL_METHOD(Array, hasChildren)
+{
+       zval *object = getThis(), **entry;
+       spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
+       HashTable *aht = spl_array_get_hash_table(intern, 0 TSRMLS_CC);
+       
+       if (!aht) {
+               php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Array was modified outside object and is no longer an array");
+               RETURN_FALSE;
+       }
+
+       if ((intern->ar_flags & SPL_ARRAY_IS_REF) && spl_hash_verify_pos(intern TSRMLS_CC) == FAILURE) {
+               php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Array was modified outside object and internal position is no longer valid");
+               RETURN_FALSE;
+       }
+
+       if (zend_hash_get_current_data_ex(aht, (void **) &entry, &intern->pos) == FAILURE) {
+               RETURN_FALSE;
+       }
+
+       RETURN_BOOL(Z_TYPE_PP(entry) == IS_ARRAY || Z_TYPE_PP(entry) == IS_OBJECT);
+}
+/* }}} */
+
+/* {{{ proto object RecursiveArrayIterator::getChildren()
+   Create a sub iterator for the current element (same class as $this) */
+SPL_METHOD(Array, getChildren)
+{
+       zval *object = getThis(), **entry;
+       spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
+       HashTable *aht = spl_array_get_hash_table(intern, 0 TSRMLS_CC);
+
+       if (!aht) {
+               php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Array was modified outside object and is no longer an array");
+               return;
+       }
+
+       if ((intern->ar_flags & SPL_ARRAY_IS_REF) && spl_hash_verify_pos(intern TSRMLS_CC) == FAILURE) {
+               php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Array was modified outside object and internal position is no longer valid");
+               return;
+       }
+
+       if (zend_hash_get_current_data_ex(aht, (void **) &entry, &intern->pos) == FAILURE) {
+               return;
+       }
+
+       spl_instantiate_arg_ex1(Z_OBJCE_P(getThis()), &return_value, 0, *entry TSRMLS_CC);
+}
+/* }}} */
+
+static
+ZEND_BEGIN_ARG_INFO(arginfo_array___construct, 0)
+       ZEND_ARG_INFO(0, array)
+ZEND_END_ARG_INFO();
+
+static
+ZEND_BEGIN_ARG_INFO_EX(arginfo_array_offsetGet, 0, 0, 1)
+       ZEND_ARG_INFO(0, index)
+ZEND_END_ARG_INFO();
+
+static
+ZEND_BEGIN_ARG_INFO_EX(arginfo_array_offsetSet, 0, 0, 2)
+       ZEND_ARG_INFO(0, index)
+       ZEND_ARG_INFO(0, newval)
+ZEND_END_ARG_INFO();
+
+static
+ZEND_BEGIN_ARG_INFO(arginfo_array_append, 0)
+       ZEND_ARG_INFO(0, value)
+ZEND_END_ARG_INFO();
+
+static
+ZEND_BEGIN_ARG_INFO(arginfo_array_seek, 0)
+       ZEND_ARG_INFO(0, position)
+ZEND_END_ARG_INFO();
+
+static
+ZEND_BEGIN_ARG_INFO(arginfo_array_exchangeArray, 0)
+       ZEND_ARG_INFO(0, array)
+ZEND_END_ARG_INFO();
+
+static
+ZEND_BEGIN_ARG_INFO(arginfo_array_setFlags, 0)
+       ZEND_ARG_INFO(0, flags)
+ZEND_END_ARG_INFO();
+
+static
+ZEND_BEGIN_ARG_INFO(arginfo_array_setIteratorClass, 0)
+       ZEND_ARG_INFO(0, iteratorClass)
+ZEND_END_ARG_INFO();
+
+static zend_function_entry spl_funcs_ArrayObject[] = {
+       SPL_ME(Array, __construct,   arginfo_array___construct, ZEND_ACC_PUBLIC)
+       SPL_ME(Array, offsetExists,  arginfo_array_offsetGet, ZEND_ACC_PUBLIC)
+       SPL_ME(Array, offsetGet,     arginfo_array_offsetGet, ZEND_ACC_PUBLIC)
+       SPL_ME(Array, offsetSet,     arginfo_array_offsetSet, ZEND_ACC_PUBLIC)
+       SPL_ME(Array, offsetUnset,   arginfo_array_offsetGet, ZEND_ACC_PUBLIC)
+       SPL_ME(Array, append,        arginfo_array_append,    ZEND_ACC_PUBLIC)
+       SPL_ME(Array, getArrayCopy,  NULL, ZEND_ACC_PUBLIC)
+       SPL_ME(Array, count,         NULL, ZEND_ACC_PUBLIC)
+       SPL_ME(Array, getFlags,      NULL, ZEND_ACC_PUBLIC)
+       SPL_ME(Array, setFlags,      arginfo_array_setFlags,  ZEND_ACC_PUBLIC)
+       /* ArrayObject specific */
+       SPL_ME(Array, getIterator,      NULL,                           ZEND_ACC_PUBLIC)
+       SPL_ME(Array, exchangeArray,    arginfo_array_exchangeArray,    ZEND_ACC_PUBLIC)
+       SPL_ME(Array, setIteratorClass, arginfo_array_setIteratorClass, ZEND_ACC_PUBLIC)
+       SPL_ME(Array, getIteratorClass, NULL,                           ZEND_ACC_PUBLIC)
+       {NULL, NULL, NULL}
+};
+
+static zend_function_entry spl_funcs_ArrayIterator[] = {
+       SPL_ME(Array, __construct,   arginfo_array___construct, ZEND_ACC_PUBLIC)
+       SPL_ME(Array, offsetExists,  arginfo_array_offsetGet, ZEND_ACC_PUBLIC)
+       SPL_ME(Array, offsetGet,     arginfo_array_offsetGet, ZEND_ACC_PUBLIC)
+       SPL_ME(Array, offsetSet,     arginfo_array_offsetSet, ZEND_ACC_PUBLIC)
+       SPL_ME(Array, offsetUnset,   arginfo_array_offsetGet, ZEND_ACC_PUBLIC)
+       SPL_ME(Array, append,        arginfo_array_append,    ZEND_ACC_PUBLIC)
+       SPL_ME(Array, getArrayCopy,  NULL, ZEND_ACC_PUBLIC)
+       SPL_ME(Array, count,         NULL, ZEND_ACC_PUBLIC)
+       SPL_ME(Array, getFlags,      NULL, ZEND_ACC_PUBLIC)
+       SPL_ME(Array, setFlags,      arginfo_array_setFlags,  ZEND_ACC_PUBLIC)
+       /* ArrayIterator specific */
+       SPL_ME(Array, rewind,        NULL, ZEND_ACC_PUBLIC)
+       SPL_ME(Array, current,       NULL, ZEND_ACC_PUBLIC)
+       SPL_ME(Array, key,           NULL, ZEND_ACC_PUBLIC)
+       SPL_ME(Array, next,          NULL, ZEND_ACC_PUBLIC)
+       SPL_ME(Array, valid,         NULL, ZEND_ACC_PUBLIC)
+       SPL_ME(Array, seek,          arginfo_array_seek,        ZEND_ACC_PUBLIC)
+       {NULL, NULL, NULL}
+};
+
+static zend_function_entry spl_funcs_RecursiveArrayIterator[] = {
+       SPL_ME(Array, hasChildren,   NULL, ZEND_ACC_PUBLIC)
+       SPL_ME(Array, getChildren,   NULL, ZEND_ACC_PUBLIC)
+       {NULL, NULL, NULL}
+};
+
+static zend_function_entry spl_funcs_Countable[] = {
+       SPL_ABSTRACT_ME(Countable, count,   NULL)
+       {NULL, NULL, NULL}
+};
+
 /* {{{ PHP_MINIT_FUNCTION(spl_array) */
 PHP_MINIT_FUNCTION(spl_array)
 {
@@ -1230,6 +1326,9 @@ PHP_MINIT_FUNCTION(spl_array)
        REGISTER_SPL_IMPLEMENTS(ArrayIterator, SeekableIterator);
        memcpy(&spl_handler_ArrayIterator, &spl_handler_ArrayObject, sizeof(zend_object_handlers));
        spl_ce_ArrayIterator->get_iterator = spl_array_get_iterator;
+       
+       REGISTER_SPL_SUB_CLASS_EX(RecursiveArrayIterator, ArrayIterator, spl_array_object_new, spl_funcs_RecursiveArrayIterator);
+       REGISTER_SPL_IMPLEMENTS(RecursiveArrayIterator, RecursiveIterator);
 
        REGISTER_SPL_INTERFACE(Countable);
        
index 4ee39cb81b65809bb2c1f3e41427e27fece212ae..34dedc78dd4182917bdcff0e2a947b224a05d779 100755 (executable)
@@ -26,6 +26,7 @@
 
 extern PHPAPI zend_class_entry *spl_ce_ArrayObject;
 extern PHPAPI zend_class_entry *spl_ce_ArrayIterator;
+extern PHPAPI zend_class_entry *spl_ce_RecursiveArrayIterator;
 extern PHPAPI zend_class_entry *spl_ce_Countable;
 
 PHP_MINIT_FUNCTION(spl_array);
index 0ad559089ac5a05dbc02fca242de27d3fb6f0077..52c1dcbd4976f20c30bfb9cce037b975bc421fbc 100755 (executable)
@@ -89,6 +89,9 @@ typedef struct _spl_recursive_it_object {
        int                      level;
        RecursiveIteratorMode    mode;
        int                      flags;
+       zend_bool                in_iteration;
+       zend_function            *beginIteration;
+       zend_function            *endIteration;
        zend_function            *callHasChildren;
        zend_function            *callGetChildren;
        zend_function            *beginChildren;
@@ -123,7 +126,7 @@ static void spl_recursive_it_dtor(zend_object_iterator *_iter TSRMLS_DC)
        efree(iter);
 }
        
-static int spl_recursive_it_valid_ex(spl_recursive_it_object *object TSRMLS_DC)
+static int spl_recursive_it_valid_ex(spl_recursive_it_object *object, zval *zthis TSRMLS_DC)
 {
        zend_object_iterator      *sub_iter;
        int                       level = object->level;
@@ -135,6 +138,10 @@ static int spl_recursive_it_valid_ex(spl_recursive_it_object *object TSRMLS_DC)
                }
                level--;
        }
+       if (object->endIteration && object->in_iteration) {
+               zend_call_method_with_0_params(&zthis, object->ce, &object->endIteration, "endIteration", NULL);
+       }
+       object->in_iteration = 0;
        return FAILURE;
 }
 
@@ -142,7 +149,7 @@ static int spl_recursive_it_valid(zend_object_iterator *iter TSRMLS_DC)
 {
        spl_recursive_it_object   *object = (spl_recursive_it_object*)iter->data;
        
-       return spl_recursive_it_valid_ex(object TSRMLS_CC);
+       return spl_recursive_it_valid_ex(object, ((spl_recursive_it_iterator*)iter)->zobject TSRMLS_CC);
 }
 
 static void spl_recursive_it_get_current_data(zend_object_iterator *iter, zval ***data TSRMLS_DC)
@@ -306,6 +313,10 @@ static void spl_recursive_it_rewind_ex(spl_recursive_it_object *object, zval *zt
        if (sub_iter->funcs->rewind) {
                sub_iter->funcs->rewind(sub_iter TSRMLS_CC);
        }
+       if (object->beginIteration && !object->in_iteration) {
+               zend_call_method_with_0_params(&zthis, object->ce, &object->beginIteration, "beginIteration", NULL);
+       }
+       object->in_iteration = 1;
        spl_recursive_it_move_forward_ex(object, zthis TSRMLS_CC);
 }
 
@@ -371,7 +382,16 @@ SPL_METHOD(RecursiveIteratorIterator, __construct)
        intern->level = 0;
        intern->mode = mode;
        intern->flags = flags;
+       intern->in_iteration = 0;
        intern->ce = Z_OBJCE_P(object);
+       zend_hash_find(&intern->ce->function_table, "beginiteration", sizeof("beginiteration"), (void **) &intern->beginIteration);
+       if (intern->beginIteration->common.scope == U_CLASS_ENTRY(spl_ce_RecursiveIteratorIterator)) {
+               intern->beginIteration = NULL;
+       }
+       zend_hash_find(&intern->ce->function_table, "enditeration", sizeof("enditeration"), (void **) &intern->endIteration);
+       if (intern->endIteration->common.scope == U_CLASS_ENTRY(spl_ce_RecursiveIteratorIterator)) {
+               intern->endIteration = NULL;
+       }
        zend_hash_find(&intern->ce->function_table, "callhaschildren", sizeof("callHasChildren"), (void **) &intern->callHasChildren);
        if (intern->callHasChildren->common.scope == U_CLASS_ENTRY(spl_ce_RecursiveIteratorIterator)) {
                intern->callHasChildren = NULL;
@@ -417,7 +437,7 @@ SPL_METHOD(RecursiveIteratorIterator, valid)
 {
        spl_recursive_it_object   *object = (spl_recursive_it_object*)zend_object_store_get_object(getThis() TSRMLS_CC);
 
-       RETURN_BOOL(spl_recursive_it_valid_ex(object TSRMLS_CC) == SUCCESS);
+       RETURN_BOOL(spl_recursive_it_valid_ex(object, getThis() TSRMLS_CC) == SUCCESS);
 } /* }}} */
 
 /* {{{ proto mixed RecursiveIteratorIterator::key()
@@ -497,6 +517,20 @@ SPL_METHOD(RecursiveIteratorIterator, getInnerIterator)
        RETURN_ZVAL(object->iterators[level].zobject, 1, 0);
 } /* }}} */
 
+/* {{{ proto RecursiveIterator RecursiveIteratorIterator::beginIteration()
+   Called when iteration begins (after first rewind() call) */
+SPL_METHOD(RecursiveIteratorIterator, beginIteration)
+{
+       /* nothing to do */
+} /* }}} */
+
+/* {{{ proto RecursiveIterator RecursiveIteratorIterator::endIteration()
+   Called when iteration ends (when valid() first returns false */
+SPL_METHOD(RecursiveIteratorIterator, endIteration)
+{
+       /* nothing to do */
+} /* }}} */
+
 /* {{{ proto bool RecursiveIteratorIterator::callHasChildren()
    Called for each element to test whether it has children */
 SPL_METHOD(RecursiveIteratorIterator, callHasChildren)
@@ -637,6 +671,8 @@ static zend_function_entry spl_funcs_RecursiveIteratorIterator[] = {
        SPL_ME(RecursiveIteratorIterator, getDepth,      NULL, ZEND_ACC_PUBLIC)
        SPL_ME(RecursiveIteratorIterator, getSubIterator,    arginfo_recursive_it_getSubIterator, ZEND_ACC_PUBLIC)
        SPL_ME(RecursiveIteratorIterator, getInnerIterator,  NULL, ZEND_ACC_PUBLIC)
+       SPL_ME(RecursiveIteratorIterator, beginIteration,    NULL, ZEND_ACC_PUBLIC)
+       SPL_ME(RecursiveIteratorIterator, endIteration,      NULL, ZEND_ACC_PUBLIC)
        SPL_ME(RecursiveIteratorIterator, callHasChildren,   NULL, ZEND_ACC_PUBLIC)
        SPL_ME(RecursiveIteratorIterator, callGetChildren,   NULL, ZEND_ACC_PUBLIC)
        SPL_ME(RecursiveIteratorIterator, beginChildren,     NULL, ZEND_ACC_PUBLIC)
@@ -765,6 +801,8 @@ static INLINE spl_dual_it_object* spl_dual_it_construct(INTERNAL_FUNCTION_PARAME
                                return NULL;
                        }
                        intern->u.caching.flags |= flags & CIT_PUBLIC;
+                       MAKE_STD_ZVAL(intern->u.caching.zcache);
+                       array_init(intern->u.caching.zcache);
                        break;
                }
                case DIT_IteratorIterator: {
@@ -1097,7 +1135,7 @@ SPL_METHOD(RecursiveFilterIterator, getChildren)
        intern = (spl_dual_it_object*)zend_object_store_get_object(getThis() TSRMLS_CC);
 
        zend_call_method_with_0_params(&intern->inner.zobject, intern->inner.ce, NULL, "getchildren", &retval);
-       spl_instantiate_arg_ex1(U_CLASS_ENTRY(spl_ce_RecursiveFilterIterator), &return_value, 0, retval TSRMLS_CC);
+       spl_instantiate_arg_ex1(Z_OBJCE_P(getThis()), &return_value, 0, retval TSRMLS_CC);
        zval_ptr_dtor(&retval);
 } /* }}} */
 
@@ -1131,7 +1169,7 @@ SPL_METHOD(ParentIterator, getChildren)
        intern = (spl_dual_it_object*)zend_object_store_get_object(getThis() TSRMLS_CC);
 
        zend_call_method_with_0_params(&intern->inner.zobject, intern->inner.ce, NULL, "getchildren", &retval);
-       spl_instantiate_arg_ex1(U_CLASS_ENTRY(spl_ce_ParentIterator), &return_value, 0, retval TSRMLS_CC);
+       spl_instantiate_arg_ex1(Z_OBJCE_P(getThis()), &return_value, 0, retval TSRMLS_CC);
        zval_ptr_dtor(&retval);
 } /* }}} */
 
@@ -1155,6 +1193,13 @@ static INLINE void spl_dual_it_free_storage(void *_object TSRMLS_DC)
                zval_ptr_dtor(&object->u.append.zarrayit);
        }
 
+       if (object->dit_type == DIT_CachingIterator || object->dit_type == DIT_CachingRecursiveIterator) {
+               if (object->u.caching.zcache) {
+                       zval_ptr_dtor(&object->u.caching.zcache);
+                       object->u.caching.zcache = NULL;
+               }
+       }
+
        zend_hash_destroy(object->std.properties);
        FREE_HASHTABLE(object->std.properties);
 
@@ -1388,6 +1433,22 @@ static INLINE void spl_caching_it_next(spl_dual_it_object *intern TSRMLS_DC)
 {
        if (spl_dual_it_fetch(intern, 1 TSRMLS_CC) == SUCCESS) {
                intern->u.caching.flags |= CIT_VALID;
+               /* Full cache ? */
+               if (intern->u.caching.flags & CIT_FULL_CACHE) {
+                       zval *zcacheval;
+                       
+                       MAKE_STD_ZVAL(zcacheval);
+                       ZVAL_ZVAL(zcacheval, intern->current.data, 1, 0);
+                       switch(intern->current.key_type) {
+                               case HASH_KEY_IS_STRING:
+                                       zend_u_symtable_update(HASH_OF(intern->u.caching.zcache), IS_STRING, intern->current.str_key, intern->current.str_key_len, &zcacheval, sizeof(void*), NULL);
+                                       break;
+                               case HASH_KEY_IS_LONG:
+                                       add_index_zval(intern->u.caching.zcache, intern->current.int_key, zcacheval);
+                                       break;
+                       }
+               }
+               /* Recursion ? */
                if (intern->dit_type == DIT_CachingRecursiveIterator) {
                        zval *retval, *zchildren, zflags;
                        zend_call_method_with_0_params(&intern->inner.zobject, intern->inner.ce, NULL, "haschildren", &retval);
@@ -1459,6 +1520,7 @@ static INLINE void spl_caching_it_next(spl_dual_it_object *intern TSRMLS_DC)
 static INLINE void spl_caching_it_rewind(spl_dual_it_object *intern TSRMLS_DC)
 {
        spl_dual_it_rewind(intern TSRMLS_CC);
+       zend_hash_clean(HASH_OF(intern->u.caching.zcache));
        spl_caching_it_next(intern TSRMLS_CC);
 }
 
@@ -1534,12 +1596,165 @@ SPL_METHOD(CachingIterator, __toString)
        }
 } /* }}} */
 
+/* {{{ proto void CachingIterator::offsetSet(mixed index, mixed newval)
+   Set given index in cache */
+SPL_METHOD(CachingIterator, offsetSet)
+{
+       spl_dual_it_object   *intern;
+       void *arKey;
+       uint nKeyLength;
+       zend_uchar type;
+       zval *value;
+
+       intern = (spl_dual_it_object*)zend_object_store_get_object(getThis() TSRMLS_CC);
+
+       if (!(intern->u.caching.flags & CIT_FULL_CACHE))        {
+               zend_throw_exception_ex(U_CLASS_ENTRY(spl_ce_BadMethodCallException), 0 TSRMLS_CC, "%v does not use a full cache (see CachingIterator::__construct)", Z_OBJCE_P(getThis())->name);
+       }
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Tz", &arKey, &nKeyLength, &type, &value) == FAILURE) {
+               return;
+       }
+
+       value->refcount++;
+       zend_u_symtable_update(HASH_OF(intern->u.caching.zcache), type, arKey, nKeyLength+1, &value, sizeof(value), NULL);
+}
+/* }}} */
+
+/* {{{ proto string CachingIterator::offsetGet(mixed index)
+   Return the internal cache if used */
+SPL_METHOD(CachingIterator, offsetGet)
+{
+       spl_dual_it_object   *intern;
+       void *arKey;
+       uint nKeyLength;
+       zend_uchar type;
+       zval **value;
+
+       intern = (spl_dual_it_object*)zend_object_store_get_object(getThis() TSRMLS_CC);
+
+       if (!(intern->u.caching.flags & CIT_FULL_CACHE))        {
+               zend_throw_exception_ex(U_CLASS_ENTRY(spl_ce_BadMethodCallException), 0 TSRMLS_CC, "%v does not use a full cache (see CachingIterator::__construct)", Z_OBJCE_P(getThis())->name);
+       }
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "T", &arKey, &nKeyLength, &type) == FAILURE) {
+               return;
+       }
+
+       if (zend_u_symtable_find(HASH_OF(intern->u.caching.zcache), type, arKey, nKeyLength+1, (void**)&value) == FAILURE) {
+               zend_error(E_NOTICE, "Undefined index:  %R", type, arKey);
+               return;
+       }
+       
+       RETURN_ZVAL(*value, 1, 0);
+}
+/* }}} */
+
+/* {{{ proto void CachingIterator::offsetUnset(mixed index)
+   Unset given index in cache */
+SPL_METHOD(CachingIterator, offsetUnset)
+{
+       spl_dual_it_object   *intern;
+       void *arKey;
+       uint nKeyLength;
+       zend_uchar type;
+
+       intern = (spl_dual_it_object*)zend_object_store_get_object(getThis() TSRMLS_CC);
+
+       if (!(intern->u.caching.flags & CIT_FULL_CACHE))        {
+               zend_throw_exception_ex(U_CLASS_ENTRY(spl_ce_BadMethodCallException), 0 TSRMLS_CC, "%v does not use a full cache (see CachingIterator::__construct)", Z_OBJCE_P(getThis())->name);
+       }
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "T", &arKey, &nKeyLength, &type) == FAILURE) {
+               return;
+       }
+
+       zend_u_symtable_del(HASH_OF(intern->u.caching.zcache), type, arKey, nKeyLength+1);
+}
+/* }}} */
+
+/* {{{ proto bool CachingIterator::offsetExists(mixed index)
+   Return whether the requested index exists */
+SPL_METHOD(CachingIterator, offsetExists)
+{
+       spl_dual_it_object   *intern;
+       void *arKey;
+       uint nKeyLength;
+       zend_uchar type;
+       
+       intern = (spl_dual_it_object*)zend_object_store_get_object(getThis() TSRMLS_CC);
+
+       if (!(intern->u.caching.flags & CIT_FULL_CACHE))        {
+               zend_throw_exception_ex(U_CLASS_ENTRY(spl_ce_BadMethodCallException), 0 TSRMLS_CC, "%v does not use a full cache (see CachingIterator::__construct)", Z_OBJCE_P(getThis())->name);
+       }
+       
+       if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "T", &arKey, &nKeyLength, &type) == FAILURE) {
+               return;
+       }
+
+       RETURN_BOOL(zend_u_symtable_exists(HASH_OF(intern->u.caching.zcache), type, arKey, nKeyLength+1));
+}
+/* }}} */
+
+/* {{{ proto int CachingIterator::getFlags()
+   Return the internal flags */
+SPL_METHOD(CachingIterator, getFlags)
+{
+       spl_dual_it_object   *intern;
+
+       intern = (spl_dual_it_object*)zend_object_store_get_object(getThis() TSRMLS_CC);
+
+       RETURN_LONG(intern->u.caching.flags);
+}
+/* }}} */
+
+/* {{{ proto void CachingIterator::setFlags()
+   Set the internal flags */
+SPL_METHOD(CachingIterator, setFlags)
+{
+       spl_dual_it_object   *intern;
+       long flags;
+
+       intern = (spl_dual_it_object*)zend_object_store_get_object(getThis() TSRMLS_CC);
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", flags) == FAILURE) {
+               return;
+       }
+
+       if ((intern->u.caching.flags & CIT_CALL_TOSTRING) != 0 && (flags & ~CIT_CALL_TOSTRING) == 0) {
+               zend_throw_exception(U_CLASS_ENTRY(spl_ce_InvalidArgumentException), "Unsetting flag CALL_TO_STRING is not possible", 0 TSRMLS_CC);
+               return;
+       }
+       if ((flags && CIT_FULL_CACHE) != 0 && (intern->u.caching.flags & CIT_FULL_CACHE) == 0) {
+               /* clear on (re)enable */
+               zend_hash_clean(HASH_OF(intern->u.caching.zcache));
+       }
+       intern->u.caching.flags = (intern->u.caching.flags & ~CIT_PUBLIC) | (flags & CIT_PUBLIC);
+}
+/* }}} */
+
 static
 ZEND_BEGIN_ARG_INFO(arginfo_caching_it___construct, 0) 
        ZEND_ARG_OBJ_INFO(0, iterator, Iterator, 0)
        ZEND_ARG_INFO(0, flags)
 ZEND_END_ARG_INFO();
 
+static
+ZEND_BEGIN_ARG_INFO(arginfo_caching_it_setFlags, 0) 
+       ZEND_ARG_INFO(0, flags)
+ZEND_END_ARG_INFO();
+
+static
+ZEND_BEGIN_ARG_INFO_EX(arginfo_caching_it_offsetGet, 0, 0, 1)
+       ZEND_ARG_INFO(0, index)
+ZEND_END_ARG_INFO();
+
+static
+ZEND_BEGIN_ARG_INFO_EX(arginfo_caching_it_offsetSet, 0, 0, 2)
+       ZEND_ARG_INFO(0, index)
+       ZEND_ARG_INFO(0, newval)
+ZEND_END_ARG_INFO();
+
 static zend_function_entry spl_funcs_CachingIterator[] = {
        SPL_ME(CachingIterator, __construct,      arginfo_caching_it___construct, ZEND_ACC_PUBLIC)
        SPL_ME(CachingIterator, rewind,           NULL, ZEND_ACC_PUBLIC)
@@ -1550,6 +1765,12 @@ static zend_function_entry spl_funcs_CachingIterator[] = {
        SPL_ME(CachingIterator, hasNext,          NULL, ZEND_ACC_PUBLIC)
        SPL_ME(CachingIterator, __toString,       NULL, ZEND_ACC_PUBLIC)
        SPL_ME(dual_it,         getInnerIterator, NULL, ZEND_ACC_PUBLIC)
+       SPL_ME(CachingIterator, getFlags,         NULL, ZEND_ACC_PUBLIC)
+       SPL_ME(CachingIterator, setFlags,         arginfo_caching_it_setFlags,    ZEND_ACC_PUBLIC)
+       SPL_ME(CachingIterator, offsetGet,        arginfo_caching_it_offsetGet,   ZEND_ACC_PUBLIC)
+       SPL_ME(CachingIterator, offsetSet,        arginfo_caching_it_offsetSet,   ZEND_ACC_PUBLIC)
+       SPL_ME(CachingIterator, offsetUnset,      arginfo_caching_it_offsetGet,   ZEND_ACC_PUBLIC)
+       SPL_ME(CachingIterator, offsetExists,     arginfo_caching_it_offsetGet,   ZEND_ACC_PUBLIC)
        {NULL, NULL, NULL}
 };
 
@@ -1842,7 +2063,7 @@ SPL_METHOD(AppendIterator, append)
 
        intern = (spl_dual_it_object*)zend_object_store_get_object(getThis() TSRMLS_CC);
 
-       if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "o", &it, U_CLASS_ENTRY(zend_ce_iterator)) == FAILURE) {
+       if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &it, U_CLASS_ENTRY(zend_ce_iterator)) == FAILURE) {
                return;
        }
        spl_array_iterator_append(intern->u.append.zarrayit, it TSRMLS_CC);
@@ -2032,9 +2253,11 @@ PHP_MINIT_FUNCTION(spl_iterators)
 
        REGISTER_SPL_STD_CLASS_EX(CachingIterator, spl_dual_it_new, spl_funcs_CachingIterator);
        REGISTER_SPL_ITERATOR(CachingIterator);
+       REGISTER_SPL_IMPLEMENTS(CachingIterator, ArrayAccess);
 
        REGISTER_SPL_CLASS_CONST_LONG(CachingIterator, "CALL_TOSTRING",    CIT_CALL_TOSTRING); 
        REGISTER_SPL_CLASS_CONST_LONG(CachingIterator, "CATCH_GET_CHILD",  CIT_CATCH_GET_CHILD); 
+       REGISTER_SPL_CLASS_CONST_LONG(CachingIterator, "FULL_CACHE",       CIT_FULL_CACHE); 
 
        REGISTER_SPL_SUB_CLASS_EX(CachingRecursiveIterator, CachingIterator, spl_dual_it_new, spl_funcs_CachingRecursiveIterator);
        REGISTER_SPL_IMPLEMENTS(CachingRecursiveIterator, RecursiveIterator);
index 3e0d0cba116a9f9d7eed619159bf64395de84d94..582db5e946b297c3e159d5b5a5f306c7c88276e8 100755 (executable)
@@ -64,12 +64,13 @@ typedef enum {
 
 enum {
        /* public */
-       CIT_CALL_TOSTRING   = 1,
-       CIT_CATCH_GET_CHILD = 2,
-       CIT_PUBLIC          = CIT_CALL_TOSTRING|CIT_CATCH_GET_CHILD,
+       CIT_CALL_TOSTRING   = 0x00000001,
+       CIT_CATCH_GET_CHILD = 0x00000002,
+       CIT_FULL_CACHE      = 0x00000004,
+       CIT_PUBLIC          = 0x00FFFFFF,
        /* private */
-       CIT_VALID           = 4,
-       CIT_HAS_CHILDREN    = 8
+       CIT_VALID           = 0x01000000,
+       CIT_HAS_CHILDREN    = 0x02000000,
 };
 
 typedef struct _spl_dual_it_object {
@@ -95,9 +96,10 @@ typedef struct _spl_dual_it_object {
                        long             count;
                } limit;
                struct {
-                       int              flags; /* CIT_VALID, CIT_CALL_TOSTRING, CIT_CATCH_GET_CHILD */
+                       int              flags; /* CIT_VALID, CIT_CALL_TOSTRING, CIT_CATCH_GET_CHILD, ... */
                        zval             *zstr;
                        zval             *zchildren;
+                       zval             *zcache;
                } caching;
                struct {
                        zval                 *zarrayit;
index fce2b42df29ad2ac0533b0d0a81e604f0f8c95d3..0431cca77ee39065dbdb82218fb6ce89791cd517 100755 (executable)
@@ -5,22 +5,9 @@ SPL: ArrayIterator implementing RecursiveIterator
 --FILE--
 <?php
 
-class RecursiceArrayIterator extends ArrayIterator implements RecursiveIterator
-{
-       function hasChildren()
-       {
-               return is_array($this->current());
-       }
-       
-       function getChildren()
-       {
-               return new RecursiceArrayIterator($this->current());
-       }
-}
-
 $array = array(1, 2 => array(21, 22 => array(221, 222), 23 => array(231)), 3);
 
-$dir = new RecursiveIteratorIterator(new RecursiceArrayIterator($array), RecursiveIteratorIterator::LEAVES_ONLY);
+$dir = new RecursiveIteratorIterator(new RecursiveArrayIterator($array), RecursiveIteratorIterator::LEAVES_ONLY);
 
 foreach ($dir as $file) {
        print "$file\n";
diff --git a/ext/spl/tests/array_009a.phpt b/ext/spl/tests/array_009a.phpt
new file mode 100755 (executable)
index 0000000..aebac08
--- /dev/null
@@ -0,0 +1,39 @@
+--TEST--
+SPL: ArrayIterator implementing RecursiveIterator
+--SKIPIF--
+<?php if (!extension_loaded("spl")) print "skip"; ?>
+--FILE--
+<?php
+
+class MyRecursiveArrayIterator extends ArrayIterator implements RecursiveIterator
+{
+       function hasChildren()
+       {
+               return is_array($this->current());
+       }
+       
+       function getChildren()
+       {
+               return new MyRecursiveArrayIterator($this->current());
+       }
+}
+
+$array = array(1, 2 => array(21, 22 => array(221, 222), 23 => array(231)), 3);
+
+$dir = new RecursiveIteratorIterator(new MyRecursiveArrayIterator($array), RecursiveIteratorIterator::LEAVES_ONLY);
+
+foreach ($dir as $file) {
+       print "$file\n";
+}
+
+?>
+===DONE===
+<?php exit(0); ?>
+--EXPECT--
+1
+21
+221
+222
+231
+3
+===DONE===
index 1159051a4a5537ab5cc85fe2b3672d6f2f955a8e..bcc9ed3d2c9d421ae91d770a44280df5bc3c3f90 100755 (executable)
@@ -5,17 +5,6 @@ Bug #31926 (php in free() error with RecursiveArrayIterator)
 
 $array = array(0 => array('world'));
 
-class RecursiveArrayIterator extends ArrayIterator implements
-RecursiveIterator {
-   function hasChildren() {
-       return (is_array($this->current()));
-   }
-
-   function getChildren() {
-       return new self($this->current());
-   }
-}
-
 $it = new RecursiveIteratorIterator(new RecursiveArrayIterator($array));
 foreach($it as $key => $val) {
    var_dump($key, $val);
index f6e8ce9d0104e25cf2d420ff9c8f3ba3ea740267..119fad05fbad25bbfc73c4536554ffe6b5bda3d7 100755 (executable)
@@ -3,19 +3,8 @@ SPL: RecursiveIteratorIterator and beginChildren/endChildren
 --FILE--
 <?php
 
-class RecursiveArrayIterator extends ArrayIterator implements RecursiveIterator
+class MyRecursiveArrayIterator extends RecursiveArrayIterator
 {
-       function hasChildren()
-       {
-               return is_array($this->current());
-       }
-       
-       function getChildren()
-       {
-               echo __METHOD__ . "\n";
-               return new RecursiveArrayIterator($this->current());
-       }
-
        function valid()
        {
                if (!parent::valid())
@@ -28,6 +17,12 @@ class RecursiveArrayIterator extends ArrayIterator implements RecursiveIterator
                        return true;
                }
        }
+
+       function getChildren()
+       {
+               echo __METHOD__ . "\n";
+               return parent::getChildren();
+       }
 }
 
 class RecursiveArrayIteratorIterator extends RecursiveIteratorIterator
@@ -73,7 +68,7 @@ class RecursiveArrayIteratorIterator extends RecursiveIteratorIterator
        }
 }
 
-foreach(new RecursiveArrayIteratorIterator(new RecursiveArrayIterator(array("a", array("ba", array("bba", "bbb"), array(array("bcaa"))), array("ca"), "d"))) as $k=>$v)
+foreach(new RecursiveArrayIteratorIterator(new MyRecursiveArrayIterator(array("a", array("ba", array("bba", "bbb"), array(array("bcaa"))), array("ca"), "d"))) as $k=>$v)
 {
        echo "$k=>$v\n";
 }
@@ -87,14 +82,14 @@ RecursiveArrayIteratorIterator::current
 RecursiveArrayIteratorIterator::key
 0=>a
 RecursiveArrayIteratorIterator::next
-RecursiveArrayIterator::getChildren
+MyRecursiveArrayIterator::getChildren
 RecursiveArrayIteratorIterator::beginChildren(1)
 RecursiveArrayIteratorIterator::valid
 RecursiveArrayIteratorIterator::current
 RecursiveArrayIteratorIterator::key
 0=>ba
 RecursiveArrayIteratorIterator::next
-RecursiveArrayIterator::getChildren
+MyRecursiveArrayIterator::getChildren
 RecursiveArrayIteratorIterator::beginChildren(2)
 RecursiveArrayIteratorIterator::valid
 RecursiveArrayIteratorIterator::current
@@ -106,38 +101,38 @@ RecursiveArrayIteratorIterator::current
 RecursiveArrayIteratorIterator::key
 1=>bbb
 RecursiveArrayIteratorIterator::next
-RecursiveArrayIterator::valid = false
+MyRecursiveArrayIterator::valid = false
 RecursiveArrayIteratorIterator::endChildren(2)
-RecursiveArrayIterator::getChildren
+MyRecursiveArrayIterator::getChildren
 RecursiveArrayIteratorIterator::beginChildren(2)
-RecursiveArrayIterator::getChildren
+MyRecursiveArrayIterator::getChildren
 RecursiveArrayIteratorIterator::beginChildren(3)
 RecursiveArrayIteratorIterator::valid
 RecursiveArrayIteratorIterator::current
 RecursiveArrayIteratorIterator::key
 0=>bcaa
 RecursiveArrayIteratorIterator::next
-RecursiveArrayIterator::valid = false
+MyRecursiveArrayIterator::valid = false
 RecursiveArrayIteratorIterator::endChildren(3)
-RecursiveArrayIterator::valid = false
+MyRecursiveArrayIterator::valid = false
 RecursiveArrayIteratorIterator::endChildren(2)
-RecursiveArrayIterator::valid = false
+MyRecursiveArrayIterator::valid = false
 RecursiveArrayIteratorIterator::endChildren(1)
-RecursiveArrayIterator::getChildren
+MyRecursiveArrayIterator::getChildren
 RecursiveArrayIteratorIterator::beginChildren(1)
 RecursiveArrayIteratorIterator::valid
 RecursiveArrayIteratorIterator::current
 RecursiveArrayIteratorIterator::key
 0=>ca
 RecursiveArrayIteratorIterator::next
-RecursiveArrayIterator::valid = false
+MyRecursiveArrayIterator::valid = false
 RecursiveArrayIteratorIterator::endChildren(1)
 RecursiveArrayIteratorIterator::valid
 RecursiveArrayIteratorIterator::current
 RecursiveArrayIteratorIterator::key
 3=>d
 RecursiveArrayIteratorIterator::next
-RecursiveArrayIterator::valid = false
+MyRecursiveArrayIterator::valid = false
 RecursiveArrayIteratorIterator::valid
-RecursiveArrayIterator::valid = false
+MyRecursiveArrayIterator::valid = false
 ===DONE===
index 3a18efd80f49e09afa1a8168d536cd84c731db48..aa30f79e1f675514f01b7b9e17cca83ab593f64b 100755 (executable)
@@ -3,19 +3,6 @@ SPL: RecursiveIteratorIterator and beginChildren/endChildren
 --FILE--
 <?php
 
-class RecursiveArrayIterator extends ArrayIterator implements RecursiveIterator
-{
-       function hasChildren()
-       {
-               return is_array($this->current());
-       }
-       
-       function getChildren()
-       {
-               return new RecursiveArrayIterator($this->current());
-       }
-}
-
 class RecursiveArrayIteratorIterator extends RecursiveIteratorIterator
 {
        function rewind()
index 6a811edf29a98a89c2004ad0da7803e941838f0d..f231c6eb347e2f9675877438a036bffdf8630ee9 100755 (executable)
@@ -3,19 +3,6 @@ SPL: RecursiveIteratorIterator and beginChildren/endChildren
 --FILE--
 <?php
 
-class RecursiveArrayIterator extends ArrayIterator implements RecursiveIterator
-{
-       function hasChildren()
-       {
-               return is_array($this->current());
-       }
-       
-       function getChildren()
-       {
-               return new RecursiveArrayIterator($this->current());
-       }
-}
-
 class Menu extends ArrayObject
 {
        function getIterator()
index 115461d05328ecd5a570709df71e194d19fc81d8..4f2395a8e591681f61d5bec666bd638b57af3e7c 100755 (executable)
@@ -3,19 +3,8 @@ SPL: RecursiveIteratorIterator and hasChildren
 --FILE--
 <?php
 
-class RecursiveArrayIterator extends ArrayIterator implements RecursiveIterator
+class MyRecursiveArrayIterator extends RecursiveArrayIterator
 {
-       function hasChildren()
-       {
-               return is_array($this->current());
-       }
-       
-       function getChildren()
-       {
-               echo __METHOD__ . "\n";
-               return new RecursiveArrayIterator($this->current());
-       }
-
        function valid()
        {
                if (!parent::valid())
@@ -28,6 +17,12 @@ class RecursiveArrayIterator extends ArrayIterator implements RecursiveIterator
                        return true;
                }
        }
+
+       function getChildren()
+       {
+               echo __METHOD__ . "\n";
+               return parent::getChildren();
+       }
 }
 
 class RecursiveArrayIteratorIterator extends RecursiveIteratorIterator
@@ -105,7 +100,7 @@ class RecursiveArrayIteratorIterator extends RecursiveIteratorIterator
        }
 }
 
-foreach(new RecursiveArrayIteratorIterator(new RecursiveArrayIterator(array("a", array("ba", array("bba", "bbb"), array(array("bcaa"), array("bcba"))), array("ca"), "d")), 2) as $k=>$v)
+foreach(new RecursiveArrayIteratorIterator(new MyRecursiveArrayIterator(array("a", array("ba", array("bba", "bbb"), array(array("bcaa"), array("bcba"))), array("ca"), "d")), 2) as $k=>$v)
 {
        if (is_array($v)) $v = join('',$v);
        echo "$k=>$v\n";
@@ -122,7 +117,7 @@ RecursiveArrayIteratorIterator::key
 0=>a
 RecursiveArrayIteratorIterator::next
 RecursiveArrayIteratorIterator::callHasChildren(0) = yes/yes
-RecursiveArrayIterator::getChildren
+MyRecursiveArrayIterator::getChildren
 RecursiveArrayIteratorIterator::beginChildren(1)
 RecursiveArrayIteratorIterator::callHasChildren(1) = no/no
 RecursiveArrayIteratorIterator::valid
@@ -131,7 +126,7 @@ RecursiveArrayIteratorIterator::key
 0=>ba
 RecursiveArrayIteratorIterator::next
 RecursiveArrayIteratorIterator::callHasChildren(1) = yes/yes
-RecursiveArrayIterator::getChildren
+MyRecursiveArrayIterator::getChildren
 RecursiveArrayIteratorIterator::beginChildren(2)
 RecursiveArrayIteratorIterator::callHasChildren(2) = no/no
 RecursiveArrayIteratorIterator::valid
@@ -145,10 +140,10 @@ RecursiveArrayIteratorIterator::current
 RecursiveArrayIteratorIterator::key
 1=>bbb
 RecursiveArrayIteratorIterator::next
-RecursiveArrayIterator::valid = false
+MyRecursiveArrayIterator::valid = false
 RecursiveArrayIteratorIterator::endChildren(2)
 RecursiveArrayIteratorIterator::callHasChildren(1) = yes/yes
-RecursiveArrayIterator::getChildren
+MyRecursiveArrayIterator::getChildren
 RecursiveArrayIteratorIterator::beginChildren(2)
 RecursiveArrayIteratorIterator::callHasChildren(2) = no/yes
 RecursiveArrayIteratorIterator::valid
@@ -159,19 +154,19 @@ RecursiveArrayIteratorIterator::next
 RecursiveArrayIteratorIterator::callHasChildren(2) = no/yes
 RecursiveArrayIteratorIterator::valid
 RecursiveArrayIteratorIterator::next
-RecursiveArrayIterator::valid = false
+MyRecursiveArrayIterator::valid = false
 RecursiveArrayIteratorIterator::endChildren(2)
-RecursiveArrayIterator::valid = false
+MyRecursiveArrayIterator::valid = false
 RecursiveArrayIteratorIterator::endChildren(1)
 RecursiveArrayIteratorIterator::callHasChildren(0) = yes/yes
-RecursiveArrayIterator::getChildren
+MyRecursiveArrayIterator::getChildren
 RecursiveArrayIteratorIterator::beginChildren(1)
 RecursiveArrayIteratorIterator::callHasChildren(1) = no/no
 RecursiveArrayIteratorIterator::current
 RecursiveArrayIteratorIterator::key
 0=>ca
 RecursiveArrayIteratorIterator::next
-RecursiveArrayIterator::valid = false
+MyRecursiveArrayIterator::valid = false
 RecursiveArrayIteratorIterator::endChildren(1)
 RecursiveArrayIteratorIterator::callHasChildren(0) = no/no
 RecursiveArrayIteratorIterator::valid
@@ -179,7 +174,7 @@ RecursiveArrayIteratorIterator::current
 RecursiveArrayIteratorIterator::key
 3=>d
 RecursiveArrayIteratorIterator::next
-RecursiveArrayIterator::valid = false
+MyRecursiveArrayIterator::valid = false
 RecursiveArrayIteratorIterator::valid
-RecursiveArrayIterator::valid = false
+MyRecursiveArrayIterator::valid = false
 ===DONE===
index 12bec48b157cc555f1a73cd7d7a44b8a0cd02a1f..8d055313ace5c0af88d1990b58ce1e05f8943471 100755 (executable)
@@ -3,13 +3,8 @@ SPL: RecursiveIteratorIterator and callHasChildren/callGetChildren
 --FILE--
 <?php
 
-class RecursiveArrayIterator extends ArrayIterator implements RecursiveIterator
+class MyRecursiveArrayIterator extends RecursiveArrayIterator
 {
-       function hasChildren()
-       {
-               return is_array($this->current());
-       }
-       
        function getChildren()
        {
                echo __METHOD__ . "\n";
@@ -102,7 +97,7 @@ class RecursiveArrayIteratorIterator extends RecursiveIteratorIterator
                        return NULL;
                }
                echo __METHOD__ . "(ok:{$this->over})\n";
-               return new RecursiveArrayIterator($this->current());
+               return new MyRecursiveArrayIterator($this->current());
        }
 
        function beginChildren()
@@ -118,7 +113,7 @@ class RecursiveArrayIteratorIterator extends RecursiveIteratorIterator
 
 try
 {
-       foreach(new RecursiveArrayIteratorIterator(new RecursiveArrayIterator(array("a", array("ba", array("bba", "bbb"), array(array("bcaa"), array("bcba"))), array("ca"), "d")), 2) as $k=>$v)
+       foreach(new RecursiveArrayIteratorIterator(new MyRecursiveArrayIterator(array("a", array("ba", array("bba", "bbb"), array(array("bcaa"), array("bcba"))), array("ca"), "d")), 2) as $k=>$v)
        {
                if (is_array($v)) $v = join('',$v);
                echo "$k=>$v\n";
@@ -166,7 +161,7 @@ RecursiveArrayIteratorIterator::current
 RecursiveArrayIteratorIterator::key
 1=>bbb
 RecursiveArrayIteratorIterator::next
-RecursiveArrayIterator::valid = false
+MyRecursiveArrayIterator::valid = false
 RecursiveArrayIteratorIterator::endChildren(2)
 RecursiveArrayIteratorIterator::callHasChildren(1) = yes/yes
 RecursiveArrayIteratorIterator::callGetChildren(ok:0)
@@ -181,9 +176,9 @@ RecursiveArrayIteratorIterator::next
 RecursiveArrayIteratorIterator::callHasChildren(2) = no/yes
 RecursiveArrayIteratorIterator::valid
 RecursiveArrayIteratorIterator::next
-RecursiveArrayIterator::valid = false
+MyRecursiveArrayIterator::valid = false
 RecursiveArrayIteratorIterator::endChildren(2)
-RecursiveArrayIterator::valid = false
+MyRecursiveArrayIterator::valid = false
 RecursiveArrayIteratorIterator::endChildren(1)
 RecursiveArrayIteratorIterator::callHasChildren(0) = yes/yes
 RecursiveArrayIteratorIterator::callGetChildren(skip)
index 8521fa46748fb6dc342583795ef59b731682a6f2..1b6b4685ec112775bb6510dca808ffec45d5915c 100755 (executable)
@@ -3,13 +3,8 @@ SPL: RecursiveIteratorIterator and catch getChildren
 --FILE--
 <?php
 
-class RecursiveArrayIterator extends ArrayIterator implements RecursiveIterator
+class MyRecursiveArrayIterator extends RecursiveArrayIterator
 {
-       function hasChildren()
-       {
-               return is_array($this->current());
-       }
-       
        function getChildren()
        {
                echo __METHOD__ . "\n";
@@ -102,7 +97,7 @@ class RecursiveArrayIteratorIterator extends RecursiveIteratorIterator
                        throw new Exception("Thrown in callGetChildren()");
                }
                echo __METHOD__ . "(ok:{$this->over})\n";
-               return new RecursiveArrayIterator($this->current());
+               return new MyRecursiveArrayIterator($this->current());
        }
 
        function beginChildren()
@@ -118,7 +113,7 @@ class RecursiveArrayIteratorIterator extends RecursiveIteratorIterator
 
 try
 {
-       foreach(new RecursiveArrayIteratorIterator(new RecursiveArrayIterator(array("a", array("ba", array("bba", "bbb"), array(array("bcaa"), array("bcba"))), array("ca"), "d")), 2) as $k=>$v)
+       foreach(new RecursiveArrayIteratorIterator(new MyRecursiveArrayIterator(array("a", array("ba", array("bba", "bbb"), array(array("bcaa"), array("bcba"))), array("ca"), "d")), 2) as $k=>$v)
        {
                if (is_array($v)) $v = join('',$v);
                echo "$k=>$v\n";
@@ -166,7 +161,7 @@ RecursiveArrayIteratorIterator::current
 RecursiveArrayIteratorIterator::key
 1=>bbb
 RecursiveArrayIteratorIterator::next
-RecursiveArrayIterator::valid = false
+MyRecursiveArrayIterator::valid = false
 RecursiveArrayIteratorIterator::endChildren(2)
 RecursiveArrayIteratorIterator::callHasChildren(1) = yes/yes
 RecursiveArrayIteratorIterator::callGetChildren(ok:0)
@@ -181,9 +176,9 @@ RecursiveArrayIteratorIterator::next
 RecursiveArrayIteratorIterator::callHasChildren(2) = no/yes
 RecursiveArrayIteratorIterator::valid
 RecursiveArrayIteratorIterator::next
-RecursiveArrayIterator::valid = false
+MyRecursiveArrayIterator::valid = false
 RecursiveArrayIteratorIterator::endChildren(2)
-RecursiveArrayIterator::valid = false
+MyRecursiveArrayIterator::valid = false
 RecursiveArrayIteratorIterator::endChildren(1)
 RecursiveArrayIteratorIterator::callHasChildren(0) = yes/yes
 RecursiveArrayIteratorIterator::callGetChildren(throw)
@@ -192,7 +187,7 @@ RecursiveArrayIteratorIterator::current
 RecursiveArrayIteratorIterator::key
 3=>d
 RecursiveArrayIteratorIterator::next
-RecursiveArrayIterator::valid = false
+MyRecursiveArrayIterator::valid = false
 RecursiveArrayIteratorIterator::valid
-RecursiveArrayIterator::valid = false
+MyRecursiveArrayIterator::valid = false
 ===DONE===
diff --git a/ext/spl/tests/iterator_024.phpt b/ext/spl/tests/iterator_024.phpt
new file mode 100755 (executable)
index 0000000..0c7dea1
--- /dev/null
@@ -0,0 +1,49 @@
+--TEST--
+SPL: RecursiveIteratorIterator with custom iterator class
+--FILE--
+<?php
+
+$ar = array(1, 2, array(31, 32, array(331)), 4);
+
+foreach(new RecursiveIteratorIterator(new ArrayObject($ar, 0, "RecursiveArrayIterator")) as $v) echo "$v\n";
+
+$it = new ArrayObject($ar);
+var_dump($it->getIteratorClass());
+
+try
+{
+       foreach(new RecursiveIteratorIterator(new ArrayObject($ar)) as $v) echo "$v\n";
+}
+catch (InvalidArgumentException $e)
+{
+       echo $e->getMessage() . "\n";
+}
+
+echo "===MANUAL===\n";
+
+$it->setIteratorClass("RecursiveArrayIterator");
+var_dump($it->getIteratorClass());
+foreach(new RecursiveIteratorIterator($it) as $v) echo "$v\n";
+
+
+?>
+===DONE===
+<?php exit(0); ?>
+--EXPECT--
+1
+2
+31
+32
+331
+4
+string(13) "ArrayIterator"
+An instance of RecursiveIterator or IteratorAggregate creating it is required
+===MANUAL===
+string(22) "RecursiveArrayIterator"
+1
+2
+31
+32
+331
+4
+===DONE===
diff --git a/ext/spl/tests/iterator_025.phpt b/ext/spl/tests/iterator_025.phpt
new file mode 100755 (executable)
index 0000000..e582b1f
--- /dev/null
@@ -0,0 +1,92 @@
+--TEST--
+SPL: RecursiveIteratorIterator and begin/endIteration()
+--FILE--
+<?php
+
+class MyRecursiveIteratorIterator extends RecursiveIteratorIterator
+{
+       function beginIteration()
+       {
+               echo __METHOD__ . "()\n";
+       }
+       
+       function endIteration()
+       {
+               echo __METHOD__ . "()\n";
+       }
+}
+
+$ar = array(1, 2, array(31, 32, array(331)), 4);
+
+$it = new MyRecursiveIteratorIterator(new ArrayObject($ar, 0, "RecursiveArrayIterator"));
+
+foreach($it as $v) echo "$v\n";
+
+echo "===MORE===\n";
+
+foreach($it as $v) echo "$v\n";
+
+echo "===MORE===\n";
+
+$it->rewind();
+foreach($it as $v) echo "$v\n";
+var_dump($it->valid());
+
+echo "===MANUAL===\n";
+
+$it->rewind();
+while($it->valid())
+{
+       echo $it->current() . "\n";
+       $it->next();
+       break;
+}
+$it->rewind();
+while($it->valid())
+{
+       echo $it->current() . "\n";
+       $it->next();
+}
+
+?>
+===DONE===
+<?php exit(0); ?>
+--EXPECT--
+MyRecursiveIteratorIterator::beginIteration()
+1
+2
+31
+32
+331
+4
+MyRecursiveIteratorIterator::endIteration()
+===MORE===
+MyRecursiveIteratorIterator::beginIteration()
+1
+2
+31
+32
+331
+4
+MyRecursiveIteratorIterator::endIteration()
+===MORE===
+MyRecursiveIteratorIterator::beginIteration()
+1
+2
+31
+32
+331
+4
+MyRecursiveIteratorIterator::endIteration()
+bool(false)
+===MANUAL===
+MyRecursiveIteratorIterator::beginIteration()
+1
+1
+2
+31
+32
+331
+4
+MyRecursiveIteratorIterator::endIteration()
+===DONE===
diff --git a/ext/spl/tests/iterator_026.phpt b/ext/spl/tests/iterator_026.phpt
new file mode 100755 (executable)
index 0000000..100c2a4
--- /dev/null
@@ -0,0 +1,34 @@
+--TEST--
+SPL: CachingIterator::hasNext()
+--FILE--
+<?php
+
+$ar = array(1, 2, array(31, 32, array(331)), 4);
+
+$it = new RecursiveArrayIterator($ar);
+$it = new CachingRecursiveIterator($it);
+$it = new RecursiveIteratorIterator($it);
+
+foreach($it as $k=>$v)
+{
+       echo "$k=>$v\n";
+       echo "hasNext: " . ($it->getInnerIterator()->hasNext() ? "yes" : "no") . "\n";
+}
+
+?>
+===DONE===
+<?php exit(0); ?>
+--EXPECT--
+0=>1
+hasNext: yes
+1=>2
+hasNext: yes
+0=>31
+hasNext: yes
+1=>32
+hasNext: yes
+0=>331
+hasNext: no
+3=>4
+hasNext: no
+===DONE===
diff --git a/ext/spl/tests/iterator_027.phpt b/ext/spl/tests/iterator_027.phpt
new file mode 100755 (executable)
index 0000000..fd9ba70
--- /dev/null
@@ -0,0 +1,83 @@
+--TEST--
+SPL: CachingIterator::FULL_CACHE
+--FILE--
+<?php
+
+$ar = array(1, 2, array(31, 32, array(331)), 4);
+
+$it = new RecursiveArrayIterator($ar);
+$it = new RecursiveIteratorIterator($it);
+$it = new CachingIterator($it, CachingIterator::FULL_CACHE);
+
+foreach($it as $k=>$v)
+{
+       echo "$k=>$v\n";
+}
+
+echo "===CHECK===\n";
+
+for ($i = 0; $i < 4; $i++)
+{
+       if (isset($it[$i]))
+       {
+               var_dump($i, $it[$i]);
+       }
+}
+
+$it[2] = 'foo';
+$it[3] = 'bar';
+$it['baz'] = '25';
+
+var_dump($it[2]);
+var_dump($it[3]);
+var_dump($it['baz']);
+
+unset($it[0]);
+unset($it[2]);
+unset($it['baz']);
+
+var_dump(isset($it[0])); // unset
+var_dump(isset($it[1])); // still present
+var_dump(isset($it[2])); // unset
+var_dump(isset($it[3])); // still present
+var_dump(isset($it['baz']));
+
+echo "===REWIND===\n";
+
+$it->rewind(); // cleans and reads first element
+var_dump(isset($it[0])); // pre-fetched
+var_dump(isset($it[1])); // deleted
+var_dump(isset($it[2])); // unset
+var_dump(isset($it[3])); // deleted
+
+?>
+===DONE===
+<?php exit(0); ?>
+--EXPECT--
+0=>1
+1=>2
+0=>31
+1=>32
+0=>331
+3=>4
+===CHECK===
+int(0)
+int(331)
+int(1)
+int(32)
+int(3)
+int(4)
+string(3) "foo"
+string(3) "bar"
+string(2) "25"
+bool(false)
+bool(true)
+bool(false)
+bool(true)
+bool(false)
+===REWIND===
+bool(true)
+bool(false)
+bool(false)
+bool(false)
+===DONE===