]> granicus.if.org Git - php/commitdiff
- MFH
authorMarcus Boerger <helly@php.net>
Sun, 21 May 2006 18:13:37 +0000 (18:13 +0000)
committerMarcus Boerger <helly@php.net>
Sun, 21 May 2006 18:13:37 +0000 (18:13 +0000)
  . Make code exception safe
  . Make RecursiveRegexIterator::getChildren pass regex to inner ctor
  . Fix CachingIterator::__toString() in TOSTRING_USE_KEY mode
  . Add CachingIterator::getCache()
  . Add tests

ext/spl/spl_iterators.c
ext/spl/spl_iterators.h
ext/spl/tests/iterator_042.phpt [new file with mode: 0755]
ext/spl/tests/iterator_043.phpt [new file with mode: 0755]
ext/spl/tests/iterator_044.phpt [new file with mode: 0755]
ext/spl/tests/iterator_045.phpt [new file with mode: 0755]
ext/spl/tests/iterator_046.phpt [new file with mode: 0755]
ext/spl/tests/iterator_047.phpt [new file with mode: 0755]
ext/spl/tests/iterator_048.phpt [new file with mode: 0755]

index e2ec9f4e48f1829842393a8c045982dd3c92d02a..1bfe01ca055e0cb9e470030265abdabe82abc81b 100755 (executable)
@@ -193,6 +193,13 @@ next_step:
                switch (object->iterators[object->level].state) {
                        case RS_NEXT:
                                iterator->funcs->move_forward(iterator TSRMLS_CC);
+                               if (EG(exception)) {
+                                       if (!(object->flags & RIT_CATCH_GET_CHILD)) {
+                                               return;
+                                       } else {
+                                               zend_clear_exception(TSRMLS_C);
+                                       }
+                               }
                        case RS_START:
                                if (iterator->funcs->valid(iterator TSRMLS_CC) == FAILURE) {
                                        break;
@@ -207,6 +214,14 @@ next_step:
                                } else {
                                        zend_call_method_with_0_params(&zobject, ce, NULL, "haschildren", &retval);
                                }
+                               if (EG(exception)) {
+                                       if (!(object->flags & RIT_CATCH_GET_CHILD)) {
+                                               object->iterators[object->level].state = RS_NEXT;
+                                               return;
+                                       } else {
+                                               zend_clear_exception(TSRMLS_C);
+                                       }
+                               }
                                if (retval) {
                                        has_children = zend_is_true(retval);
                                        zval_ptr_dtor(&retval);
@@ -235,6 +250,13 @@ next_step:
                                        zend_call_method_with_0_params(&zthis, object->ce, &object->nextElement, "nextelement", NULL);
                                }
                                object->iterators[object->level].state = RS_NEXT;
+                               if (EG(exception)) {
+                                       if (!(object->flags & RIT_CATCH_GET_CHILD)) {
+                                               return;
+                                       } else {
+                                               zend_clear_exception(TSRMLS_C);
+                                       }
+                               }
                                return /* self */;
                        case RS_SELF:
                                if (object->nextElement && (object->mode == RIT_SELF_FIRST || object->mode == RIT_CHILD_FIRST)) {
@@ -292,6 +314,13 @@ next_step:
                                }
                                if (object->beginChildren) {
                                        zend_call_method_with_0_params(&zthis, object->ce, &object->beginChildren, "beginchildren", NULL);
+                                       if (EG(exception)) {
+                                               if (!(object->flags & RIT_CATCH_GET_CHILD)) {
+                                                       return;
+                                               } else {
+                                                       zend_clear_exception(TSRMLS_C);
+                                               }
+                                       }
                                }
                                goto next_step;
                }
@@ -299,6 +328,13 @@ next_step:
                if (object->level > 0) {
                        if (object->endChildren) {
                                zend_call_method_with_0_params(&zthis, object->ce, &object->endChildren, "endchildren", NULL);
+                               if (EG(exception)) {
+                                       if (!(object->flags & RIT_CATCH_GET_CHILD)) {
+                                               return;
+                                       } else {
+                                               zend_clear_exception(TSRMLS_C);
+                                       }
+                               }
                        }
                        iterator->funcs->dtor(iterator TSRMLS_CC);
                        zval_ptr_dtor(&object->iterators[object->level].zobject);
@@ -317,7 +353,7 @@ static void spl_recursive_it_rewind_ex(spl_recursive_it_object *object, zval *zt
                sub_iter = object->iterators[object->level].iterator;
                sub_iter->funcs->dtor(sub_iter TSRMLS_CC);
                zval_ptr_dtor(&object->iterators[object->level--].zobject);
-               if (!object->endChildren || object->endChildren->common.scope != spl_ce_RecursiveIteratorIterator) {
+               if (!EG(exception) && (!object->endChildren || object->endChildren->common.scope != spl_ce_RecursiveIteratorIterator)) {
                        zend_call_method_with_0_params(&zthis, object->ce, &object->endChildren, "endchildren", NULL);
                }
        }
@@ -327,7 +363,7 @@ 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) {
+       if (!EG(exception) && object->beginIteration && !object->in_iteration) {
                zend_call_method_with_0_params(&zthis, object->ce, &object->beginIteration, "beginIteration", NULL);
        }
        object->in_iteration = 1;
@@ -573,7 +609,11 @@ SPL_METHOD(RecursiveIteratorIterator, callHasChildren)
                RETURN_FALSE;
        } else {
                zend_call_method_with_0_params(&zobject, ce, NULL, "haschildren", &retval);
-               RETURN_ZVAL(retval, 0, 1);
+               if (retval) {
+                       RETURN_ZVAL(retval, 0, 1);
+               } else {
+                       RETURN_FALSE;
+               }
        }
 } /* }}} */
 
