From 96916c95e889f2a1adb8da04da187e783da3fe27 Mon Sep 17 00:00:00 2001 From: Marcus Boerger Date: Sat, 20 May 2006 21:01:42 +0000 Subject: [PATCH] - MFH Sync with head fixes part of an issue with iterator_(to_array|count) --- ext/spl/spl_iterators.c | 141 +++++++++++++++++-------------- ext/spl/spl_iterators.h | 4 + ext/spl/tests/iterator_041.phpt | 119 ++++++++++++++++++++++++++ ext/spl/tests/iterator_041a.phpt | 109 ++++++++++++++++++++++++ ext/spl/tests/iterator_041b.phpt | 107 +++++++++++++++++++++++ 5 files changed, 417 insertions(+), 63 deletions(-) create mode 100755 ext/spl/tests/iterator_041.phpt create mode 100755 ext/spl/tests/iterator_041a.phpt create mode 100755 ext/spl/tests/iterator_041b.phpt diff --git a/ext/spl/spl_iterators.c b/ext/spl/spl_iterators.c index e76fc0c6ae..e2ec9f4e48 100755 --- a/ext/spl/spl_iterators.c +++ b/ext/spl/spl_iterators.c @@ -2372,63 +2372,100 @@ static zend_function_entry spl_funcs_AppendIterator[] = { {NULL, NULL, NULL} }; -/* {{{ proto array iterator_to_array(Traversable it) - Copy the iterator into an array */ -PHP_FUNCTION(iterator_to_array) +PHPAPI int spl_iterator_apply(zval *obj, spl_iterator_apply_func_t apply_func, void *puser TSRMLS_DC) { - zval *obj, **data; zend_object_iterator *iter; - char *str_key; - uint str_key_len; - ulong int_key; - int key_type; + zend_class_entry *ce = Z_OBJCE_P(obj); - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &obj, zend_ce_traversable) == FAILURE) { - RETURN_FALSE; + iter = ce->get_iterator(ce, obj, 0 TSRMLS_CC); + + if (EG(exception)) { + goto done; } - - array_init(return_value); - - iter = Z_OBJCE_P(obj)->get_iterator(Z_OBJCE_P(obj), obj, 0 TSRMLS_CC); if (iter->funcs->rewind) { iter->funcs->rewind(iter TSRMLS_CC); + if (EG(exception)) { + goto done; + } } - if (EG(exception)) { - return; - } + while (iter->funcs->valid(iter TSRMLS_CC) == SUCCESS) { - iter->funcs->get_current_data(iter, &data TSRMLS_CC); if (EG(exception)) { - return; + goto done; } - (*data)->refcount++; - if (iter->funcs->get_current_key) { - key_type = iter->funcs->get_current_key(iter, &str_key, &str_key_len, &int_key TSRMLS_CC); - if (EG(exception)) { - return; - } - switch(key_type) { - case HASH_KEY_IS_STRING: - add_assoc_zval_ex(return_value, str_key, str_key_len, *data); - efree(str_key); - break; - case HASH_KEY_IS_LONG: - add_index_zval(return_value, int_key, *data); - break; - } - } else { - add_next_index_zval(return_value, *data); + if (apply_func(iter, puser TSRMLS_CC) == ZEND_HASH_APPLY_STOP || EG(exception)) { + goto done; } iter->funcs->move_forward(iter TSRMLS_CC); if (EG(exception)) { - return; + goto done; } } + +done: iter->funcs->dtor(iter TSRMLS_CC); + return EG(exception) ? FAILURE : SUCCESS; +} +/* }}} */ + +static int spl_iterator_to_array_apply(zend_object_iterator *iter, void *puser TSRMLS_DC) /* {{{ */ +{ + zval **data, *return_value = (zval*)puser; + char *str_key; + uint str_key_len; + ulong int_key; + int key_type; + + iter->funcs->get_current_data(iter, &data TSRMLS_CC); if (EG(exception)) { - return; + return ZEND_HASH_APPLY_STOP; + } + if (iter->funcs->get_current_key) { + key_type = iter->funcs->get_current_key(iter, &str_key, &str_key_len, &int_key TSRMLS_CC); + if (EG(exception)) { + return ZEND_HASH_APPLY_STOP; + } + (*data)->refcount++; + switch(key_type) { + case HASH_KEY_IS_STRING: + add_assoc_zval_ex(return_value, str_key, str_key_len, *data); + efree(str_key); + break; + case HASH_KEY_IS_LONG: + add_index_zval(return_value, int_key, *data); + break; + } + } else { + (*data)->refcount++; + add_next_index_zval(return_value, *data); + } + return ZEND_HASH_APPLY_KEEP; +} +/* }}} */ + +/* {{{ proto array iterator_to_array(Traversable it) + Copy the iterator into an array */ +PHP_FUNCTION(iterator_to_array) +{ + zval *obj; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &obj, zend_ce_traversable) == FAILURE) { + RETURN_FALSE; } + + array_init(return_value); + + if (spl_iterator_apply(obj, spl_iterator_to_array_apply, (void*)return_value TSRMLS_CC) != SUCCESS) { + zval_dtor(return_value); + RETURN_NULL(); + } +} + +static int spl_iterator_count_apply(zend_object_iterator *iter, void *puser TSRMLS_DC) /* {{{ */ +{ + (*(long*)puser)++; + return ZEND_HASH_APPLY_KEEP; } /* }}} */ @@ -2437,37 +2474,15 @@ PHP_FUNCTION(iterator_to_array) PHP_FUNCTION(iterator_count) { zval *obj; - zend_object_iterator *iter; long count = 0; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &obj, zend_ce_traversable) == FAILURE) { RETURN_FALSE; } - iter = Z_OBJCE_P(obj)->get_iterator(Z_OBJCE_P(obj), obj, 0 TSRMLS_CC); - - if (iter->funcs->rewind) { - iter->funcs->rewind(iter TSRMLS_CC); + if (spl_iterator_apply(obj, spl_iterator_count_apply, (void*)&count TSRMLS_CC) == SUCCESS) { + RETURN_LONG(count); } - if (EG(exception)) { - return; - } - while (iter->funcs->valid(iter TSRMLS_CC) == SUCCESS) { - if (EG(exception)) { - return; - } - count++; - iter->funcs->move_forward(iter TSRMLS_CC); - if (EG(exception)) { - return; - } - } - iter->funcs->dtor(iter TSRMLS_CC); - if (EG(exception)) { - return; - } - - RETURN_LONG(count); } /* }}} */ @@ -2529,7 +2544,7 @@ PHP_MINIT_FUNCTION(spl_iterators) REGISTER_SPL_CLASS_CONST_LONG(CachingIterator, "CATCH_GET_CHILD", CIT_CATCH_GET_CHILD); REGISTER_SPL_CLASS_CONST_LONG(CachingIterator, "TOSTRING_USE_KEY", CIT_TOSTRING_USE_KEY); REGISTER_SPL_CLASS_CONST_LONG(CachingIterator, "TOSTRING_USE_CURRENT", CIT_TOSTRING_USE_CURRENT); - REGISTER_SPL_CLASS_CONST_LONG(CachingIterator, "TOSTRING_USE_INNER",CIT_TOSTRING_USE_INNER); + REGISTER_SPL_CLASS_CONST_LONG(CachingIterator, "TOSTRING_USE_INNER", CIT_TOSTRING_USE_INNER); REGISTER_SPL_CLASS_CONST_LONG(CachingIterator, "FULL_CACHE", CIT_FULL_CACHE); REGISTER_SPL_SUB_CLASS_EX(RecursiveCachingIterator, CachingIterator, spl_dual_it_new, spl_funcs_RecursiveCachingIterator); diff --git a/ext/spl/spl_iterators.h b/ext/spl/spl_iterators.h index 0154dba2b6..430fe7ec59 100755 --- a/ext/spl/spl_iterators.h +++ b/ext/spl/spl_iterators.h @@ -135,6 +135,10 @@ typedef struct _spl_dual_it_object { } u; } spl_dual_it_object; +typedef int (*spl_iterator_apply_func_t)(zend_object_iterator *iter, void *puser TSRMLS_DC); + +PHPAPI int spl_iterator_apply(zval *obj, spl_iterator_apply_func_t apply_func, void *puser TSRMLS_DC); + #endif /* SPL_ITERATORS_H */ /* diff --git a/ext/spl/tests/iterator_041.phpt b/ext/spl/tests/iterator_041.phpt new file mode 100755 index 0000000000..af42b1cdec --- /dev/null +++ b/ext/spl/tests/iterator_041.phpt @@ -0,0 +1,119 @@ +--TEST-- +SPL: iterator_to_array() and exceptions +--SKIPIF-- + +--FILE-- +state = MyArrayIterator::$fail; + self::fail(0, __FUNCTION__); + parent::__construct(array(1, 2)); + self::fail(1, __FUNCTION__); + } + + function rewind() + { + self::fail(2, __FUNCTION__); + return parent::rewind(); + } + + function valid() + { + self::fail(3, __FUNCTION__); + return parent::valid(); + } + + function current() + { + self::fail(4, __FUNCTION__); + return parent::current(); + } + + function key() + { + self::fail(5, __FUNCTION__); + return parent::key(); + } + + function next() + { + self::fail(6, __FUNCTION__); + return parent::next(); + } + + function __destruct() + { +// self::fail(7, __FUNCTION__); + } + + static function test($func, $skip = null) + { + echo "===$func===\n"; + self::$fail = 0; + while(self::$fail < 10) + { + try + { + var_dump($func(new MyArrayIterator())); + break; + } + catch (Exception $e) + { + echo $e->getMessage() . "\n"; + } + if (isset($skip[self::$fail])) + { + self::$fail = $skip[self::$fail]; + } + else + { + self::$fail++; + } + } + } +} + +MyArrayIterator::test('iterator_to_array'); +MyArrayIterator::test('iterator_count', array(3 => 6)); + +?> +===DONE=== + +--EXPECT-- +===iterator_to_array=== +State 0: __construct() +State 1: __construct() +State 2: rewind() +State 3: valid() +State 4: current() +State 5: key() +State 6: next() +array(2) { + [0]=> + int(1) + [1]=> + int(2) +} +===iterator_count=== +State 0: __construct() +State 1: __construct() +State 2: rewind() +State 3: valid() +State 6: next() +int(2) +===DONE=== diff --git a/ext/spl/tests/iterator_041a.phpt b/ext/spl/tests/iterator_041a.phpt new file mode 100755 index 0000000000..d03cbba9dc --- /dev/null +++ b/ext/spl/tests/iterator_041a.phpt @@ -0,0 +1,109 @@ +--TEST-- +SPL: iterator_to_array() and exceptions from destruct +--SKIPIF-- + +--FILE-- +state = MyArrayIterator::$fail; + self::fail(0, __FUNCTION__); + parent::__construct(array(1, 2)); + self::fail(1, __FUNCTION__); + } + + function rewind() + { + self::fail(2, __FUNCTION__); + return parent::rewind(); + } + + function valid() + { + self::fail(3, __FUNCTION__); + return parent::valid(); + } + + function current() + { + self::fail(4, __FUNCTION__); + return parent::current(); + } + + function key() + { + self::fail(5, __FUNCTION__); + return parent::key(); + } + + function next() + { + self::fail(6, __FUNCTION__); + return parent::next(); + } + + function __destruct() + { + self::fail(7, __FUNCTION__); + } + + static function test($func, $skip = null) + { + echo "===$func===\n"; + self::$fail = 7; + while(self::$fail < 10) + { + try + { + var_dump($func(new MyArrayIterator())); + break; + } + catch (Exception $e) + { + echo $e->getMessage() . "\n"; + } + if (isset($skip[self::$fail])) + { + self::$fail = $skip[self::$fail]; + } + else + { + self::$fail++; + } + } + } +} + +MyArrayIterator::test('iterator_to_array'); +MyArrayIterator::test('iterator_count', array(3 => 6)); + +?> +===DONE=== + +--EXPECT-- +===iterator_to_array=== +State 7: __destruct() +array(2) { + [0]=> + int(1) + [1]=> + int(2) +} +===iterator_count=== +State 7: __destruct() +int(2) +===DONE=== diff --git a/ext/spl/tests/iterator_041b.phpt b/ext/spl/tests/iterator_041b.phpt new file mode 100755 index 0000000000..3c999e8325 --- /dev/null +++ b/ext/spl/tests/iterator_041b.phpt @@ -0,0 +1,107 @@ +--TEST-- +SPL: iterator_to_array() and exceptions from delayed destruct +--SKIPIF-- + +--FILE-- +state = MyArrayIterator::$fail; + self::fail(0, __FUNCTION__); + parent::__construct(array(1, 2)); + self::fail(1, __FUNCTION__); + } + + function rewind() + { + self::fail(2, __FUNCTION__); + return parent::rewind(); + } + + function valid() + { + self::fail(3, __FUNCTION__); + return parent::valid(); + } + + function current() + { + self::fail(4, __FUNCTION__); + return parent::current(); + } + + function key() + { + self::fail(5, __FUNCTION__); + return parent::key(); + } + + function next() + { + self::fail(6, __FUNCTION__); + return parent::next(); + } + + function __destruct() + { + self::fail(7, __FUNCTION__); + } + + static function test($func, $skip = null) + { + echo "===$func===\n"; + self::$fail = 0; + while(self::$fail < 10) + { + try + { + var_dump($func(new MyArrayIterator())); + break; + } + catch (Exception $e) + { + echo $e->getMessage() . "\n"; + } + if (isset($skip[self::$fail])) + { + self::$fail = $skip[self::$fail]; + } + else + { + self::$fail++; + } + } + } +} + +MyArrayIterator::test('iterator_to_array'); +MyArrayIterator::test('iterator_count', array(3 => 6)); + +?> +===DONE=== + +--EXPECT-- +===iterator_to_array=== +State 0: __construct() +State 1: __construct() +State 2: rewind() +State 3: valid() +State 4: current() +State 5: key() +State 6: next() + +Fatal error: Ignoring exception from MyArrayIterator::__destruct() while an exception is already active (Uncaught Exception in /usr/src/php-cvs/ext/spl/tests/iterator_041b.phpt on line 17) in %siterator_041b.php on line %d -- 2.40.0