- Implement RecursiveIteratorIterator::setMaxDepth()/getMaxDepth()

This commit is contained in:
Marcus Boerger 2005-09-25 12:01:31 +00:00
parent 7ae48d8182
commit 7e1763a115
3 changed files with 267 additions and 74 deletions

View File

@ -12,7 +12,7 @@
/** /**
* @brief Iterates through recursive iterators * @brief Iterates through recursive iterators
* @author Marcus Boerger * @author Marcus Boerger
* @version 1.2 * @version 1.3
* @since PHP 5.0 * @since PHP 5.0
* *
* The objects of this class are created by instances of RecursiveIterator. * The objects of this class are created by instances of RecursiveIterator.
@ -33,9 +33,10 @@ class RecursiveIteratorIterator implements OuterIterator
const CATCH_GET_CHILD = 2; const CATCH_GET_CHILD = 2;
private $ait = array(); private $ait = array();
private $count = 0; private $level = 0;
private $mode = self::LEAVES_ONLY; private $mode = self::LEAVES_ONLY;
private $flags = 0; private $flags = 0;
private $max_depth = -1;
/** Construct from RecursiveIterator /** Construct from RecursiveIterator
* *
@ -61,8 +62,8 @@ class RecursiveIteratorIterator implements OuterIterator
*/ */
function rewind() function rewind()
{ {
while ($this->count) { while ($this->level) {
unset($this->ait[$this->count--]); unset($this->ait[$this->level--]);
$this->endChildren(); $this->endChildren();
} }
$this->ait[0]->rewind(); $this->ait[0]->rewind();
@ -74,13 +75,13 @@ class RecursiveIteratorIterator implements OuterIterator
*/ */
function valid() function valid()
{ {
$count = $this->count; $level = $this->level;
while ($count) { while ($level >= 0) {
$it = $this->ait[$count]; $it = $this->ait[$level];
if ($it->valid()) { if ($it->valid()) {
return true; return true;
} }
$count--; $level--;
$this->endChildren(); $this->endChildren();
} }
return false; return false;
@ -90,7 +91,7 @@ class RecursiveIteratorIterator implements OuterIterator
*/ */
function key() function key()
{ {
$it = $this->ait[$this->count]; $it = $this->ait[$this->level];
return $it->key(); return $it->key();
} }
@ -98,7 +99,7 @@ class RecursiveIteratorIterator implements OuterIterator
*/ */
function current() function current()
{ {
$it = $this->ait[$this->count]; $it = $this->ait[$this->level];
return $it->current(); return $it->current();
} }
@ -106,46 +107,61 @@ class RecursiveIteratorIterator implements OuterIterator
*/ */
function next() function next()
{ {
while ($this->count) { while ($this->level >= 0) {
$it = $this->ait[$this->count]; $it = $this->ait[$this->level];
if ($it->valid()) { if ($it->valid()) {
if (!$it->recursed && callHasChildren()) { if (!$it->recursed && callHasChildren()) {
$it->recursed = true; if ($this->max_depth == -1 || $this->max_depth > $this->level) {
try $it->recursed = true;
{ try
$sub = callGetChildren();
}
catch (Exception $e)
{
if (!($this->flags & self::CATCH_GET_CHILD))
{ {
throw $e; $sub = callGetChildren();
} }
$it->next(); catch (Exception $e)
continue; {
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);
} }
$sub->recursed = false; else
$sub->rewind(); {
if ($sub->valid()) { /* do not recurse because of depth restriction */
$this->ait[++$this->count] = $sub; if ($this->flages & self::LEVAES_ONLY)
if (!$sub instanceof RecursiveIterator) { {
throw new Exception(get_class($sub).'::getChildren() must return an object that implements RecursiveIterator'); $it->next();
continue;
} }
$this->beginChildren(); else
{
return; // we want the parent
}
}
$it->next();
$it->recursed = false;
if ($it->valid()) {
return; return;
} }
unset($sub); $it->recursed = false;
} }
$it->next();
$it->recursed = false;
if ($it->valid()) {
return;
}
$it->recursed = false;
} }
if ($this->count) { else if ($this->level > 0) {
unset($this->ait[$this->count--]); unset($this->ait[$this->level--]);
$it = $this->ait[$this->count]; $it = $this->ait[$this->level];
$this->endChildren(); $this->endChildren();
callNextElement(false); callNextElement(false);
} }
@ -159,7 +175,7 @@ class RecursiveIteratorIterator implements OuterIterator
function getSubIterator($level = NULL) function getSubIterator($level = NULL)
{ {
if (is_null($level)) { if (is_null($level)) {
$level = $this->count; $level = $this->level;
} }
return @$this->ait[$level]; return @$this->ait[$level];
} }
@ -184,7 +200,7 @@ class RecursiveIteratorIterator implements OuterIterator
*/ */
function callHasChildren() function callHasChildren()
{ {
return $this->ait[$this->count]->hasChildren(); return $this->ait[$this->level]->hasChildren();
} }
/** @return current sub iterators current children /** @return current sub iterators current children
@ -192,7 +208,7 @@ class RecursiveIteratorIterator implements OuterIterator
*/ */
function callGetChildren() function callGetChildren()
{ {
return $this->ait[$this->count]->getChildren(); return $this->ait[$this->level]->getChildren();
} }
/** Called right after calling getChildren() and its rewind(). /** Called right after calling getChildren() and its rewind().
@ -230,6 +246,24 @@ class RecursiveIteratorIterator implements OuterIterator
/** Called when the next element is available /** Called when the next element is available
*/ */
function nextElement(); 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;
}
} }
?> ?>

View File

@ -89,6 +89,7 @@ typedef struct _spl_recursive_it_object {
int level; int level;
RecursiveIteratorMode mode; RecursiveIteratorMode mode;
int flags; int flags;
int max_depth;
zend_bool in_iteration; zend_bool in_iteration;
zend_function *beginIteration; zend_function *beginIteration;
zend_function *endIteration; 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); sub_iter->funcs->dtor(sub_iter TSRMLS_CC);
zval_ptr_dtor(&object->iterators[object->level--].zobject); 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; object->level = 0;
zval_ptr_dtor(&iter->zobject); zval_ptr_dtor(&iter->zobject);
@ -206,14 +207,23 @@ next_step:
has_children = zend_is_true(retval); has_children = zend_is_true(retval);
zval_ptr_dtor(&retval); zval_ptr_dtor(&retval);
if (has_children) { if (has_children) {
switch (object->mode) { if (object->max_depth == -1 || object->max_depth > object->level) {
case RIT_LEAVES_ONLY: switch (object->mode) {
case RIT_CHILD_FIRST: case RIT_LEAVES_ONLY:
object->iterators[object->level].state = RS_CHILD; case RIT_CHILD_FIRST:
goto next_step; object->iterators[object->level].state = RS_CHILD;
case RIT_SELF_FIRST: goto next_step;
object->iterators[object->level].state = RS_SELF; case RIT_SELF_FIRST:
goto next_step; 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->level = 0;
intern->mode = mode; intern->mode = mode;
intern->flags = flags; intern->flags = flags;
intern->max_depth = -1;
intern->in_iteration = 0; intern->in_iteration = 0;
intern->ce = Z_OBJCE_P(object); intern->ce = Z_OBJCE_P(object);
zend_hash_find(&intern->ce->function_table, "beginiteration", sizeof("beginiteration"), (void **) &intern->beginIteration); zend_hash_find(&intern->ce->function_table, "beginiteration", sizeof("beginiteration"), (void **) &intern->beginIteration);
if (intern->beginIteration->common.scope == U_CLASS_ENTRY(spl_ce_RecursiveIteratorIterator)) { if (intern->beginIteration->common.scope == U_CLASS_ENTRY(spl_ce_RecursiveIteratorIterator)) {
intern->beginIteration = NULL; intern->beginIteration = NULL;
@ -588,6 +600,36 @@ SPL_METHOD(RecursiveIteratorIterator, nextElement)
/* nothing to do */ /* 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) static union _zend_function *spl_recursive_it_get_method(zval **object_ptr, char *method, int method_len TSRMLS_DC)
{ {
union _zend_function *function_handler; 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); zval_ptr_dtor(&object->iterators[object->level--].zobject);
} }
efree(object->iterators); efree(object->iterators);
object->iterators = NULL;
} }
zend_hash_destroy(object->std.properties); 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_ARG_INFO(0, level)
ZEND_END_ARG_INFO(); 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[] = { static zend_function_entry spl_funcs_RecursiveIteratorIterator[] = {
SPL_ME(RecursiveIteratorIterator, __construct, arginfo_recursive_it___construct, 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, rewind, NULL, ZEND_ACC_PUBLIC)
SPL_ME(RecursiveIteratorIterator, valid, NULL, ZEND_ACC_PUBLIC) SPL_ME(RecursiveIteratorIterator, valid, NULL, ZEND_ACC_PUBLIC)
SPL_ME(RecursiveIteratorIterator, key, NULL, ZEND_ACC_PUBLIC) SPL_ME(RecursiveIteratorIterator, key, NULL, ZEND_ACC_PUBLIC)
SPL_ME(RecursiveIteratorIterator, current, NULL, ZEND_ACC_PUBLIC) SPL_ME(RecursiveIteratorIterator, current, NULL, ZEND_ACC_PUBLIC)
SPL_ME(RecursiveIteratorIterator, next, NULL, ZEND_ACC_PUBLIC) SPL_ME(RecursiveIteratorIterator, next, NULL, ZEND_ACC_PUBLIC)
SPL_ME(RecursiveIteratorIterator, getDepth, 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, getSubIterator, arginfo_recursive_it_getSubIterator, ZEND_ACC_PUBLIC)
SPL_ME(RecursiveIteratorIterator, getInnerIterator, NULL, ZEND_ACC_PUBLIC) SPL_ME(RecursiveIteratorIterator, getInnerIterator, NULL, ZEND_ACC_PUBLIC)
SPL_ME(RecursiveIteratorIterator, beginIteration, NULL, ZEND_ACC_PUBLIC) SPL_ME(RecursiveIteratorIterator, beginIteration, NULL, ZEND_ACC_PUBLIC)
SPL_ME(RecursiveIteratorIterator, endIteration, NULL, ZEND_ACC_PUBLIC) SPL_ME(RecursiveIteratorIterator, endIteration, NULL, ZEND_ACC_PUBLIC)
SPL_ME(RecursiveIteratorIterator, callHasChildren, NULL, ZEND_ACC_PUBLIC) SPL_ME(RecursiveIteratorIterator, callHasChildren, NULL, ZEND_ACC_PUBLIC)
SPL_ME(RecursiveIteratorIterator, callGetChildren, NULL, ZEND_ACC_PUBLIC) SPL_ME(RecursiveIteratorIterator, callGetChildren, NULL, ZEND_ACC_PUBLIC)
SPL_ME(RecursiveIteratorIterator, beginChildren, NULL, ZEND_ACC_PUBLIC) SPL_ME(RecursiveIteratorIterator, beginChildren, NULL, ZEND_ACC_PUBLIC)
SPL_ME(RecursiveIteratorIterator, endChildren, NULL, ZEND_ACC_PUBLIC) SPL_ME(RecursiveIteratorIterator, endChildren, NULL, ZEND_ACC_PUBLIC)
SPL_ME(RecursiveIteratorIterator, nextElement, 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} {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); MAKE_STD_ZVAL(zcacheval);
ZVAL_ZVAL(zcacheval, intern->current.data, 1, 0); ZVAL_ZVAL(zcacheval, intern->current.data, 1, 0);
switch(intern->current.key_type) { if (intern->current.key_type == HASH_KEY_IS_LONG) {
case HASH_KEY_IS_STRING: add_index_zval(intern->u.caching.zcache, intern->current.int_key, zcacheval);
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); } else {
break; 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);
case HASH_KEY_IS_LONG:
add_index_zval(intern->u.caching.zcache, intern->current.int_key, zcacheval);
break;
} }
} }
/* Recursion ? */ /* Recursion ? */

112
ext/spl/tests/iterator_028.phpt Executable file
View File

@ -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===