@@ -925,6 +965,13 @@ static spl_dual_it_object* spl_dual_it_construct(INTERNAL_FUNCTION_PARAMETERS, z
                                }
                                if (instanceof_function(ce, zend_ce_aggregate TSRMLS_CC)) {
                                        zend_call_method_with_0_params(&zobject, ce, &ce->iterator_funcs.zf_new_iterator, "getiterator", &retval);
+                                       if (EG(exception)) {
+                                               if (retval) {
+                                                       zval_ptr_dtor(&retval);
+                                               }
+                                               php_set_error_handling(EH_NORMAL, NULL TSRMLS_CC);
+                                               return NULL;
+                                       }
                                        if (!retval || Z_TYPE_P(retval) != IS_OBJECT || !instanceof_function(Z_OBJCE_P(retval), zend_ce_traversable TSRMLS_CC)) {
                                                zend_throw_exception_ex(spl_ce_LogicException, 0 TSRMLS_CC, "%s::getIterator() must return an object that implememnts Traversable", ce->name);
                                                php_set_error_handling(EH_NORMAL, NULL TSRMLS_CC);
@@ -955,6 +1002,7 @@ static spl_dual_it_object* spl_dual_it_construct(INTERNAL_FUNCTION_PARAMETERS, z
                                php_set_error_handling(EH_NORMAL, NULL TSRMLS_CC);
                                return NULL;
                        }
+                       intern->u.regex.regex = estrndup(regex, len);
                        intern->u.regex.pce = pcre_get_compiled_regex_cache(regex, len, &extra, &poptions, &coptions TSRMLS_CC);
                        intern->u.regex.pce->refcount++;
                        break;;
@@ -1241,7 +1289,11 @@ SPL_METHOD(RecursiveFilterIterator, hasChildren)
        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, "haschildren", &retval);
-       RETURN_ZVAL(retval, 0, 1);
+       if (retval) {
+               RETURN_ZVAL(retval, 0, 1);
+       } else {
+               RETURN_FALSE;
+       }
 } /* }}} */
 
 /* {{{ proto RecursiveFilterIterator RecursiveFilterIterator::getChildren()
@@ -1254,8 +1306,12 @@ 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(Z_OBJCE_P(getThis()), &return_value, 0, retval TSRMLS_CC);
-       zval_ptr_dtor(&retval);
+       if (!EG(exception) && retval) {
+               spl_instantiate_arg_ex1(Z_OBJCE_P(getThis()), &return_value, 0, retval TSRMLS_CC);
+       }
+       if (retval) {
+               zval_ptr_dtor(&retval);
+       }
 } /* }}} */
 
 /* {{{ proto void ParentIterator::__construct(RecursiveIterator it)
@@ -1275,7 +1331,11 @@ SPL_METHOD(ParentIterator, hasChildren)
        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, "haschildren", &retval);
-       RETURN_ZVAL(retval, 0, 1);
+       if (retval) {
+               RETURN_ZVAL(retval, 0, 1);
+       } else {
+               RETURN_FALSE;
+       }
 } /* }}} */
 
 /* {{{ proto ParentIterator ParentIterator::getChildren()
@@ -1288,8 +1348,12 @@ 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(Z_OBJCE_P(getThis()), &return_value, 0, retval TSRMLS_CC);
-       zval_ptr_dtor(&retval);
+       if (!EG(exception) && retval) {
+               spl_instantiate_arg_ex1(Z_OBJCE_P(getThis()), &return_value, 0, retval TSRMLS_CC);
+       }
+       if (retval) {
+               zval_ptr_dtor(&retval);
+       }
 } /* }}} */
 
 #if HAVE_PCRE || HAVE_BUNDLED_PCRE
