]> granicus.if.org Git - php/commitdiff
- Allow to hook into RecursiveIteratorIterator's child handling
authorMarcus Boerger <helly@php.net>
Sat, 12 Mar 2005 23:03:33 +0000 (23:03 +0000)
committerMarcus Boerger <helly@php.net>
Sat, 12 Mar 2005 23:03:33 +0000 (23:03 +0000)
- Better=faster function caching for RecursiveIteratorIterator
- Check for exceptions and fix check for illegal return values
- Add UnexpectedValueException
- Add docu

ext/spl/spl.php
ext/spl/spl_exceptions.c
ext/spl/spl_exceptions.h
ext/spl/spl_iterators.c
ext/spl/tests/iterator_021.phpt [new file with mode: 0755]
ext/spl/tests/iterator_022.phpt [new file with mode: 0755]

index cac58446e795b8eef799a855a01f948f29fc1d24..b4c7ddfc2cc3ec7f58f6c283bc8af8496a8754ad 100755 (executable)
@@ -252,6 +252,8 @@ class BadMethodCallException extends BadFunctionCallException
  *
  * This kind of exception should be used to inform about domain erors in 
  * mathematical sense.
+ *
+ * @see RangeException
  */
 class DomainException extends LogicException
 {
@@ -260,6 +262,8 @@ class DomainException extends LogicException
 /** @ingroup SPL
  * @brief Exception that denotes invalid arguments were passed.
  * @since PHP 5.1
+ *
+ * @see UnexpectedValueException
  */
 class InvalidArgumentException extends LogicException
 {
@@ -321,19 +325,36 @@ class OverflowException extends RuntimeException
  * @since PHP 5.1
  *
  * Normally this means there was an arithmetic error other than under/overflow.
+ * This is the runtime version of DomainException.
+ *
+ * @see DomainException
  */
 class RangeException extends RuntimeException
 {
 }
 
 /** @ingroup SPL
- * @brief Exception Exception thrown to indicate arithmetic/buffer underflow.
+ * @brief Exception thrown to indicate arithmetic/buffer underflow.
  * @since PHP 5.1
  */
 class UnderflowException extends RuntimeException
 {
 }
 
+/** @ingroup SPL
+ * @brief Exception thrown to indicate an unexpected value.
+ * @since PHP 5.1
+ *
+ * Typically this happens when a function calls another function and espects
+ * the return value to be of a certain type or value not including arithmetic
+ * or buffer related errors. 
+ *
+ * @see InvalidArgumentException
+ */
+class UnexpectedValueException extends RuntimeException
+{
+}
+
 /** @ingroup ZendEngine
  * @brief Interface to override array access of objects.
  * @since PHP 5.0
index c4f3a6b46f2ba87699148560cc04374a8995b764..8d0bbfa060a94aba7872dcc626fcf44a4b63ae15 100755 (executable)
@@ -45,6 +45,7 @@ PHPAPI zend_class_entry *spl_ce_OutOfBoundsException;
 PHPAPI zend_class_entry *spl_ce_OverflowException;
 PHPAPI zend_class_entry *spl_ce_RangeException;
 PHPAPI zend_class_entry *spl_ce_UnderflowException;
+PHPAPI zend_class_entry *spl_ce_UnexpectedValueException;
 
 #define spl_ce_Exception zend_exception_get_default()
 
@@ -64,6 +65,7 @@ PHP_MINIT_FUNCTION(spl_exceptions)
     REGISTER_SPL_SUB_CLASS_EX(OverflowException,        RuntimeException, NULL, NULL);
     REGISTER_SPL_SUB_CLASS_EX(RangeException,           RuntimeException, NULL, NULL);
     REGISTER_SPL_SUB_CLASS_EX(UnderflowException,       RuntimeException, NULL, NULL);
+    REGISTER_SPL_SUB_CLASS_EX(UnexpectedValueException, RuntimeException, NULL, NULL);
 
        return SUCCESS;
 }
index 0f26e677b7f80b58c7adf56bdd5d1be6f2b656d5..bb3e7970928725e6305a8e8f6f6a610e65f6e4f6 100755 (executable)
@@ -37,6 +37,7 @@ extern PHPAPI zend_class_entry *spl_ce_OutOfBoundsException;
 extern PHPAPI zend_class_entry *spl_ce_OverflowException;
 extern PHPAPI zend_class_entry *spl_ce_RangeException;
 extern PHPAPI zend_class_entry *spl_ce_UnderflowException;
+extern PHPAPI zend_class_entry *spl_ce_UnexpectedValueException;
 
 PHP_MINIT_FUNCTION(spl_exceptions);
 
index bdf8d247d380e4ded5553c868a6263a7ca8a5833..e9858ea774ddd347c23c88da19a4c9ba5ba0055d 100755 (executable)
@@ -85,6 +85,8 @@ typedef struct _spl_recursive_it_object {
        spl_sub_iterator         *iterators;
        int                      level;
        RecursiveIteratorMode    mode;
+       zend_function            *callHasChildren;
+       zend_function            *callGetChildren;
        zend_function            *beginChildren;
        zend_function            *endChildren;
        zend_class_entry         *ce;
@@ -183,7 +185,11 @@ next_step:
                        case RS_TEST:
                                ce = object->iterators[object->level].ce;
                                zobject = object->iterators[object->level].zobject;
-                               zend_call_method_with_0_params(&zobject, ce, NULL, "haschildren", &retval);
+                               if (object->callHasChildren) {
+                                       zend_call_method_with_0_params(&zthis, object->ce, &object->callHasChildren, "callHasChildren", &retval);
+                               } else {
+                                       zend_call_method_with_0_params(&zobject, ce, NULL, "haschildren", &retval);
+                               }
                                if (retval) {
                                        has_children = zend_is_true(retval);
                                        zval_ptr_dtor(&retval);
@@ -211,13 +217,17 @@ next_step:
                        case RS_CHILD:
                                ce = object->iterators[object->level].ce;
                                zobject = object->iterators[object->level].zobject;
-                               zend_call_method_with_0_params(&zobject, ce, NULL, "getchildren", &child);
-                               ce = child ? Z_OBJCE_P(child) : NULL;
+                               if (object->callGetChildren) {
+                                       zend_call_method_with_0_params(&zthis, object->ce, &object->callGetChildren, "callGetChildren", &child);
+                               } else {
+                                       zend_call_method_with_0_params(&zobject, ce, NULL, "getchildren", &child);
+                               }
+                               ce = child && Z_TYPE_P(child) == IS_OBJECT ? Z_OBJCE_P(child) : NULL;
                                if (!ce || !instanceof_function(ce, spl_ce_RecursiveIterator TSRMLS_CC)) {
                                        if (child) {
                                                zval_ptr_dtor(&child);
                                        }
-                                       zend_throw_exception(spl_ce_InvalidArgumentException, "Objects returned by RecursiveIterator::getChildren() must implement RecursiveIterator", 0 TSRMLS_CC);
+                                       zend_throw_exception(spl_ce_UnexpectedValueException, "Objects returned by RecursiveIterator::getChildren() must implement RecursiveIterator", 0 TSRMLS_CC);
                                        return;
                                }
                                if (object->mode == RIT_CHILD_FIRST) {
@@ -234,7 +244,7 @@ next_step:
                                if (sub_iter->funcs->rewind) {
                                        sub_iter->funcs->rewind(sub_iter TSRMLS_CC);
                                }
-                               if (!object->beginChildren || object->beginChildren->common.scope != spl_ce_RecursiveIteratorIterator) {
+                               if (object->beginChildren) {
                                        zend_call_method_with_0_params(&zthis, object->ce, &object->beginChildren, "beginchildren", NULL);
                                }
                                goto next_step;
@@ -244,7 +254,7 @@ next_step:
                        iterator->funcs->dtor(iterator TSRMLS_CC);
                        zval_ptr_dtor(&object->iterators[object->level].zobject);
                        object->level--;
-                       if (!object->endChildren || object->endChildren->common.scope != spl_ce_RecursiveIteratorIterator) {
+                       if (object->endChildren) {
                                zend_call_method_with_0_params(&zthis, object->ce, &object->endChildren, "endchildren", NULL);
                        }
                } else {
@@ -335,9 +345,23 @@ SPL_METHOD(RecursiveIteratorIterator, __construct)
        intern->iterators = emalloc(sizeof(spl_sub_iterator));
        intern->level = 0;
        intern->mode = mode;
-       intern->beginChildren = NULL;
-       intern->endChildren = NULL;
        intern->ce = Z_OBJCE_P(object);
+       zend_hash_find(&intern->ce->function_table, "callhaschildren", sizeof("callHasChildren"), (void **) &intern->callHasChildren);
+       if (intern->callHasChildren->common.scope == spl_ce_RecursiveIteratorIterator) {
+               intern->callHasChildren = NULL;
+       }
+       zend_hash_find(&intern->ce->function_table, "callgetchildren", sizeof("callGetChildren"), (void **) &intern->callGetChildren);
+       if (intern->callGetChildren->common.scope == spl_ce_RecursiveIteratorIterator) {
+               intern->callGetChildren = NULL;
+       }
+       zend_hash_find(&intern->ce->function_table, "beginchildren", sizeof("beginchildren"), (void **) &intern->beginChildren);
+       if (intern->beginChildren->common.scope == spl_ce_RecursiveIteratorIterator) {
+               intern->beginChildren = NULL;
+       }
+       zend_hash_find(&intern->ce->function_table, "endchildren", sizeof("endchildren"), (void **) &intern->endChildren);
+       if (intern->endChildren->common.scope == spl_ce_RecursiveIteratorIterator) {
+               intern->endChildren = NULL;
+       }
        ce_iterator = Z_OBJCE_P(iterator); /* respect inheritance, don't use spl_ce_RecursiveIterator */
        intern->iterators[0].iterator = ce_iterator->get_iterator(ce_iterator, iterator TSRMLS_CC);
        iterator->refcount++;
@@ -443,6 +467,40 @@ SPL_METHOD(RecursiveIteratorIterator, getInnerIterator)
        RETURN_ZVAL(object->iterators[level].zobject, 1, 0);
 } /* }}} */
 
+/* {{{ proto bool RecursiveIteratorIterator::callHasChildren()
+   Called for each element to test whether it has children */
+SPL_METHOD(RecursiveIteratorIterator, callHasChildren)
+{
+       spl_recursive_it_object   *object = (spl_recursive_it_object*)zend_object_store_get_object(getThis() TSRMLS_CC);
+       zend_class_entry *ce = object->iterators[object->level].ce;
+       zval *retval, *zobject;
+
+       zobject = object->iterators[object->level].zobject;
+       if (!zobject) {
+               RETURN_FALSE;
+       } else {
+               zend_call_method_with_0_params(&zobject, ce, NULL, "haschildren", &retval);
+               RETURN_ZVAL(retval, 0, 1);
+       }
+} /* }}} */
+
+/* {{{ proto RecursiveIterator RecursiveIteratorIterator::callGetChildren()
+   Return children of current element */
+SPL_METHOD(RecursiveIteratorIterator, callGetChildren)
+{
+       spl_recursive_it_object   *object = (spl_recursive_it_object*)zend_object_store_get_object(getThis() TSRMLS_CC);
+       zend_class_entry *ce = object->iterators[object->level].ce;
+       zval *retval, *zobject;
+
+       zobject = object->iterators[object->level].zobject;
+       if (!zobject) {
+               return;
+       } else {
+               zend_call_method_with_0_params(&zobject, ce, NULL, "getchildren", &retval);
+               RETURN_ZVAL(retval, 0, 1);
+       }
+} /* }}} */
+
 /* {{{ proto RecursiveIterator RecursiveIteratorIterator::beginChildren()
    Called when recursing one level down */
 SPL_METHOD(RecursiveIteratorIterator, beginChildren)
@@ -538,10 +596,12 @@ static zend_function_entry spl_funcs_RecursiveIteratorIterator[] = {
        SPL_ME(RecursiveIteratorIterator, current,       NULL, ZEND_ACC_PUBLIC)
        SPL_ME(RecursiveIteratorIterator, next,          NULL, ZEND_ACC_PUBLIC)
        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, beginChildren, NULL, ZEND_ACC_PUBLIC)
-       SPL_ME(RecursiveIteratorIterator, endChildren,   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, callHasChildren,   NULL, ZEND_ACC_PUBLIC)
+       SPL_ME(RecursiveIteratorIterator, callGetChildren,   NULL, ZEND_ACC_PUBLIC)
+       SPL_ME(RecursiveIteratorIterator, beginChildren,     NULL, ZEND_ACC_PUBLIC)
+       SPL_ME(RecursiveIteratorIterator, endChildren,       NULL, ZEND_ACC_PUBLIC)
        {NULL, NULL, NULL}
 };
 
