- Allow to hook into RecursiveIteratorIterator's child handling

- Better=faster function caching for RecursiveIteratorIterator
- Check for exceptions and fix check for illegal return values
- Add UnexpectedValueException
- Add docu
This commit is contained in:
Marcus Boerger 2005-03-12 23:03:33 +00:00
parent 6dc04d98f2
commit f9de3fd02b
6 changed files with 473 additions and 13 deletions

View file

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

View file

@ -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;
}

View file

@ -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);

View file

@ -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}
};

185
ext/spl/tests/iterator_021.phpt Executable file
View file

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

191
ext/spl/tests/iterator_022.phpt Executable file
View file

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