]> granicus.if.org Git - php/commitdiff
- Implement RecursiveIteratorIterator::setMaxDepth()/getMaxDepth()
authorMarcus Boerger <helly@php.net>
Sun, 25 Sep 2005 12:01:31 +0000 (12:01 +0000)
committerMarcus Boerger <helly@php.net>
Sun, 25 Sep 2005 12:01:31 +0000 (12:01 +0000)
ext/spl/internal/recursiveiteratoriterator.inc
ext/spl/spl_iterators.c
ext/spl/tests/iterator_028.phpt [new file with mode: 0755]

index 61a9d60a2715c782c3af17942b69c6637c151ada..cc26e3ef49b577dc1ed7517b9af33eb47581b27b 100755 (executable)
@@ -12,7 +12,7 @@
 /**
  * @brief   Iterates through recursive iterators
  * @author  Marcus Boerger
- * @version 1.2
+ * @version 1.3
  * @since PHP 5.0
  *
  * The objects of this class are created by instances of RecursiveIterator. 
@@ -33,9 +33,10 @@ class RecursiveIteratorIterator implements OuterIterator
        const CATCH_GET_CHILD   = 2;
 
        private $ait = array();
-       private $count = 0;
+       private $level = 0;
        private $mode  = self::LEAVES_ONLY;
        private $flags = 0;
+       private $max_depth = -1;
 
        /** Construct from RecursiveIterator
         *
@@ -61,8 +62,8 @@ class RecursiveIteratorIterator implements OuterIterator
         */
        function rewind()
        {
-               while ($this->count) {
-                       unset($this->ait[$this->count--]);
+               while ($this->level) {
+                       unset($this->ait[$this->level--]);
                        $this->endChildren();
                }
                $this->ait[0]->rewind();
@@ -74,13 +75,13 @@ class RecursiveIteratorIterator implements OuterIterator
         */
        function valid()
        {
-               $count = $this->count;
-               while ($count) {
-                       $it = $this->ait[$count];
+               $level = $this->level;
+               while ($level >= 0) {
+                       $it = $this->ait[$level];
                        if ($it->valid()) {
                                return true;
                        }
-                       $count--;
+                       $level--;
                        $this->endChildren();
                }
                return false;
@@ -90,7 +91,7 @@ class RecursiveIteratorIterator implements OuterIterator
         */
        function key()
        {
-               $it = $this->ait[$this->count];
+               $it = $this->ait[$this->level];
                return $it->key();
        }
        
@@ -98,7 +99,7 @@ class RecursiveIteratorIterator implements OuterIterator
         */
        function current()
        {
-               $it = $this->ait[$this->count];
+               $it = $this->ait[$this->level];
                return $it->current();
        }
        
@@ -106,46 +107,61 @@ class RecursiveIteratorIterator implements OuterIterator
         */
        function next()
        {
-               while ($this->count) {
-                       $it = $this->ait[$this->count];
+               while ($this->level >= 0) {
+                       $it = $this->ait[$this->level];
                        if ($it->valid()) {
                                if (!$it->recursed && callHasChildren()) {
-                                       $it->recursed = true;
-                                       try
-                                       {
-                                               $sub = callGetChildren();
+                                       if ($this->max_depth == -1 || $this->max_depth > $this->level) {
+                                               $it->recursed = true;
+                                               try
+                                               {
+                                                       $sub = callGetChildren();
+                                               }
+                                               catch (Exception $e)
+                                               {
+                                                       if (!($this->flags & self::CATCH_GET_CHILD))
+                                                       {
+                                                               throw $e;
+                                                       }
+                                                       $it->next();
+                                                       continue;
+                                               }
+                                               $sub->recursed = false;
+                                               $sub->rewind();
+                                               if ($sub->valid()) {
+                                                       $this->ait[++$this->level] = $sub;
+                                                       if (!$sub instanceof RecursiveIterator) {
+                                                               throw new Exception(get_class($sub).'::getChildren() must return an object that implements RecursiveIterator');
+                                                       }
+                                                       $this->beginChildren();
+                                                       return;
+                                               }
+                                               unset($sub);
                                        }
-                                       catch (Exception $e)
+                                       else
                                        {
-                                               if (!($this->flags & self::CATCH_GET_CHILD))
+                                               /* do not recurse because of depth restriction */
+                                               if ($this->flages & self::LEVAES_ONLY)
                                                {
-                                                       throw $e;
+                                                       $it->next();
+                                                       continue;
                                                }
-                                               $it->next();
-                                               continue;
-                                       }
-                                       $sub->recursed = false;
-                                       $sub->rewind();
-                                       if ($sub->valid()) {
-                                               $this->ait[++$this->count] = $sub;
-                                               if (!$sub instanceof RecursiveIterator) {
-                                                       throw new Exception(get_class($sub).'::getChildren() must return an object that implements RecursiveIterator');
+                                               else
+                                               {
+                                                       return; // we want the parent
                                                }
-                                               $this->beginChildren();
+                                       }
+                                       $it->next();
+                                       $it->recursed = false;
+                                       if ($it->valid()) {
                                                return;
                                        }
-                                       unset($sub);
-                               }
-                               $it->next();
-                               $it->recursed = false;
-                               if ($it->valid()) {
-                                       return;
+                                       $it->recursed = false;
                                }
-                               $it->recursed = false;
                        }
-                       if ($this->count) {
-                               unset($this->ait[$this->count--]);
-                               $it = $this->ait[$this->count];
+                       else if ($this->level > 0) {
+                               unset($this->ait[$this->level--]);
+                               $it = $this->ait[$this->level];
                                $this->endChildren();
                                callNextElement(false);
                        }
@@ -159,7 +175,7 @@ class RecursiveIteratorIterator implements OuterIterator
        function getSubIterator($level = NULL)
        {
                if (is_null($level)) {
-                       $level = $this->count;
+                       $level = $this->level;
                }
                return @$this->ait[$level];
        }
@@ -184,7 +200,7 @@ class RecursiveIteratorIterator implements OuterIterator
         */
        function callHasChildren()
        {
-               return $this->ait[$this->count]->hasChildren();
+               return $this->ait[$this->level]->hasChildren();
        }
 
        /** @return current sub iterators current children
@@ -192,7 +208,7 @@ class RecursiveIteratorIterator implements OuterIterator
         */
        function callGetChildren()
        {
-               return $this->ait[$this->count]->getChildren();
+               return $this->ait[$this->level]->getChildren();
        }
 
        /** Called right after calling getChildren() and its rewind().
@@ -230,6 +246,24 @@ class RecursiveIteratorIterator implements OuterIterator
        /** Called when the next element is available
         */
        function nextElement();
+
+       /** @param max_depth new maximum allowed depth or -1 for any depth
+        */
+       function setMaxDepth($max_depth = -1)
+       {
+               $max_depth = (int)$max_depth;
+               if ($max_depth < -1) {
+                       throw new OutOfRangeException('Parameter max_depth must be >= -1');
+               }
+               $this->max_depth = $max_depth;
+       }
+       
+       /** @return maximum allowed depth or false if any depth is allowed
+        */
+       function getMaxDepth()
+       {
+               return $this->max_depth == -1 ? false : $this->max_depth;
+       }
 }
 
 ?>
\ No newline at end of file
index ef016961e1d9a6dd96bb92be7aa1a5a0692fd8ae..87ad2a99643dedf6bfc066168e23358521857bba 100755 (executable)
@@ -89,6 +89,7 @@ typedef struct _spl_recursive_it_object {
        int                      level;
        RecursiveIteratorMode    mode;
        int                      flags;
+       int                      max_depth;
        zend_bool                in_iteration;
        zend_function            *beginIteration;
        zend_function            *endIteration;
@@ -119,7 +120,7 @@ static void spl_recursive_it_dtor(zend_object_iterator *_iter TSRMLS_DC)
                sub_iter->funcs->dtor(sub_iter TSRMLS_CC);
                zval_ptr_dtor(&object->iterators[object->level--].zobject);
        }
-       erealloc(object->iterators, sizeof(spl_sub_iterator));
+       object->iterators = erealloc(object->iterators, sizeof(spl_sub_iterator));
        object->level = 0;
 
        zval_ptr_dtor(&iter->zobject);  
@@ -206,14 +207,23 @@ next_step:
                                        has_children = zend_is_true(retval);
                                        zval_ptr_dtor(&retval);
                                        if (has_children) {
-                                               switch (object->mode) {
-                                               case RIT_LEAVES_ONLY:
-                                               case RIT_CHILD_FIRST:
-                                                       object->iterators[object->level].state = RS_CHILD;
-                                                       goto next_step;
-                                               case RIT_SELF_FIRST:
-                                                       object->iterators[object->level].state = RS_SELF;
-                                                       goto next_step;
+                                               if (object->max_depth == -1 || object->max_depth > object->level) {
+                                                       switch (object->mode) {
+                                                       case RIT_LEAVES_ONLY:
+                                                       case RIT_CHILD_FIRST:
+                                                               object->iterators[object->level].state = RS_CHILD;
+                                                               goto next_step;
+                                                       case RIT_SELF_FIRST:
+                                                               object->iterators[object->level].state = RS_SELF;
+                                                               goto next_step;
+                                                       }
+                                               } else {
+                                                       /* do not recurse into */
+                                                       if (object->mode == RIT_LEAVES_ONLY) {
+                                                               /* this is not a leave, so skip it */
+                                                               object->iterators[object->level].state = RS_NEXT;
+                                                               goto next_step;
+                                                       }
                                                }
                                        }
                                }
@@ -382,8 +392,10 @@ SPL_METHOD(RecursiveIteratorIterator, __construct)
        intern->level = 0;
        intern->mode = mode;
        intern->flags = flags;
+       intern->max_depth = -1;
        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;
@@ -588,6 +600,36 @@ SPL_METHOD(RecursiveIteratorIterator, nextElement)
        /* nothing to do */
 } /* }}} */
 
+/* {{{ proto RecursiveIterator RecursiveIteratorIterator::setMaxDepth([$max_depth = -1])
+   Set the maximum allowed depth (or any depth if pmax_depth = -1] */
+SPL_METHOD(RecursiveIteratorIterator, setMaxDepth)
+{
+       spl_recursive_it_object   *object = (spl_recursive_it_object*)zend_object_store_get_object(getThis() TSRMLS_CC);
+       long  max_depth = -1;
+       
+       if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &max_depth) == FAILURE) {
+               return;
+       }
+       if (max_depth < -1) {
+               zend_throw_exception(U_CLASS_ENTRY(spl_ce_OutOfRangeException), "Parameter max_depth must be >= -1", 0 TSRMLS_CC);
+               return;
+       }
+       object->max_depth = max_depth;
+} /* }}} */
+
+/* {{{ proto RecursiveIterator RecursiveIteratorIterator::getMaxDepth()
+   Return the maximum accepted depth or false if any depth is allowed */
+SPL_METHOD(RecursiveIteratorIterator, getMaxDepth)
+{
+       spl_recursive_it_object   *object = (spl_recursive_it_object*)zend_object_store_get_object(getThis() TSRMLS_CC);
+
+       if (object->max_depth == -1) {
+               RETURN_FALSE;
+       } else {
+               RETURN_LONG(object->max_depth);
+       }
+} /* }}} */
+
 static union _zend_function *spl_recursive_it_get_method(zval **object_ptr, char *method, int method_len TSRMLS_DC)
 {
        union _zend_function    *function_handler;
@@ -620,6 +662,7 @@ static void spl_RecursiveIteratorIterator_free_storage(void *_object TSRMLS_DC)
                        zval_ptr_dtor(&object->iterators[object->level--].zobject);
                }
                efree(object->iterators);
+               object->iterators = NULL;
        }
 
        zend_hash_destroy(object->std.properties);
@@ -661,23 +704,30 @@ ZEND_BEGIN_ARG_INFO(arginfo_recursive_it_getSubIterator, 0)
        ZEND_ARG_INFO(0, level)
 ZEND_END_ARG_INFO();
 
+static
+ZEND_BEGIN_ARG_INFO(arginfo_recursive_it_setMaxDepth, 0) 
+       ZEND_ARG_INFO(0, max_depth)
+ZEND_END_ARG_INFO();
+
 static zend_function_entry spl_funcs_RecursiveIteratorIterator[] = {
-       SPL_ME(RecursiveIteratorIterator, __construct,   arginfo_recursive_it___construct, ZEND_ACC_PUBLIC)
-       SPL_ME(RecursiveIteratorIterator, rewind,        NULL, ZEND_ACC_PUBLIC)
-       SPL_ME(RecursiveIteratorIterator, valid,         NULL, ZEND_ACC_PUBLIC)
-       SPL_ME(RecursiveIteratorIterator, key,           NULL, ZEND_ACC_PUBLIC)
-       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, __construct,       arginfo_recursive_it___construct,    ZEND_ACC_PUBLIC)
+       SPL_ME(RecursiveIteratorIterator, rewind,            NULL,                                ZEND_ACC_PUBLIC)
+       SPL_ME(RecursiveIteratorIterator, valid,             NULL,                                ZEND_ACC_PUBLIC)
+       SPL_ME(RecursiveIteratorIterator, key,               NULL,                                ZEND_ACC_PUBLIC)
+       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, 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)
-       SPL_ME(RecursiveIteratorIterator, endChildren,       NULL, ZEND_ACC_PUBLIC)
-       SPL_ME(RecursiveIteratorIterator, nextElement,       NULL, 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)
+       SPL_ME(RecursiveIteratorIterator, endChildren,       NULL,                                ZEND_ACC_PUBLIC)
+       SPL_ME(RecursiveIteratorIterator, nextElement,       NULL,                                ZEND_ACC_PUBLIC)
+       SPL_ME(RecursiveIteratorIterator, setMaxDepth,       arginfo_recursive_it_setMaxDepth,    ZEND_ACC_PUBLIC)
+       SPL_ME(RecursiveIteratorIterator, getMaxDepth,       NULL,                                ZEND_ACC_PUBLIC)
        {NULL, NULL, NULL}
 };
 
@@ -1439,13 +1489,10 @@ static INLINE void spl_caching_it_next(spl_dual_it_object *intern TSRMLS_DC)
                        
                        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;
+                       if (intern->current.key_type == HASH_KEY_IS_LONG) {
+                               add_index_zval(intern->u.caching.zcache, intern->current.int_key, zcacheval);
+                       } else {
+                               zend_u_symtable_update(HASH_OF(intern->u.caching.zcache), intern->current.key_type, intern->current.str_key, intern->current.str_key_len, &zcacheval, sizeof(void*), NULL);
                        }
                }
                /* Recursion ? */
diff --git a/ext/spl/tests/iterator_028.phpt b/ext/spl/tests/iterator_028.phpt
new file mode 100755 (executable)
index 0000000..8b53b21
--- /dev/null
@@ -0,0 +1,112 @@
+--TEST--
+SPL: RecursiveIteratorIterator and setMaxDepth()
+--FILE--
+<?php
+
+$ar = array(1, 2, array(31, 32, array(331, array(3321, array(33221)))), 4);
+
+$it = new RecursiveIteratorIterator(new RecursiveArrayIterator($ar));
+
+echo "===?===\n";
+var_dump($it->getMaxDepth());
+foreach($it as $v) echo $it->getDepth() . ": $v\n";
+
+echo "===2===\n";
+$it->setMaxDepth(2);
+var_dump($it->getMaxDepth());
+foreach($it as $v) echo $it->getDepth() . ": $v\n";
+
+echo "===X===\n";
+$it->setMaxDepth();
+var_dump($it->getMaxDepth());
+foreach($it as $v) echo $it->getDepth() . ": $v\n";
+
+echo "===3===\n";
+$it->setMaxDepth(3);
+var_dump($it->getMaxDepth());
+foreach($it as $v) echo $it->getDepth() . ": $v\n";
+
+echo "===5===\n";
+$it->setMaxDepth(5);
+var_dump($it->getMaxDepth());
+foreach($it as $v) echo $it->getDepth() . ": $v\n";
+
+echo "===0===\n";
+$it->setMaxDepth(0);
+var_dump($it->getMaxDepth());
+foreach($it as $v) echo $it->getDepth() . ": $v\n";
+
+echo "===-1===\n";
+$it->setMaxDepth(-1);
+var_dump($it->getMaxDepth());
+try
+{
+       $it->setMaxDepth(4);
+       $it->setMaxDepth(-2);
+}
+catch(Exception $e)
+{
+       var_dump($e->getMessage());
+}
+var_dump($it->getMaxDepth());
+?>
+===DONE===
+<?php exit(0); ?>
+--EXPECT--
+===?===
+bool(false)
+0: 1
+0: 2
+1: 31
+1: 32
+2: 331
+3: 3321
+4: 33221
+0: 4
+===2===
+int(2)
+0: 1
+0: 2
+1: 31
+1: 32
+2: 331
+0: 4
+===X===
+bool(false)
+0: 1
+0: 2
+1: 31
+1: 32
+2: 331
+3: 3321
+4: 33221
+0: 4
+===3===
+int(3)
+0: 1
+0: 2
+1: 31
+1: 32
+2: 331
+3: 3321
+0: 4
+===5===
+int(5)
+0: 1
+0: 2
+1: 31
+1: 32
+2: 331
+3: 3321
+4: 33221
+0: 4
+===0===
+int(0)
+0: 1
+0: 2
+0: 4
+===-1===
+bool(false)
+string(33) "Parameter max_depth must be >= -1"
+int(4)
+===DONE===