diff --git a/ext/spl/tests/iterator_021.phpt b/ext/spl/tests/iterator_021.phpt
new file mode 100755 (executable)
index 0000000..cb17907
--- /dev/null
@@ -0,0 +1,185 @@
+--TEST--
+SPL: RecursiveIteratorIterator and hasChildren
+--FILE--
+<?php
+
+class RecursiveArrayIterator extends ArrayIterator implements RecursiveIterator
+{
+       function hasChildren()
+       {
+               return is_array($this->current());
+       }
+       
+       function getChildren()
+       {
+               echo __METHOD__ . "\n";
+               return new RecursiveArrayIterator($this->current());
+       }
+
+       function valid()
+       {
+               if (!parent::valid())
+               {
+                       echo __METHOD__ . " = false\n";
+                       return false;
+               }
+               else
+               {
+                       return true;
+               }
+       }
+}
+
+class RecursiveArrayIteratorIterator extends RecursiveIteratorIterator
+{
+       private $max_depth;
+       private $over = 0;
+       private $skip = false;
+
+       function __construct($it, $max_depth)
+       {
+               $this->max_depth = $max_depth;
+               parent::__construct($it);
+       }
+
+       function rewind()
+       {
+               echo __METHOD__ . "\n";
+               $this->skip = false;
+               parent::rewind();
+       }
+
+       function valid()
+       {
+               echo __METHOD__ . "\n";
+               if ($this->skip)
+               {
+                       $this->skip = false;
+                       $this->next();
+               }
+               return parent::valid();
+       }
+
+       function current()
+       {
+               echo __METHOD__ . "\n";
+               return parent::current();
+       }
+
+       function key()
+       {
+               echo __METHOD__ . "\n";
+               return parent::key();
+       }
+
+       function next()
+       {
+               echo __METHOD__ . "\n";
+               parent::next();
+       }
+
+       function callHasChildren()
+       {
+               $this->skip = false;
+               $has = parent::callHasChildren();
+               $res = $this->getDepth() < $this->max_depth && $has;
+               echo __METHOD__ . "(".$this->getDepth().") = ".($res?"yes":"no")."/".($has?"yes":"no")."\n";
+               if ($has && !$res)
+               {
+                       $this->over++;
+                       if ($this->over == 2) {
+                               $this->skip = true;
+                       }
+               }
+               return $res;
+       }
+
+       function beginChildren()
+       {
+               echo __METHOD__ . "(".$this->getDepth().")\n";
+       }
+
+       function endChildren()
+       {
+               echo __METHOD__ . "(".$this->getDepth().")\n";
+       }
+}
+
+foreach(new RecursiveArrayIteratorIterator(new RecursiveArrayIterator(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";
+}
+?>
+===DONE===
+<?php exit(0); ?>
+--EXPECTF--
+RecursiveArrayIteratorIterator::rewind
+RecursiveArrayIteratorIterator::callHasChildren(0) = no/no
+RecursiveArrayIteratorIterator::valid
+RecursiveArrayIteratorIterator::current
+RecursiveArrayIteratorIterator::key
+0=>a
+RecursiveArrayIteratorIterator::next
+RecursiveArrayIteratorIterator::callHasChildren(0) = yes/yes
+RecursiveArrayIterator::getChildren
+RecursiveArrayIteratorIterator::beginChildren(1)
+RecursiveArrayIteratorIterator::callHasChildren(1) = no/no
+RecursiveArrayIteratorIterator::valid
+RecursiveArrayIteratorIterator::current
+RecursiveArrayIteratorIterator::key
+0=>ba
+RecursiveArrayIteratorIterator::next
+RecursiveArrayIteratorIterator::callHasChildren(1) = yes/yes
+RecursiveArrayIterator::getChildren
+RecursiveArrayIteratorIterator::beginChildren(2)
+RecursiveArrayIteratorIterator::callHasChildren(2) = no/no
+RecursiveArrayIteratorIterator::valid
+RecursiveArrayIteratorIterator::current
+RecursiveArrayIteratorIterator::key
+0=>bba
+RecursiveArrayIteratorIterator::next
+RecursiveArrayIteratorIterator::callHasChildren(2) = no/no
+RecursiveArrayIteratorIterator::valid
+RecursiveArrayIteratorIterator::current
+RecursiveArrayIteratorIterator::key
+1=>bbb
+RecursiveArrayIteratorIterator::next
+RecursiveArrayIterator::valid = false
+RecursiveArrayIteratorIterator::endChildren(1)
+RecursiveArrayIteratorIterator::callHasChildren(1) = yes/yes
+RecursiveArrayIterator::getChildren
+RecursiveArrayIteratorIterator::beginChildren(2)
+RecursiveArrayIteratorIterator::callHasChildren(2) = no/yes
+RecursiveArrayIteratorIterator::valid
+RecursiveArrayIteratorIterator::current
+RecursiveArrayIteratorIterator::key
+0=>bcaa
+RecursiveArrayIteratorIterator::next
+RecursiveArrayIteratorIterator::callHasChildren(2) = no/yes
+RecursiveArrayIteratorIterator::valid
+RecursiveArrayIteratorIterator::next
+RecursiveArrayIterator::valid = false
+RecursiveArrayIteratorIterator::endChildren(1)
+RecursiveArrayIterator::valid = false
+RecursiveArrayIteratorIterator::endChildren(0)
+RecursiveArrayIteratorIterator::callHasChildren(0) = yes/yes
+RecursiveArrayIterator::getChildren
+RecursiveArrayIteratorIterator::beginChildren(1)
+RecursiveArrayIteratorIterator::callHasChildren(1) = no/no
+RecursiveArrayIteratorIterator::current
+RecursiveArrayIteratorIterator::key
+0=>ca
+RecursiveArrayIteratorIterator::next
+RecursiveArrayIterator::valid = false
+RecursiveArrayIteratorIterator::endChildren(0)
+RecursiveArrayIteratorIterator::callHasChildren(0) = no/no
+RecursiveArrayIteratorIterator::valid
+RecursiveArrayIteratorIterator::current
+RecursiveArrayIteratorIterator::key
+3=>d
+RecursiveArrayIteratorIterator::next
+RecursiveArrayIterator::valid = false
+RecursiveArrayIteratorIterator::valid
+RecursiveArrayIterator::valid = false
+===DONE===
diff --git a/ext/spl/tests/iterator_022.phpt b/ext/spl/tests/iterator_022.phpt
new file mode 100755 (executable)
index 0000000..988eb62
--- /dev/null
@@ -0,0 +1,191 @@
+--TEST--
+SPL: RecursiveIteratorIterator and callHasChildren/callGetChildren
+--FILE--
+<?php
+
+class RecursiveArrayIterator extends ArrayIterator implements RecursiveIterator
+{
+       function hasChildren()
+       {
+               return is_array($this->current());
+       }
+       
+       function getChildren()
+       {
+               echo __METHOD__ . "\n";
+               return $this->current();
+       }
+
+       function valid()
+       {
+               if (!parent::valid())
+               {
+                       echo __METHOD__ . " = false\n";
+                       return false;
+               }
+               else
+               {
+                       return true;
+               }
+       }
+}
+
+class RecursiveArrayIteratorIterator extends RecursiveIteratorIterator
+{
+       private $max_depth;
+       private $over = 0;
+       private $skip = false;
+
+       function __construct($it, $max_depth)
+       {
+               $this->max_depth = $max_depth;
+               parent::__construct($it);
+       }
+
+       function rewind()
+       {
+               echo __METHOD__ . "\n";
+               $this->skip = false;
+               parent::rewind();
+       }
+
+       function valid()
+       {
+               echo __METHOD__ . "\n";
+               if ($this->skip)
+               {
+                       $this->skip = false;
+                       $this->next();
+               }
+               return parent::valid();
+       }
+
+       function current()
+       {
+               echo __METHOD__ . "\n";
+               return parent::current();
+       }
+
+       function key()
+       {
+               echo __METHOD__ . "\n";
+               return parent::key();
+       }
+
+       function next()
+       {
+               echo __METHOD__ . "\n";
+               parent::next();
+       }
+
+       function callHasChildren()
+       {
+               $this->skip = false;
+               $has = parent::callHasChildren();
+               $res = $this->getDepth() < $this->max_depth && $has;
+               echo __METHOD__ . "(".$this->getDepth().") = ".($res?"yes":"no")."/".($has?"yes":"no")."\n";
+               if ($has && !$res)
+               {
+                       $this->over++;
+                       if ($this->over == 2) {
+                               $this->skip = true;
+                       }
+               }
+               return $res;
+       }
+       
+       function callGetChildren()
+       {
+               if ($this->over == 2)
+               {
+                       echo __METHOD__ . "(skip)\n";
+                       return NULL;
+               }
+               echo __METHOD__ . "(ok:{$this->over})\n";
+               return new RecursiveArrayIterator($this->current());
+       }
+
+       function beginChildren()
+       {
+               echo __METHOD__ . "(".$this->getDepth().")\n";
+       }
+
+       function endChildren()
+       {
+               echo __METHOD__ . "(".$this->getDepth().")\n";
+       }
+}
+
+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)
+       {
+               if (is_array($v)) $v = join('',$v);
+               echo "$k=>$v\n";
+       }
+}
+catch(UnexpectedValueException $e)
+{
+       echo $e->getMessage() . "\n";
+}
+
+?>
+===DONE===
+<?php exit(0); ?>
+--EXPECTF--
+RecursiveArrayIteratorIterator::rewind
+RecursiveArrayIteratorIterator::callHasChildren(0) = no/no
+RecursiveArrayIteratorIterator::valid
+RecursiveArrayIteratorIterator::current
+RecursiveArrayIteratorIterator::key
+0=>a
+RecursiveArrayIteratorIterator::next
+RecursiveArrayIteratorIterator::callHasChildren(0) = yes/yes
+RecursiveArrayIteratorIterator::callGetChildren(ok:0)
+RecursiveArrayIteratorIterator::current
+RecursiveArrayIteratorIterator::beginChildren(1)
+RecursiveArrayIteratorIterator::callHasChildren(1) = no/no
+RecursiveArrayIteratorIterator::valid
+RecursiveArrayIteratorIterator::current
+RecursiveArrayIteratorIterator::key
+0=>ba
+RecursiveArrayIteratorIterator::next
+RecursiveArrayIteratorIterator::callHasChildren(1) = yes/yes
+RecursiveArrayIteratorIterator::callGetChildren(ok:0)
+RecursiveArrayIteratorIterator::current
+RecursiveArrayIteratorIterator::beginChildren(2)
+RecursiveArrayIteratorIterator::callHasChildren(2) = no/no
+RecursiveArrayIteratorIterator::valid
+RecursiveArrayIteratorIterator::current
+RecursiveArrayIteratorIterator::key
+0=>bba
+RecursiveArrayIteratorIterator::next
+RecursiveArrayIteratorIterator::callHasChildren(2) = no/no
+RecursiveArrayIteratorIterator::valid
+RecursiveArrayIteratorIterator::current
+RecursiveArrayIteratorIterator::key
+1=>bbb
+RecursiveArrayIteratorIterator::next
+RecursiveArrayIterator::valid = false
+RecursiveArrayIteratorIterator::endChildren(1)
+RecursiveArrayIteratorIterator::callHasChildren(1) = yes/yes
+RecursiveArrayIteratorIterator::callGetChildren(ok:0)
+RecursiveArrayIteratorIterator::current
+RecursiveArrayIteratorIterator::beginChildren(2)
+RecursiveArrayIteratorIterator::callHasChildren(2) = no/yes
+RecursiveArrayIteratorIterator::valid
+RecursiveArrayIteratorIterator::current
+RecursiveArrayIteratorIterator::key
+0=>bcaa
+RecursiveArrayIteratorIterator::next
+RecursiveArrayIteratorIterator::callHasChildren(2) = no/yes
+RecursiveArrayIteratorIterator::valid
+RecursiveArrayIteratorIterator::next
+RecursiveArrayIterator::valid = false
+RecursiveArrayIteratorIterator::endChildren(1)
+RecursiveArrayIterator::valid = false
+RecursiveArrayIteratorIterator::endChildren(0)
+RecursiveArrayIteratorIterator::callHasChildren(0) = yes/yes
+RecursiveArrayIteratorIterator::callGetChildren(skip)
+Objects returned by RecursiveIterator::getChildren() must implement RecursiveIterator
+===DONE===