@@ -1319,7 +1383,6 @@ SPL_METHOD(RegexIterator, accept)
                        subject = &tmp[0];
                } else {
                        subject_len = intern->current.str_key_len;
-                       /* FIXME: Unicode support??? */
                        subject = intern->current.str_key;
                }
        } else {
@@ -1349,6 +1412,28 @@ SPL_METHOD(RecursiveRegexIterator, __construct)
 {
        spl_dual_it_construct(INTERNAL_FUNCTION_PARAM_PASSTHRU, spl_ce_RecursiveRegexIterator, spl_ce_RecursiveIterator, DIT_RecursiveRegexIterator);
 } /* }}} */
+
+/* {{{ proto RecursiveRegexIterator RecursiveRegexIterator::getChildren()
+   Return the inner iterator's children contained in a RecursiveRegexIterator */
+SPL_METHOD(RecursiveRegexIterator, getChildren)
+{
+       spl_dual_it_object   *intern;
+       zval                 *retval, *regex;
+
+       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);
+       if (!EG(exception)) {
+               MAKE_STD_ZVAL(regex);
+               ZVAL_STRING(regex, intern->u.regex.regex, 1);
+               spl_instantiate_arg_ex2(Z_OBJCE_P(getThis()), &return_value, 0, retval, regex TSRMLS_CC);
+               zval_ptr_dtor(&regex);
+       }
+       if (retval) {
+               zval_ptr_dtor(&retval);
+       }
+} /* }}} */
+
 #endif
 
 /* {{{ spl_dual_it_free_storage */
@@ -1385,6 +1470,9 @@ static inline void spl_dual_it_free_storage(void *_object TSRMLS_DC)
                if (object->u.regex.pce) {
                        object->u.regex.pce->refcount--;
                }
+               if (object->u.regex.regex) {
+                       efree(object->u.regex.regex);
+               }
        }
 #endif
 
@@ -1477,7 +1565,7 @@ ZEND_END_ARG_INFO();
 static zend_function_entry spl_funcs_RecursiveRegexIterator[] = {
        SPL_ME(RecursiveRegexIterator, __construct,      arginfo_rec_regex_it___construct, ZEND_ACC_PUBLIC)
        SPL_ME(ParentIterator,         hasChildren,      NULL, ZEND_ACC_PUBLIC)
-       SPL_ME(ParentIterator,         getChildren,      NULL, ZEND_ACC_PUBLIC)
+       SPL_ME(RecursiveRegexIterator, getChildren,      NULL, ZEND_ACC_PUBLIC)
        {NULL, NULL, NULL}
 };
 #endif
