From: Marcus Boerger Date: Wed, 27 Jul 2005 22:19:01 +0000 (+0000) Subject: - Fix issues with iterators and excpetions X-Git-Tag: RELEASE_2_0_0~92 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=a935b062367008784d385eb5616c3cbf5528ce43;p=php - Fix issues with iterators and excpetions # update documentation # # In 5.2 we need to implement an event handler onException() to be invoked # on exceptions during getChildren() calls. Its default implementation # would simply rethrow the exception if the flag is not set and delete if # if it was set. To do so the exceptions refcount needs to be increased # before calling zend_clear_exception() to keep the exception alive but # clear the control information. # # As a side note this is alos the easy solution to allow multi exception # handling: Simply clear the engine's exception info and add a property # called $previousException to the base exception and assign it from the # already pending one. --- diff --git a/ext/spl/internal/recursiveiteratoriterator.inc b/ext/spl/internal/recursiveiteratoriterator.inc index 0e76de21dd..66cffc4875 100755 --- a/ext/spl/internal/recursiveiteratoriterator.inc +++ b/ext/spl/internal/recursiveiteratoriterator.inc @@ -12,6 +12,7 @@ define('RIT_LEAVES_ONLY', 0); define('RIT_SELF_FIRST', 1); define('RIT_CHILD_FIRST', 2); +define('RIT_CATCH_GET_CHILD', 256); /** * @brief Iterates through recursive iterators @@ -27,18 +28,26 @@ class RecursiveIteratorIterator implements OuterIterator { private $ait = array(); private $count = 0; + private $mode = RIT_LEAVES_ONLY; + private $flags = 0; /** Construct from RecursiveIterator * * @param it RecursiveIterator to iterate - * @param flags Operation mode: + * @param flags Operation mode (one of): * - RIT_LEAVES_ONLY only show leaves * - RIT_SELF_FIRST show parents prior to their childs * - RIT_CHILD_FIRST show all childs prior to their parent + * or'ed with the following flags: + * - RIT_CATCH_GET_CHILD which catches exceptions during + * getChildren() calls and simply jumps to the next + * element. */ function __construct(RecursiveIterator $it, $flags) { $this->ait[0] = $it; + $this->mode = $flags & 0xFF; + $this->flags = $flags & ~0xFF; } /** Rewind to top iterator as set in constructor @@ -94,7 +103,19 @@ class RecursiveIteratorIterator implements OuterIterator if ($it->valid()) { if (!$it->recursed && callHasChildren()) { $it->recursed = true; - $sub = callGetChildren(); + try + { + $sub = callGetChildren(); + } + catch (Exception $e) + { + if (!($this->flags & RIT_CATCH_GET_CHILD)) + { + throw $e; + } + $it->next(); + continue; + } $sub->recursed = false; $sub->rewind(); if ($sub->valid()) { diff --git a/ext/spl/spl_iterators.c b/ext/spl/spl_iterators.c index 71ad2fc9ee..2924b90966 100755 --- a/ext/spl/spl_iterators.c +++ b/ext/spl/spl_iterators.c @@ -65,6 +65,9 @@ typedef enum { RIT_CHILD_FIRST = 2 } RecursiveIteratorMode; +#define RIT_MODE_MASK 0x000000FF +#define RIT_CATCH_GET_CHILD 0x00000100 + typedef enum { RS_NEXT = 0, RS_TEST = 1, @@ -85,6 +88,7 @@ typedef struct _spl_recursive_it_object { spl_sub_iterator *iterators; int level; RecursiveIteratorMode mode; + int flags; zend_function *callHasChildren; zend_function *callGetChildren; zend_function *beginChildren; @@ -222,6 +226,20 @@ next_step: } else { zend_call_method_with_0_params(&zobject, ce, NULL, "getchildren", &child); } + + if (EG(exception)) { + if (!(object->flags & RIT_CATCH_GET_CHILD)) { + return; + } else { + zend_clear_exception(TSRMLS_C); + if (child) { + zval_ptr_dtor(&child); + } + object->iterators[object->level].state = RS_NEXT; + goto next_step; + } + } + 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) { @@ -344,7 +362,8 @@ SPL_METHOD(RecursiveIteratorIterator, __construct) intern = (spl_recursive_it_object*)zend_object_store_get_object(object TSRMLS_CC); intern->iterators = emalloc(sizeof(spl_sub_iterator)); intern->level = 0; - intern->mode = mode; + intern->mode = mode & RIT_MODE_MASK; + intern->flags = mode & ~RIT_MODE_MASK; 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) { @@ -1308,8 +1327,7 @@ static INLINE void spl_caching_it_next(spl_dual_it_object *intern TSRMLS_DC) if (zend_is_true(retval)) { zend_call_method_with_0_params(&intern->inner.zobject, intern->inner.ce, NULL, "getchildren", &zchildren); if (EG(exception) && intern->u.caching.flags & CIT_CATCH_GET_CHILD) { - zval_ptr_dtor(&EG(exception)); - EG(exception) = NULL; + zend_clear_exception(TSRMLS_C); if (zchildren) { zval_ptr_dtor(&zchildren); } @@ -1914,9 +1932,10 @@ PHP_MINIT_FUNCTION(spl_iterators) spl_ce_RecursiveIteratorIterator->get_iterator = spl_recursive_it_get_iterator; spl_ce_RecursiveIteratorIterator->iterator_funcs.funcs = &spl_recursive_it_iterator_funcs; - REGISTER_LONG_CONSTANT("RIT_LEAVES_ONLY", (long)RIT_LEAVES_ONLY, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("RIT_SELF_FIRST", (long)RIT_SELF_FIRST, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("RIT_CHILD_FIRST", (long)RIT_CHILD_FIRST, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("RIT_LEAVES_ONLY", (long)RIT_LEAVES_ONLY, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("RIT_SELF_FIRST", (long)RIT_SELF_FIRST, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("RIT_CHILD_FIRST", (long)RIT_CHILD_FIRST, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("RIT_CATCH_GET_CHILD", (long)RIT_CATCH_GET_CHILD, CONST_CS | CONST_PERSISTENT); REGISTER_SPL_STD_CLASS_EX(FilterIterator, spl_dual_it_new, spl_funcs_FilterIterator); REGISTER_SPL_ITERATOR(FilterIterator);