*
* This kind of exception should be used to inform about domain erors in
* mathematical sense.
+ *
+ * @see RangeException
*/
class DomainException extends LogicException
{
/** @ingroup SPL
* @brief Exception that denotes invalid arguments were passed.
* @since PHP 5.1
+ *
+ * @see UnexpectedValueException
*/
class InvalidArgumentException extends LogicException
{
* @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
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()
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;
}
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);
spl_sub_iterator *iterators;
int level;
RecursiveIteratorMode mode;
+ zend_function *callHasChildren;
+ zend_function *callGetChildren;
zend_function *beginChildren;
zend_function *endChildren;
zend_class_entry *ce;
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);
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) {
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;
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 {
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++;
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)
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}
};
--- /dev/null
+--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===
--- /dev/null
+--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===