From 7e1763a115287c11545df256036570365528ac7d Mon Sep 17 00:00:00 2001 From: Marcus Boerger Date: Sun, 25 Sep 2005 12:01:31 +0000 Subject: [PATCH] - Implement RecursiveIteratorIterator::setMaxDepth()/getMaxDepth() --- .../internal/recursiveiteratoriterator.inc | 118 +++++++++++------- ext/spl/spl_iterators.c | 109 +++++++++++----- ext/spl/tests/iterator_028.phpt | 112 +++++++++++++++++ 3 files changed, 266 insertions(+), 73 deletions(-) create mode 100755 ext/spl/tests/iterator_028.phpt diff --git a/ext/spl/internal/recursiveiteratoriterator.inc b/ext/spl/internal/recursiveiteratoriterator.inc index 61a9d60a27..cc26e3ef49 100755 --- a/ext/spl/internal/recursiveiteratoriterator.inc +++ b/ext/spl/internal/recursiveiteratoriterator.inc @@ -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 diff --git a/ext/spl/spl_iterators.c b/ext/spl/spl_iterators.c index ef016961e1..87ad2a9964 100755 --- a/ext/spl/spl_iterators.c +++ b/ext/spl/spl_iterators.c @@ -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 index 0000000000..8b53b2123a --- /dev/null +++ b/ext/spl/tests/iterator_028.phpt @@ -0,0 +1,112 @@ +--TEST-- +SPL: RecursiveIteratorIterator and setMaxDepth() +--FILE-- +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=== + +--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=== -- 2.40.0