From: Marcus Boerger Date: Sat, 20 May 2006 13:23:00 +0000 (+0000) Subject: - Provide a generic c-level iterator apply function X-Git-Tag: BEFORE_NEW_OUTPUT_API~148 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=05de29995fc6de7eeb7a0575a6fec951661745c3;p=php - Provide a generic c-level iterator apply function - Base iterator_to_array() and iterator_count() on it - Add a testcase # Somehow there is an issue with exceptions in __destruct() here --- diff --git a/ext/spl/spl_iterators.c b/ext/spl/spl_iterators.c index fda48fb68f..8d05806df2 100755 --- a/ext/spl/spl_iterators.c +++ b/ext/spl/spl_iterators.c @@ -2401,67 +2401,107 @@ 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; - zstr str_key; - uint str_key_len; - ulong int_key; - int key_type; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &obj, zend_ce_traversable) == FAILURE) { - RETURN_FALSE; - } - - array_init(return_value); - + obj->refcount++; iter = Z_OBJCE_P(obj)->get_iterator(Z_OBJCE_P(obj), obj, 0 TSRMLS_CC); + if (EG(exception)) { + goto done; + } + 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.s, str_key_len, *data); - efree(str_key.s); - break; - case HASH_KEY_IS_UNICODE: - add_u_assoc_zval_ex(return_value, IS_UNICODE, str_key, str_key_len, *data); - efree(str_key.u); - 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); + if (obj->refcount > 0 && !EG(exception)) { + zval_ptr_dtor(&obj); + } + 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; + zstr 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.s, str_key_len, *data); + efree(str_key.s); + break; + case HASH_KEY_IS_UNICODE: + add_u_assoc_zval_ex(return_value, IS_UNICODE, str_key, str_key_len, *data); + efree(str_key.u); + 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; } /* }}} */ @@ -2470,37 +2510,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 (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; + if (spl_iterator_apply(obj, spl_iterator_count_apply, (void*)&count TSRMLS_CC) == SUCCESS) { + RETURN_LONG(count); } - - RETURN_LONG(count); } /* }}} */ @@ -2562,7 +2580,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 7d6b63e103..ed74244e4f 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..31fdd5a570 --- /dev/null +++ b/ext/spl/tests/iterator_041.phpt @@ -0,0 +1,148 @@ +--TEST-- +SPL: iterator_to_array() and exceptions +--SKIPIF-- + +--FILE-- +getMessage()); + } + self::$fail++; + } + + self::$fail = 0; + while(self::$fail < 10) + { + echo '===' . self::$fail . "===\n"; + try + { + var_dump(iterator_count(new MyArrayIterator())); + break; + } + catch (Exception $e) + { + var_dump($e->getMessage()); + } + if (self::$fail == 3) + { + self::$fail = 6; + } + else + { + self::$fail++; + } + } + } +} + +MyArrayIterator::test(); + +?> +===DONE=== + +--EXPECT-- +===0=== +string(22) "State 0: __construct()" +===1=== +string(22) "State 1: __construct()" +===2=== +string(17) "State 2: rewind()" +===3=== +string(16) "State 3: valid()" +===4=== +string(14) "State 4: key()" +===5=== +string(18) "State 5: current()" +===6=== +string(15) "State 6: next()" +===7=== +string(21) "State 7: __destruct()" +===8=== +array(2) { + [0]=> + int(1) + [1]=> + int(2) +} +===0=== +string(22) "State 0: __construct()" +===1=== +string(22) "State 1: __construct()" +===2=== +string(17) "State 2: rewind()" +===3=== +string(16) "State 3: valid()" +===4=== +string(15) "State 6: next()" +===7=== +string(21) "State 7: __destruct()" +===8=== +int(2) +===DONE===