]> granicus.if.org Git - php/commitdiff
- MFH Sync with head fixes part of an issue with iterator_(to_array|count)
authorMarcus Boerger <helly@php.net>
Sat, 20 May 2006 21:01:42 +0000 (21:01 +0000)
committerMarcus Boerger <helly@php.net>
Sat, 20 May 2006 21:01:42 +0000 (21:01 +0000)
ext/spl/spl_iterators.c
ext/spl/spl_iterators.h
ext/spl/tests/iterator_041.phpt [new file with mode: 0755]
ext/spl/tests/iterator_041a.phpt [new file with mode: 0755]
ext/spl/tests/iterator_041b.phpt [new file with mode: 0755]

index e76fc0c6aef50bdefc71d4346e0be34599c4bcfb..e2ec9f4e48f1829842393a8c045982dd3c92d02a 100755 (executable)
@@ -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);
index 0154dba2b611a16601a04b7b9266cbd1f698eb05..430fe7ec591a976041290876c6bb7ea0ede59786 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..af42b1c
--- /dev/null
@@ -0,0 +1,119 @@
+--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;
+       public $state;
+
+       static function fail($state, $method)
+       {
+               if (self::$fail == $state)
+               {
+                       throw new Exception("State $state: $method()");
+               }
+       }
+
+       function __construct()
+       {
+               $this->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===
+<?php exit(0); ?>
+--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 (executable)
index 0000000..d03cbba
--- /dev/null
@@ -0,0 +1,109 @@
+--TEST--
+SPL: iterator_to_array() and exceptions from destruct
+--SKIPIF--
+<?php if (!extension_loaded("spl")) print "skip"; ?>
+--FILE--
+<?php
+
+class MyArrayIterator extends ArrayIterator
+{
+       static protected $fail = 0;
+       public $state;
+
+       static function fail($state, $method)
+       {
+               if (self::$fail == $state)
+               {
+                       throw new Exception("State $state: $method()");
+               }
+       }
+
+       function __construct()
+       {
+               $this->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===
+<?php exit(0); ?>
+--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 (executable)
index 0000000..3c999e8
--- /dev/null
@@ -0,0 +1,107 @@
+--TEST--
+SPL: iterator_to_array() and exceptions from delayed destruct
+--SKIPIF--
+<?php if (!extension_loaded("spl")) print "skip"; ?>
+--FILE--
+<?php
+
+class MyArrayIterator extends ArrayIterator
+{
+       static protected $fail = 0;
+       public $state;
+
+       static function fail($state, $method)
+       {
+               if (self::$fail == $state)
+               {
+                       throw new Exception("State $state: $method()");
+               }
+       }
+
+       function __construct()
+       {
+               $this->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===
+<?php exit(0); ?>
+--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