]> granicus.if.org Git - php/commitdiff
- Provide a generic c-level iterator apply function
authorMarcus Boerger <helly@php.net>
Sat, 20 May 2006 13:23:00 +0000 (13:23 +0000)
committerMarcus Boerger <helly@php.net>
Sat, 20 May 2006 13:23:00 +0000 (13:23 +0000)
- Base iterator_to_array() and iterator_count() on it
- Add a testcase
# Somehow there is an issue with exceptions in __destruct() here

ext/spl/spl_iterators.c
ext/spl/spl_iterators.h
ext/spl/tests/iterator_041.phpt [new file with mode: 0755]

index fda48fb68ffdf4f97f4d93e2f131e0cb44d4ef9b..8d05806df28a2ae2d6fd612fcb1b7f83c2afcb65 100755 (executable)
@@ -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);
index 7d6b63e1038d9a88e0d52ba9ec95c2f11021aeaf..ed74244e4fcef22b4a2296d5432891a5afb89f1a 100755 (executable)
@@ -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 (executable)
index 0000000..31fdd5a
--- /dev/null
@@ -0,0 +1,148 @@
+--TEST--
+SPL: iterator_to_array() and exceptions
+--SKIPIF--
+<?php if (!extension_loaded("spl")) print "skip"; ?>
+--FILE--
+<?php
+
+class MyArrayIterator extends ArrayIterator
+{
+       static protected $fail = 0;
+
+       static function fail($state, $method)
+       {
+               if (self::$fail == $state)
+               {
+                       throw new Exception("State $state: $method()");
+               }
+       }
+
+       function __construct()
+       {
+               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 key()
+       {
+               self::fail(4, __FUNCTION__);
+               return parent::key();
+       }
+
+       function current()
+       {
+               self::fail(5, __FUNCTION__);
+               return parent::current();
+       }
+
+       function next()
+       {
+               self::fail(6, __FUNCTION__);
+               return parent::next();
+       }
+
+       function __destruct()
+       {
+               self::fail(7, __FUNCTION__);
+       }
+
+       static function test()
+       {
+               self::$fail = 0;
+               while(self::$fail < 10)
+               {
+                       echo '===' . self::$fail . "===\n";
+                       try
+                       {
+                               var_dump(iterator_to_array(new MyArrayIterator()));
+                               break;
+                       }
+                       catch (Exception $e)
+                       {
+                               var_dump($e->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===
+<?php exit(0); ?>
+--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===