@@ -1511,9 +1599,11 @@ static inline void spl_limit_it_seek(spl_dual_it_object *intern, long pos TSRMLS
                spl_dual_it_free(intern TSRMLS_CC);
                zend_call_method_with_1_params(&intern->inner.zobject, intern->inner.ce, NULL, "seek", NULL, zpos);
                zval_ptr_dtor(&zpos);
-               intern->current.pos = pos;
-               if (spl_limit_it_valid(intern TSRMLS_CC) == SUCCESS) {
-                       spl_dual_it_fetch(intern, 0 TSRMLS_CC);
+               if (!EG(exception)) {
+                       intern->current.pos = pos;
+                       if (spl_limit_it_valid(intern TSRMLS_CC) == SUCCESS) {
+                               spl_dual_it_fetch(intern, 0 TSRMLS_CC);
+                       }
                }
        } else {
                /* emulate the forward seek, by next() calls */
@@ -1664,25 +1754,43 @@ static inline void spl_caching_it_next(spl_dual_it_object *intern TSRMLS_DC)
                if (intern->dit_type == DIT_RecursiveCachingIterator) {
                        zval *retval, *zchildren, zflags;
                        zend_call_method_with_0_params(&intern->inner.zobject, intern->inner.ce, NULL, "haschildren", &retval);
-                       if (zend_is_true(retval)) {
-                               zend_call_method_with_0_params(&intern->inner.zobject, intern->inner.ce, NULL, "getchildren", &zchildren);
-                               if (EG(exception)) {
-                                       if (zchildren) {
+                       if (EG(exception)) {
+                               if (retval) {
+                                       zval_ptr_dtor(&retval);
+                               }
+                               if (intern->u.caching.flags & CIT_CATCH_GET_CHILD) {
+                                       zend_clear_exception(TSRMLS_C);
+                               } else {
+                                       return;
+                               }
+                       } else {
+                               if (zend_is_true(retval)) {
+                                       zend_call_method_with_0_params(&intern->inner.zobject, intern->inner.ce, NULL, "getchildren", &zchildren);
+                                       if (EG(exception)) {
+                                               if (zchildren) {
+                                                       zval_ptr_dtor(&zchildren);
+                                               }
+                                               if (intern->u.caching.flags & CIT_CATCH_GET_CHILD) {
+                                                       zend_clear_exception(TSRMLS_C);
+                                               } else {
+                                                       return;
+                                               }
+                                       } else {
+                                               INIT_PZVAL(&zflags);
+                                               ZVAL_LONG(&zflags, intern->u.caching.flags & CIT_PUBLIC);
+                                               spl_instantiate_arg_ex2(spl_ce_RecursiveCachingIterator, &intern->u.caching.zchildren, 1, zchildren, &zflags TSRMLS_CC);
                                                zval_ptr_dtor(&zchildren);
                                        }
+                               }
+                               zval_ptr_dtor(&retval);
+                               if (EG(exception)) {
                                        if (intern->u.caching.flags & CIT_CATCH_GET_CHILD) {
                                                zend_clear_exception(TSRMLS_C);
                                        } else {
                                                return;
                                        }
-                               } else {
-                                       INIT_PZVAL(&zflags);
-                                       ZVAL_LONG(&zflags, intern->u.caching.flags & CIT_PUBLIC);
-                                       spl_instantiate_arg_ex2(spl_ce_RecursiveCachingIterator, &intern->u.caching.zchildren, 1, zchildren, &zflags TSRMLS_CC);
-                                       zval_ptr_dtor(&zchildren);
                                }
                        }
-                       zval_ptr_dtor(&retval);
                }
                if (intern->u.caching.flags & (CIT_TOSTRING_USE_INNER|CIT_CALL_TOSTRING)) {
                        int  use_copy;
@@ -1781,7 +1889,7 @@ SPL_METHOD(CachingIterator, __toString)
        }
        if (intern->u.caching.flags & CIT_TOSTRING_USE_KEY) {
                if (intern->current.key_type == HASH_KEY_IS_STRING) {
-                       RETURN_STRINGL(intern->current.str_key, intern->current.str_key_len, 1);
+                       RETURN_STRINGL(intern->current.str_key, intern->current.str_key_len-1, 1);
                } else {
                        RETVAL_LONG(intern->current.int_key);
                        convert_to_string(return_value);
@@ -1897,6 +2005,23 @@ SPL_METHOD(CachingIterator, offsetExists)
 }
 /* }}} */
 
+/* {{{ proto bool CachingIterator::getCache()
+   Return the cache */
+SPL_METHOD(CachingIterator, getCache)
+{
+       spl_dual_it_object   *intern;
+       
+       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(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "%v does not use a full cache (see CachingIterator::__construct)", Z_OBJCE_P(getThis())->name);
+               return;
+       }
+
+       RETURN_ZVAL(intern->u.caching.zcache, 1, 0);
+}
+/* }}} */
+
 /* {{{ proto int CachingIterator::getFlags()
    Return the internal flags */
 SPL_METHOD(CachingIterator, getFlags)
@@ -1980,6 +2105,7 @@ static zend_function_entry spl_funcs_CachingIterator[] = {
        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)
+       SPL_ME(CachingIterator, getCache,         NULL, ZEND_ACC_PUBLIC)
        {NULL, NULL, NULL}
 };
 
@@ -2277,7 +2403,7 @@ SPL_METHOD(AppendIterator, append)
        
        APPENDIT_CHECK_CTOR(intern);
 
-       if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &it, zend_ce_iterator) == FAILURE) {
+       if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "O", &it, zend_ce_iterator) == FAILURE) {
                return;
        }
        spl_array_iterator_append(intern->u.append.zarrayit, it TSRMLS_CC);
index 430fe7ec591a976041290876c6bb7ea0ede59786..8a608def63d653b5c919ebb5eab2abf34694536a 100755 (executable)
@@ -130,6 +130,7 @@ typedef struct _spl_dual_it_object {
                struct {
                        int              flags;
                        pcre_cache_entry *pce;
+                       char             *regex;
                } regex;
 #endif
        } u;
diff --git a/ext/spl/tests/iterator_042.phpt b/ext/spl/tests/iterator_042.phpt
new file mode 100755 (executable)
index 0000000..8615450
--- /dev/null
@@ -0,0 +1,104 @@
+--TEST--
+SPL: AppendIterator and its ArrayIterator
+--SKIPIF--
+<?php if (!extension_loaded("spl")) print "skip"; ?>
+--FILE--
+<?php
+
+function test_error_handler($errno, $msg, $filename, $linenum, $vars)
+{
+       echo "Error $msg in $filename on line $linenum\n";
+       return true;
+}
+
+set_error_handler('test_error_handler');
+
+$it = new AppendIterator;
+
+$it->append(array());
+$it->append(new ArrayIterator(array(1)));
+$it->append(new ArrayIterator(array(21, 22)));
+
+var_dump($it->getArrayIterator());
+
+$it->append(new ArrayIterator(array(31, 32, 33)));
+
+var_dump($it->getArrayIterator());
+
+$idx = 0;
+
+foreach($it as $k => $v)
+{
+       echo '===' . $idx++ . "===\n";
+       var_dump($it->getIteratorIndex());
+       var_dump($k);
+       var_dump($v);
+}
+
+?>
+===DONE===
+<?php exit(0); ?>
+--EXPECTF--
+Error Argument 1 passed to AppendIterator::append() must implement interface Iterator, array given in %siterator_042.php on line %d
+object(ArrayIterator)#%d (2) {
+  [0]=>
+  object(ArrayIterator)#%d (1) {
+    [0]=>
+    int(1)
+  }
+  [1]=>
+  object(ArrayIterator)#%d (2) {
+    [0]=>
+    int(21)
+    [1]=>
+    int(22)
+  }
+}
+object(ArrayIterator)#%d (3) {
+  [0]=>
+  object(ArrayIterator)#%d (1) {
+    [0]=>
+    int(1)
+  }
+  [1]=>
+  object(ArrayIterator)#%d (2) {
+    [0]=>
+    int(21)
+    [1]=>
+    int(22)
+  }
+  [2]=>
+  object(ArrayIterator)#5 (3) {
+    [0]=>
+    int(31)
+    [1]=>
+    int(32)
+    [2]=>
+    int(33)
+  }
+}
+===0===
+int(0)
+int(0)
+int(1)
+===1===
+int(1)
+int(0)
+int(21)
+===2===
+int(1)
+int(1)
+int(22)
+===3===
+int(2)
+int(0)
+int(31)
+===4===
+int(2)
+int(1)
+int(32)
+===5===
+int(2)
+int(2)
+int(33)
+===DONE===
diff --git a/ext/spl/tests/iterator_043.phpt b/ext/spl/tests/iterator_043.phpt
new file mode 100755 (executable)
index 0000000..c49e334
--- /dev/null
@@ -0,0 +1,20 @@
+--TEST--
+SPL: RecursiveCachingIterator and uninitialized getChildren()
+--SKIPIF--
+<?php if (!extension_loaded("spl")) print "skip"; ?>
+--FILE--
+<?php
+
+$it = new RecursiveCachingIterator(new RecursiveArrayIterator(array(1,2)));
+
+var_dump($it->getChildren());
+$it->rewind();
+var_dump($it->getChildren());
+
+?>
+===DONE===
+<?php exit(0); ?>
+--EXPECTF--
+NULL
+NULL
+===DONE===
diff --git a/ext/spl/tests/iterator_044.phpt b/ext/spl/tests/iterator_044.phpt
new file mode 100755 (executable)
index 0000000..e25e0d1
--- /dev/null
@@ -0,0 +1,169 @@
+--TEST--
+SPL: CachingIterator and offsetGet/Exists using flag FULL_CACHE
+--SKIPIF--
+<?php if (!extension_loaded("spl")) print "skip"; ?>
+--FILE--
+<?php
+
+class MyFoo
+{
+       function __toString()
+       {
+               return 'foo';
+       }
+}
+
+class MyCachingIterator extends CachingIterator
+{
+       function __construct(Iterator $it, $flags = 0)
+       {
+               parent::__construct($it, $flags);
+       }
+
+       function test($ar)
+       {
+               foreach($ar as $k => $v)
+               {
+                       echo "===$k===\n";
+                       var_dump($v);
+                       var_dump($this->offsetExists($v));
+                       var_dump($this->offsetGet($v));
+               }
+       }
+}
+
+$it = new MyCachingIterator(new ArrayIterator(array(0, 'foo'=>1, 2, 'bar'=>3, 4)));
+
+try
+{
+       var_dump($it->offsetExists(0));
+}
+catch(Exception $e)
+{
+       echo "Exception: " . $e->getMessage() . "\n";
+}
+
+try
+{
+       var_dump($it->offsetGet(0));
+}
+catch(Exception $e)
+{
+       echo "Exception: " . $e->getMessage() . "\n";
+}
+
+$it = new MyCachingIterator(new ArrayIterator(array(0, 'foo'=>1, 2, 'bar'=>3, 4)), CachingIterator::FULL_CACHE);
+
+var_dump($it->offsetExists());
+var_dump($it->offsetGet());
+
+$checks = array(0, new stdClass, new MyFoo, NULL, 2, 'foo', 3);
+
+$it->test($checks);
+
+echo "===FILL===\n";
+
+foreach($it as $v); // read all into cache
+
+$it->test($checks);
+
+?>
+===DONE===
+<?php exit(0); ?>
+--EXPECTF--
+Exception: MyCachingIterator does not use a full cache (see CachingIterator::__construct)
+
+Notice: Undefined index:  0 in %siterator_044.php on line %d
+Exception: MyCachingIterator does not use a full cache (see CachingIterator::__construct)
+
+Warning: CachingIterator::offsetExists() expects exactly 1 parameter, 0 given in %s/iterator_044.php on line %d
+NULL
+
+Warning: CachingIterator::offsetGet() expects exactly 1 parameter, 0 given in %s/iterator_044.php on line %d
+NULL
+===0===
+int(0)
+bool(false)
+
+Notice: Undefined index:  0 in %siterator_044.php on line %d
+NULL
+===1===
+object(stdClass)#%d (0) {
+}
+
+Warning: CachingIterator::offsetExists() expects parameter 1 to be string, object given in %siterator_044.php on line %d
+NULL
+
+Warning: CachingIterator::offsetGet() expects parameter 1 to be string, object given in %siterator_044.php on line %d
+NULL
+===2===
+object(MyFoo)#%d (0) {
+}
+bool(false)
+
+Notice: Undefined index:  foo in %siterator_044.php on line %d
+NULL
+===3===
+NULL
+bool(false)
+
+Notice: Undefined index:   in %siterator_044.php on line %d
+NULL
+===4===
+int(2)
+bool(false)
+
+Notice: Undefined index:  2 in %siterator_044.php on line %d
+NULL
+===5===
+string(3) "foo"
+bool(false)
+
+Notice: Undefined index:  foo in %siterator_044.php on line %d
+NULL
+===6===
+int(3)
+bool(false)
+
+Notice: Undefined index:  3 in %siterator_044.php on line %d
+NULL
+===FILL===
+===0===
+int(0)
+bool(true)
+int(0)
+===1===
+object(stdClass)#1 (0) {
+}
+
+Warning: CachingIterator::offsetExists() expects parameter 1 to be string, object given in %siterator_044.php on line %d
+NULL
+
+Warning: CachingIterator::offsetGet() expects parameter 1 to be string, object given in %siterator_044.php on line %d
+NULL
+===2===
+object(MyFoo)#2 (0) {
+}
+bool(true)
+int(1)
+===3===
+NULL
+bool(false)
+
+Notice: Undefined index:   in %siterator_044.php on line %d
+NULL
+===4===
+int(2)
+bool(true)
+int(4)
+===5===
+string(3) "foo"
+bool(true)
+int(1)
+===6===
+int(3)
+bool(false)
+
+Notice: Undefined index:  3 in %siterator_044.php on line %d
+NULL
+===DONE===
diff --git a/ext/spl/tests/iterator_045.phpt b/ext/spl/tests/iterator_045.phpt
new file mode 100755 (executable)
index 0000000..34dcfa7
--- /dev/null
@@ -0,0 +1,244 @@
+--TEST--
+SPL: CachingIterator and offsetSet/Unset, getCache using flag FULL_CACHE
+--SKIPIF--
+<?php if (!extension_loaded("spl")) print "skip"; ?>
+--FILE--
+<?php
+
+class MyFoo
+{
+       function __toString()
+       {
+               return 'foo';
+       }
+}
+
+class MyCachingIterator extends CachingIterator
+{
+       function __construct(Iterator $it, $flags = 0)
+       {
+               parent::__construct($it, $flags);
+       }
+
+       function testSet($ar)
+       {
+               echo __METHOD__ . "()\n";
+               foreach($ar as $k => $v)
+               {
+                       echo "set($k,$v)\n";
+                       $this->offsetSet($k, $v);
+               }
+       }
+       
+       function testUnset($ar)
+       {
+               echo __METHOD__ . "()\n";
+               foreach($ar as $k => $v)
+               {
+                       echo "unset($v)\n";
+                       $this->offsetUnset($v);
+               }
+       }
+       
+       function fill()
+       {
+               echo __METHOD__ . "()\n";
+               foreach($this as $v) ;
+       }
+
+       function show()
+       {
+               echo __METHOD__ . "()\n";
+               var_dump($this->getCache());
+       }
+}
+
+$it = new MyCachingIterator(new ArrayIterator(array(0, 'foo'=>1, 2, 'bar'=>3, 4)));
+
+try
+{
+       var_dump($it->offsetSet(0, 0));
+}
+catch(Exception $e)
+{
+       echo "Exception: " . $e->getMessage() . "\n";
+}
+
+try
+{
+       var_dump($it->offsetUnset(0));
+}
+catch(Exception $e)
+{
+       echo "Exception: " . $e->getMessage() . "\n";
+}
+
+$it = new MyCachingIterator(new ArrayIterator(array(0, 1, 2, 3)), CachingIterator::FULL_CACHE);
+
+var_dump($it->offsetSet());
+var_dump($it->offsetSet(0));
+var_dump($it->offsetUnset());
+
+$checks = array(0 => 25, 1 => 42, 3 => 'FooBar');
+$unsets = array(0, 2);
+
+$it->testSet($checks);
+$it->show();
+$it->testUnset($unsets);
+$it->show();
+$it->fill();
+$it->show();
+$it->testSet($checks);
+$it->show();
+$it->testUnset($unsets);
+$it->show();
+
+?>
+===DONE===
+<?php exit(0); ?>
+--EXPECTF--
+Exception: MyCachingIterator does not use a full cache (see CachingIterator::__construct)
+Exception: MyCachingIterator does not use a full cache (see CachingIterator::__construct)
+
+Warning: CachingIterator::offsetSet() expects exactly 2 parameters, 0 given in %siterator_045.php on line %d
+NULL
+
+Warning: CachingIterator::offsetSet() expects exactly 2 parameters, 1 given in %siterator_045.php on line %d
+NULL
+
+Warning: CachingIterator::offsetUnset() expects exactly 1 parameter, 0 given in %siterator_045.php on line %d
+NULL
+MyCachingIterator::testSet()
+set(0,25)
+set(1,42)
+set(3,FooBar)
+MyCachingIterator::show()
+array(3) {
+  [0]=>
+  int(25)
+  [1]=>
+  int(42)
+  [3]=>
+  string(6) "FooBar"
+}
+MyCachingIterator::testUnset()
+unset(0)
+unset(2)
+MyCachingIterator::show()
+array(2) {
+  [1]=>
+  int(42)
+  [3]=>
+  string(6) "FooBar"
+}
+MyCachingIterator::fill()
+MyCachingIterator::show()
+array(4) {
+  [0]=>
+  int(0)
+  [1]=>
+  int(1)
+  [2]=>
+  int(2)
+  [3]=>
+  int(3)
+}
+MyCachingIterator::testSet()
+set(0,25)
+set(1,42)
+set(3,FooBar)
+MyCachingIterator::show()
+array(4) {
+  [0]=>
+  int(25)
+  [1]=>
+  int(42)
+  [2]=>
+  int(2)
+  [3]=>
+  string(6) "FooBar"
+}
+MyCachingIterator::testUnset()
+unset(0)
+unset(2)
+MyCachingIterator::show()
+array(2) {
+  [1]=>
+  int(42)
+  [3]=>
+  string(6) "FooBar"
+}
+===DONE===
+--UEXPECTF--
+Exception: MyCachingIterator does not use a full cache (see CachingIterator::__construct)
+Exception: MyCachingIterator does not use a full cache (see CachingIterator::__construct)
+
+Warning: CachingIterator::offsetSet() expects exactly 2 parameters, 0 given in %siterator_045.php on line %d
+NULL
+
+Warning: CachingIterator::offsetSet() expects exactly 2 parameters, 1 given in %siterator_045.php on line %d
+NULL
+
+Warning: CachingIterator::offsetUnset() expects exactly 1 parameter, 0 given in %siterator_045.php on line %d
+NULL
+MyCachingIterator::testSet()
+set(0,25)
+set(1,42)
+set(3,FooBar)
+MyCachingIterator::show()
+array(3) {
+  [0]=>
+  int(25)
+  [1]=>
+  int(42)
+  [3]=>
+  unicode(6) "FooBar"
+}
+MyCachingIterator::testUnset()
+unset(0)
+unset(2)
+MyCachingIterator::show()
+array(2) {
+  [1]=>
+  int(42)
+  [3]=>
+  unicode(6) "FooBar"
+}
+MyCachingIterator::fill()
+MyCachingIterator::show()
+array(4) {
+  [0]=>
+  int(0)
+  [1]=>
+  int(1)
+  [2]=>
+  int(2)
+  [3]=>
+  int(3)
+}
+MyCachingIterator::testSet()
+set(0,25)
+set(1,42)
+set(3,FooBar)
+MyCachingIterator::show()
+array(4) {
+  [0]=>
+  int(25)
+  [1]=>
+  int(42)
+  [2]=>
+  int(2)
+  [3]=>
+  unicode(6) "FooBar"
+}
+MyCachingIterator::testUnset()
+unset(0)
+unset(2)
+MyCachingIterator::show()
+array(2) {
+  [1]=>
+  int(42)
+  [3]=>
+  unicode(6) "FooBar"
+}
+===DONE===
diff --git a/ext/spl/tests/iterator_046.phpt b/ext/spl/tests/iterator_046.phpt
new file mode 100755 (executable)
index 0000000..35736c4
--- /dev/null
@@ -0,0 +1,60 @@
+--TEST--
+SPL: CachingIterator and __toString using bypassed string keys
+--SKIPIF--
+<?php if (!extension_loaded("spl")) print "skip"; ?>
+--FILE--
+<?php
+
+class MyFoo
+{
+       function __toString()
+       {
+               return 'foo';
+       }
+}
+
+class MyCachingIterator extends CachingIterator
+{
+       function __construct(Iterator $it, $flags = 0)
+       {
+               parent::__construct($it, $flags);
+       }
+
+       function fill()
+       {
+               echo __METHOD__ . "()\n";
+               foreach($this as $v) ;
+       }
+
+       function show()
+       {
+               echo __METHOD__ . "()\n";
+               foreach($this as $v)
+               {
+                       var_dump((string)$this);
+               }
+       }
+}
+
+$it = new MyCachingIterator(new ArrayIterator(array(0, 'foo'=>1, 'bar'=>2)), CachingIterator::TOSTRING_USE_KEY);
+
+$it->fill();
+$it->show();
+
+?>
+===DONE===
+<?php exit(0); ?>
+--EXPECTF--
+MyCachingIterator::fill()
+MyCachingIterator::show()
+string(1) "0"
+string(3) "foo"
+string(3) "bar"
+===DONE===
+--UEXPECTF--
+MyCachingIterator::fill()
+MyCachingIterator::show()
+unicode(1) "0"
+unicode(3) "foo"
+unicode(3) "bar"
+===DONE===
diff --git a/ext/spl/tests/iterator_047.phpt b/ext/spl/tests/iterator_047.phpt
new file mode 100755 (executable)
index 0000000..b313df3
--- /dev/null
@@ -0,0 +1,119 @@
+--TEST--
+SPL: RecursiveCachingIterator and exception in has/getChildren
+--SKIPIF--
+<?php if (!extension_loaded("spl")) print "skip"; ?>
+--FILE--
+<?php
+
+class MyRecursiveArrayIterator extends RecursiveArrayIterator
+{
+       static public $fail = 0;
+
+       static function fail($state, $method)
+       {
+               if (self::$fail == $state)
+               {
+                       throw new Exception("State $state: $method()");
+               }
+       }
+
+       function hasChildren()
+       {
+               echo __METHOD__ . "()\n";
+               self::fail(1, __METHOD__);
+               return parent::hasChildren();
+       }
+
+       function getChildren()
+       {
+               echo __METHOD__ . "()\n";
+               self::fail(2, __METHOD__);
+               return parent::getChildren();
+       }
+}
+
+class MyRecursiveCachingIterator extends RecursiveCachingIterator
+{
+       function show()
+       {
+               MyRecursiveArrayIterator::$fail = 0;
+               while(MyRecursiveArrayIterator::$fail < 4)
+               {
+                       echo "===" . MyRecursiveArrayIterator::$fail . "===\n";
+                       try
+                       {
+                               foreach(new RecursiveIteratorIterator($this) as $k => $v)
+                               {
+                                       var_dump($k);
+                                       var_dump($v);
+                               }
+                       }
+                       catch (Exception $e)
+                       {
+                               echo "Exception: " . $e->getMessage() . " in " . $e->getFile() . " on line " . $e->getLine() . "\n";
+                       }
+                       MyRecursiveArrayIterator::$fail++;
+               }
+       }
+}
+
+$it = new MyRecursiveArrayIterator(array(0, array(10), 2, array(30), 4));
+$it = new MyRecursiveCachingIterator($it);
+
+$it->show();
+
+?>
+===DONE===
+<?php exit(0); ?>
+--EXPECTF--
+===0===
+MyRecursiveArrayIterator::hasChildren()
+int(0)
+int(0)
+MyRecursiveArrayIterator::hasChildren()
+MyRecursiveArrayIterator::getChildren()
+MyRecursiveArrayIterator::hasChildren()
+int(0)
+int(10)
+MyRecursiveArrayIterator::hasChildren()
+int(2)
+int(2)
+MyRecursiveArrayIterator::hasChildren()
+MyRecursiveArrayIterator::getChildren()
+MyRecursiveArrayIterator::hasChildren()
+int(0)
+int(30)
+MyRecursiveArrayIterator::hasChildren()
+int(4)
+int(4)
+===1===
+MyRecursiveArrayIterator::hasChildren()
+Exception: State 1: MyRecursiveArrayIterator::hasChildren() in %siterator_047.php on line %d
+===2===
+MyRecursiveArrayIterator::hasChildren()
+int(0)
+int(0)
+MyRecursiveArrayIterator::hasChildren()
+MyRecursiveArrayIterator::getChildren()
+Exception: State 2: MyRecursiveArrayIterator::getChildren() in %siterator_047.php on line %d
+===3===
+MyRecursiveArrayIterator::hasChildren()
+int(0)
+int(0)
+MyRecursiveArrayIterator::hasChildren()
+MyRecursiveArrayIterator::getChildren()
+MyRecursiveArrayIterator::hasChildren()
+int(0)
+int(10)
+MyRecursiveArrayIterator::hasChildren()
+int(2)
+int(2)
+MyRecursiveArrayIterator::hasChildren()
+MyRecursiveArrayIterator::getChildren()
+MyRecursiveArrayIterator::hasChildren()
+int(0)
+int(30)
+MyRecursiveArrayIterator::hasChildren()
+int(4)
+int(4)
+===DONE===
diff --git a/ext/spl/tests/iterator_048.phpt b/ext/spl/tests/iterator_048.phpt
new file mode 100755 (executable)
index 0000000..feea204
--- /dev/null
@@ -0,0 +1,44 @@
+--TEST--
+SPL: RecursiveCachingIterator and exception in has/getChildren
+--SKIPIF--
+<?php if (!extension_loaded("spl")) print "skip"; ?>
+--FILE--
+<?php
+
+class MyRecursiveRegexIterator extends RecursiveRegexIterator
+{
+       function show()
+       {
+               foreach(new RecursiveIteratorIterator($this) as $k => $v)
+               {
+                       var_dump($k);
+                       var_dump($v);
+               }
+       }
+
+       function accept()
+       {
+               return $this->hasChildren() || parent::accept();
+       }
+}
+
+$ar = new RecursiveArrayIterator(array('Foo', array('Bar'), 'FooBar', array('Baz'), 'Biz'));
+$it = new MyRecursiveRegexIterator($ar, '/Bar/');
+
+$it->show();
+
+?>
+===DONE===
+<?php exit(0); ?>
+--EXPECTF--
+int(0)
+string(3) "Bar"
+int(2)
+string(6) "FooBar"
+===DONE===
+--UEXPECTF--
+int(0)
+unicode(3) "Bar"
+int(2)
+unicode(6) "FooBar"
+===DONE===