mirror of
https://github.com/php/php-src.git
synced 2024-09-23 19:07:26 +00:00
- Implement RecursiveIteratorIterator::setMaxDepth()/getMaxDepth()
This commit is contained in:
parent
7ae48d8182
commit
7e1763a115
@ -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,10 +107,11 @@ 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()) {
|
||||
if ($this->max_depth == -1 || $this->max_depth > $this->level) {
|
||||
$it->recursed = true;
|
||||
try
|
||||
{
|
||||
@ -127,7 +129,7 @@ class RecursiveIteratorIterator implements OuterIterator
|
||||
$sub->recursed = false;
|
||||
$sub->rewind();
|
||||
if ($sub->valid()) {
|
||||
$this->ait[++$this->count] = $sub;
|
||||
$this->ait[++$this->level] = $sub;
|
||||
if (!$sub instanceof RecursiveIterator) {
|
||||
throw new Exception(get_class($sub).'::getChildren() must return an object that implements RecursiveIterator');
|
||||
}
|
||||
@ -136,6 +138,19 @@ class RecursiveIteratorIterator implements OuterIterator
|
||||
}
|
||||
unset($sub);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* do not recurse because of depth restriction */
|
||||
if ($this->flages & self::LEVAES_ONLY)
|
||||
{
|
||||
$it->next();
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
return; // we want the parent
|
||||
}
|
||||
}
|
||||
$it->next();
|
||||
$it->recursed = false;
|
||||
if ($it->valid()) {
|
||||
@ -143,9 +158,10 @@ class RecursiveIteratorIterator implements OuterIterator
|
||||
}
|
||||
$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;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
@ -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,6 +207,7 @@ next_step:
|
||||
has_children = zend_is_true(retval);
|
||||
zval_ptr_dtor(&retval);
|
||||
if (has_children) {
|
||||
if (object->max_depth == -1 || object->max_depth > object->level) {
|
||||
switch (object->mode) {
|
||||
case RIT_LEAVES_ONLY:
|
||||
case RIT_CHILD_FIRST:
|
||||
@ -215,6 +217,14 @@ 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (object->nextElement) {
|
||||
@ -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,6 +704,11 @@ 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)
|
||||
@ -678,6 +726,8 @@ static zend_function_entry spl_funcs_RecursiveIteratorIterator[] = {
|
||||
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:
|
||||
if (intern->current.key_type == HASH_KEY_IS_LONG) {
|
||||
add_index_zval(intern->u.caching.zcache, intern->current.int_key, zcacheval);
|
||||
break;
|
||||
} 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 ? */
|
||||
|
112
ext/spl/tests/iterator_028.phpt
Executable file
112
ext/spl/tests/iterator_028.phpt
Executable 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===
|
Loading…
Reference in New Issue
Block a user