diff options
author | Marcus Boerger <helly@php.net> | 2003-10-22 20:04:48 +0000 |
---|---|---|
committer | Marcus Boerger <helly@php.net> | 2003-10-22 20:04:48 +0000 |
commit | 8abb3bd4489afd8c47fc8a2d0d741206e322af33 (patch) | |
tree | 1086723deac5c116a4a81a74ee1275ff19c65b13 | |
parent | 3c62b3b5ac8ab53b4133281e389bf990d9a30ead (diff) | |
download | php-git-8abb3bd4489afd8c47fc8a2d0d741206e322af33.tar.gz |
Impement userspace iterator interfaces and tests. See tests for details
on the names.
-rw-r--r-- | Zend/zend_default_classes.c | 2 | ||||
-rw-r--r-- | Zend/zend_exceptions.c | 2 | ||||
-rwxr-xr-x | Zend/zend_interfaces.c | 381 | ||||
-rwxr-xr-x | Zend/zend_interfaces.h | 43 | ||||
-rwxr-xr-x | tests/classes/iterators_001.phpt | 216 | ||||
-rwxr-xr-x | tests/classes/iterators_002.phpt | 120 | ||||
-rwxr-xr-x | tests/classes/iterators_003.phpt | 119 | ||||
-rwxr-xr-x | tests/classes/iterators_004.phpt | 62 | ||||
-rwxr-xr-x | tests/classes/iterators_005.phpt | 20 |
9 files changed, 965 insertions, 0 deletions
diff --git a/Zend/zend_default_classes.c b/Zend/zend_default_classes.c index 55a046e7a7..619e9ec866 100644 --- a/Zend/zend_default_classes.c +++ b/Zend/zend_default_classes.c @@ -23,6 +23,7 @@ #include "zend_API.h" #include "zend_reflection_api.h" #include "zend_builtin_functions.h" +#include "zend_interfaces.h" static zend_class_entry *default_exception_ptr; static zend_object_handlers default_exception_handlers; @@ -504,6 +505,7 @@ ZEND_API void zend_exception_error(zval *exception TSRMLS_DC) ZEND_API void zend_register_default_classes(TSRMLS_D) { + zend_register_interfaces(TSRMLS_C); zend_register_default_exception(TSRMLS_C); zend_register_reflection_api(TSRMLS_C); zend_register_iterator_wrapper(TSRMLS_C); diff --git a/Zend/zend_exceptions.c b/Zend/zend_exceptions.c index 55a046e7a7..619e9ec866 100644 --- a/Zend/zend_exceptions.c +++ b/Zend/zend_exceptions.c @@ -23,6 +23,7 @@ #include "zend_API.h" #include "zend_reflection_api.h" #include "zend_builtin_functions.h" +#include "zend_interfaces.h" static zend_class_entry *default_exception_ptr; static zend_object_handlers default_exception_handlers; @@ -504,6 +505,7 @@ ZEND_API void zend_exception_error(zval *exception TSRMLS_DC) ZEND_API void zend_register_default_classes(TSRMLS_D) { + zend_register_interfaces(TSRMLS_C); zend_register_default_exception(TSRMLS_C); zend_register_reflection_api(TSRMLS_C); zend_register_iterator_wrapper(TSRMLS_C); diff --git a/Zend/zend_interfaces.c b/Zend/zend_interfaces.c new file mode 100755 index 0000000000..04cc74ea41 --- /dev/null +++ b/Zend/zend_interfaces.c @@ -0,0 +1,381 @@ +/* + +----------------------------------------------------------------------+ + | Zend Engine | + +----------------------------------------------------------------------+ + | Copyright (c) 1998-2003 Zend Technologies Ltd. (http://www.zend.com) | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.00 of the Zend license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.zend.com/license/2_00.txt. | + | If you did not receive a copy of the Zend license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@zend.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Marcus Boerger <helly@php.net> | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#include "zend.h" +#include "zend_API.h" +#include "zend_interfaces.h" + +zend_class_entry *zend_ce_traversable; +zend_class_entry *zend_ce_aggregate; +zend_class_entry *zend_ce_iterator; + +/* {{{ zend_call_method + Only returns the returned zval if retval_ptr != NULL */ +ZEND_API zval* zend_call_method(zval **object_pp, zend_class_entry *obj_ce, zend_function **fn_proxy, char *function_name, int function_name_len, zval **retval_ptr_ptr, int param_count, zval* arg1, zval* arg2 TSRMLS_DC) +{ + int result; + zend_fcall_info fci; + zval z_fname; + zval *retval; + + zval **params[2]; + + params[0] = &arg1; + params[1] = &arg2; + + fci.size = sizeof(fci); + /*fci.function_table = NULL; will be read form zend_class_entry of object if needed */ + fci.object_pp = object_pp; + fci.function_name = &z_fname; + fci.retval_ptr_ptr = retval_ptr_ptr ? retval_ptr_ptr : &retval; + fci.param_count = param_count; + fci.params = params; + fci.no_separation = 1; + fci.symbol_table = NULL; + + if (!fn_proxy && !obj_ce) { + /* no interest in caching and no information already present that is + * needed later inside zend_call_function. */ + ZVAL_STRINGL(&z_fname, function_name, function_name_len, 0); + result = zend_call_function(&fci, NULL TSRMLS_CC); + } else { + zend_fcall_info_cache fcic; + + fcic.initialized = 1; + if (!obj_ce) { + obj_ce = Z_OBJCE_PP(object_pp); + } + if (!fn_proxy || !*fn_proxy) { + if (zend_hash_find(&obj_ce->function_table, function_name, function_name_len+1, (void **) &fcic.function_handler) == FAILURE) { + /* error at c-level */ + zend_error(E_CORE_ERROR, "Couldn't find implementation for method %s::%s", obj_ce->name, function_name); + } + if (fn_proxy) { + *fn_proxy = fcic.function_handler; + } + } else { + fcic.function_handler = *fn_proxy; + } + fcic.calling_scope = obj_ce; + fcic.object_pp = object_pp; + result = zend_call_function(&fci, &fcic TSRMLS_CC); + } + if (result == FAILURE) { + /* error at c-level */ + if (!obj_ce) { + obj_ce = Z_OBJCE_PP(object_pp); + } + zend_error(E_CORE_ERROR, "Couldn't execute method %s::%s", obj_ce->name, function_name); + } + if (!retval_ptr_ptr) { + if (retval) { + zval_dtor(retval); + FREE_ZVAL(retval); + } + return NULL; + } + return *retval_ptr_ptr; +} +/* }}} */ + +/* iterator interface, c-level functions used by engine */ + +typedef struct _zend_user_iterator { + zend_object_iterator it; + zend_class_entry *ce; + zval *value; + ulong index; +} zend_user_iterator; + +/* {{{ zend_user_new_iterator */ +static zval *zend_user_new_iterator(zend_class_entry *ce, zval *object TSRMLS_DC) +{ + zval *retval; + + return zend_call_method_with_0_params(&object, ce, &ce->iterator_funcs.zf_new_iterator, "getiterator", &retval); +} +/* }}} */ + +/* {{{ zend_user_dtor */ +static void zend_user_dtor(zend_object_iterator *_iter TSRMLS_DC) +{ + zend_user_iterator *iter = (zend_user_iterator*)_iter; + zval *object = (zval*)iter->it.data; + + if (iter->value) { + zval_ptr_dtor(&iter->value); + iter->value = NULL; + } + zval_ptr_dtor(&object); + efree(iter); +} +/* }}} */ + +/* {{{ zend_user_has_more */ +static int zend_user_has_more(zend_object_iterator *_iter TSRMLS_DC) +{ + zend_user_iterator *iter = (zend_user_iterator*)_iter; + zval *object = (zval*)iter->it.data; + zval *more; + int result; + + zend_call_method_with_0_params(&object, iter->ce, &iter->ce->iterator_funcs.zf_has_more, "hasmore", &more); + result = i_zend_is_true(more); + zval_dtor(more); + FREE_ZVAL(more); + return result ? SUCCESS : FAILURE; +} +/* }}} */ + +/* {{{ zend_user_get_current_data */ +static void zend_user_get_current_data(zend_object_iterator *_iter, zval ***data TSRMLS_DC) +{ + zend_user_iterator *iter = (zend_user_iterator*)_iter; + zval *object = (zval*)iter->it.data; + + if (!iter->value) { + zend_call_method_with_0_params(&object, iter->ce, &iter->ce->iterator_funcs.zf_current, "current", &iter->value); + } + *data = &iter->value; +} +/* }}} */ + +/* {{{ zend_user_get_current_key_default */ +#if 0 +static int zend_user_get_current_key_default(zend_object_iterator *_iter, char **str_key, uint *str_key_len, ulong *int_key TSRMLS_DC) +{ + zend_user_iterator *iter = (zend_user_iterator*)_iter; + *str_key = NULL; + *str_key_len = 0; + *int_key = iter->index; + return HASH_KEY_IS_LONG; +} +#endif +/* }}} */ + +/* {{{ zend_user_get_current_key */ +static int zend_user_get_current_key(zend_object_iterator *_iter, char **str_key, uint *str_key_len, ulong *int_key TSRMLS_DC) +{ + zend_user_iterator *iter = (zend_user_iterator*)_iter; + zval *object = (zval*)iter->it.data; + zval *retval; + + zend_call_method_with_0_params(&object, iter->ce, &iter->ce->iterator_funcs.zf_key, "key", &retval); + + switch (retval->type) { + default: + zend_error(E_WARNING, "Illegal type returned from %s::key()", iter->ce->name); + case IS_NULL: + *str_key = ""; + *str_key_len = 0; + *int_key = 0; + zval_ptr_dtor(&retval); + return HASH_KEY_IS_LONG; + + case IS_STRING: + *str_key = estrndup(retval->value.str.val, retval->value.str.len); + *str_key_len = retval->value.str.len+1; + *int_key = 0; + zval_ptr_dtor(&retval); + return HASH_KEY_IS_STRING; + + case IS_DOUBLE: + case IS_RESOURCE: + case IS_BOOL: + case IS_LONG: { + if (retval->type == IS_DOUBLE) { + *int_key = (long)retval->value.dval; + } else { + *int_key = retval->value.lval; + } + } + zval_ptr_dtor(&retval); + return HASH_KEY_IS_LONG; + } +} +/* }}} */ + +/* {{{ zend_user_move_forward */ +static void zend_user_move_forward(zend_object_iterator *_iter TSRMLS_DC) +{ + zend_user_iterator *iter = (zend_user_iterator*)_iter; + zval *object = (zval*)iter->it.data; + + ++iter->index; + if (iter->value) { + zval_ptr_dtor(&iter->value); + iter->value = NULL; + } + zend_call_method_with_0_params(&object, iter->ce, &iter->ce->iterator_funcs.zf_next, "next", NULL); +} +/* }}} */ + +/* {{{ zend_user_rewind */ +static void zend_user_rewind(zend_object_iterator *_iter TSRMLS_DC) +{ + zend_user_iterator *iter = (zend_user_iterator*)_iter; + zval *object = (zval*)iter->it.data; + + zend_call_method_with_0_params(&object, iter->ce, &iter->ce->iterator_funcs.zf_rewind, "rewind", NULL); +} +/* }}} */ + +zend_object_iterator_funcs zend_interface_iterator_funcs_iterator = { + zend_user_dtor, + zend_user_has_more, + zend_user_get_current_data, + zend_user_get_current_key, + zend_user_move_forward, + zend_user_rewind +}; + +/* {{{ zend_user_get_iterator */ +static zend_object_iterator *zend_user_get_iterator(zend_class_entry *ce, zval *object TSRMLS_DC) +{ + zend_user_iterator *iterator = emalloc(sizeof(zend_user_iterator)); + + object->refcount++; + iterator->it.data = (void*)object; + iterator->it.funcs = ce->iterator_funcs.funcs; + iterator->ce = Z_OBJCE_P(object); + iterator->value = NULL; + iterator->index = 0; + return (zend_object_iterator*)iterator; +} +/* }}} */ + +/* {{{ zend_user_get_new_iterator */ +static zend_object_iterator *zend_user_get_new_iterator(zend_class_entry *ce, zval *object TSRMLS_DC) +{ + zval *iterator = zend_user_new_iterator(ce, object TSRMLS_CC); + + zend_class_entry *ce_it = Z_OBJCE_P(iterator); + if (!ce || !ce_it->get_iterator) { + zend_error(E_WARNING, "Objects returned by %s::getIterator() must be traversable or implement interface Iterator", ce->name); + return NULL; + } + iterator->refcount--; /* from return */ + return ce_it->get_iterator(ce_it, iterator TSRMLS_CC); +} +/* }}} */ + +/* {{{ zend_implement_traversable */ +static int zend_implement_traversable(zend_class_entry *interface, zend_class_entry *class_type TSRMLS_DC) +{ + /* check that class_type is traversable at c-level or implements at least one of 'aggregate' and 'Iterator' */ + int i; + + if (class_type->get_iterator) { + return SUCCESS; + } + for (i = 0; i < class_type->num_interfaces; i++) { + if (class_type->interfaces[i] == zend_ce_aggregate || class_type->interfaces[i] == zend_ce_iterator) { + return SUCCESS; + } + } + zend_error(E_CORE_ERROR, "Class %s must implement interface %s as part of either %s or %s", + class_type->name, + zend_ce_traversable->name, + zend_ce_iterator->name, + zend_ce_aggregate->name); + return FAILURE; +} +/* }}} */ + +/* {{{ zend_implement_aggregate */ +static int zend_implement_aggregate(zend_class_entry *interface, zend_class_entry *class_type TSRMLS_DC) +{ + if (class_type->get_iterator && class_type->get_iterator != zend_user_get_new_iterator) { + return FAILURE; + } + class_type->iterator_funcs.zf_new_iterator = NULL; + class_type->get_iterator = zend_user_get_new_iterator; + return SUCCESS; +} +/* }}} */ + +/* {{{ zend_implement_iterator */ +static int zend_implement_iterator(zend_class_entry *interface, zend_class_entry *class_type TSRMLS_DC) +{ + if (class_type->get_iterator && class_type->get_iterator != zend_user_get_iterator) { + return FAILURE; + } + class_type->get_iterator = zend_user_get_iterator; + class_type->iterator_funcs.zf_has_more = NULL; + class_type->iterator_funcs.zf_current = NULL; + class_type->iterator_funcs.zf_key = NULL; + class_type->iterator_funcs.zf_next = NULL; + class_type->iterator_funcs.zf_rewind = NULL; + if (!class_type->iterator_funcs.funcs) { + class_type->iterator_funcs.funcs = &zend_interface_iterator_funcs_iterator; + } + return SUCCESS; +} +/* }}} */ + +/* {{{ function tables */ +zend_function_entry zend_funcs_aggregate[] = { + ZEND_ABSTRACT_ME(iterator, getIterator, NULL) + {NULL, NULL, NULL} +}; + +zend_function_entry zend_funcs_iterator[] = { + ZEND_ABSTRACT_ME(iterator, current, NULL) + ZEND_ABSTRACT_ME(iterator, next, NULL) + ZEND_ABSTRACT_ME(iterator, key, NULL) + ZEND_ABSTRACT_ME(iterator, hasMore, NULL) + ZEND_ABSTRACT_ME(iterator, rewind, NULL) + {NULL, NULL, NULL} +}; + +zend_function_entry *zend_funcs_traversable = NULL; +/* }}} */ + +#define REGISTER_ITERATOR_INTERFACE(class_name, class_name_str) \ + {\ + zend_class_entry ce;\ + INIT_CLASS_ENTRY(ce, # class_name_str, zend_funcs_ ## class_name) \ + zend_ce_ ## class_name = zend_register_internal_interface(&ce TSRMLS_CC);\ + zend_ce_ ## class_name->interface_gets_implemented = zend_implement_ ## class_name;\ + } + +#define REGISTER_ITERATOR_IMPLEMENT(class_name, interface_name) \ + zend_class_implements(zend_ce_ ## class_name TSRMLS_CC, 1, zend_ce_ ## interface_name) + +/* {{{ zend_register_interfaces */ +ZEND_API void zend_register_interfaces(TSRMLS_D) +{ + REGISTER_ITERATOR_INTERFACE(traversable, Traversable); + + REGISTER_ITERATOR_INTERFACE(aggregate, IteratorAggregate); + REGISTER_ITERATOR_IMPLEMENT(aggregate, traversable); + + REGISTER_ITERATOR_INTERFACE(iterator, Iterator); + REGISTER_ITERATOR_IMPLEMENT(iterator, traversable); +} +/* }}} */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * indent-tabs-mode: t + * End: + */ diff --git a/Zend/zend_interfaces.h b/Zend/zend_interfaces.h new file mode 100755 index 0000000000..8760229c19 --- /dev/null +++ b/Zend/zend_interfaces.h @@ -0,0 +1,43 @@ +/* + +----------------------------------------------------------------------+ + | Zend Engine | + +----------------------------------------------------------------------+ + | Copyright (c) 1998-2003 Zend Technologies Ltd. (http://www.zend.com) | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.00 of the Zend license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.zend.com/license/2_00.txt. | + | If you did not receive a copy of the Zend license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@zend.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Marcus Boerger <helly@php.net> | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#include "zend.h" +#include "zend_API.h" + +ZEND_API zval* zend_call_method(zval **object_pp, zend_class_entry *obj_ce, zend_function **fn_proxy, char *function_name, int function_name_len, zval **retval_ptr_ptr, int param_count, zval* arg1, zval* arg2 TSRMLS_DC); + +#define zend_call_method_with_0_params(obj, obj_ce, fn_proxy, function_name, retval) \ + zend_call_method(obj, obj_ce, fn_proxy, function_name, sizeof(function_name)-1, retval, 0, NULL, NULL TSRMLS_CC) + +#define zend_call_method_with_1_params(obj, obj_ce, fn_proxy, function_name, retval, arg1) \ + zend_call_method(obj, obj_ce, fn_proxy, function_name, sizeof(function_name)-1, retval, 1, arg1, NULL TSRMLS_CC) + +#define zend_call_method_with_2_params(obj, obj_ce, fn_proxy, function_name, retval, arg1, arg2) \ + zend_call_method(obj, obj_ce, fn_proxy, function_name, sizeof(function_name)-1, retval, 2, arg1, arg2 TSRMLS_CC) + +ZEND_API void zend_register_interfaces(TSRMLS_D); + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * indent-tabs-mode: t + * End: + */ diff --git a/tests/classes/iterators_001.phpt b/tests/classes/iterators_001.phpt new file mode 100755 index 0000000000..bef359dd1f --- /dev/null +++ b/tests/classes/iterators_001.phpt @@ -0,0 +1,216 @@ +--TEST-- +ZE2 iterators and foreach +--SKIPIF-- +<?php if (version_compare(zend_version(), '2.0.0-dev', '<')) die('skip ZendEngine 2 needed'); ?> +<?php if (!class_exists('Iterator')) print "skip interface iterator doesn't exist"; ?> +--FILE-- +<?php +class c_iter implements Iterator { + + private $obj; + private $num = 0; + + function __construct($obj) { + echo __METHOD__ . "\n"; + $this->num = 0; + $this->obj = $obj; + } + function rewind() { + } + function hasMore() { + $more = $this->num < $this->obj->max; + echo __METHOD__ . ' = ' .($more ? 'true' : 'false') . "\n"; + return $more; + } + function current() { + echo __METHOD__ . "\n"; + return $this->num; + } + function next() { + echo __METHOD__ . "\n"; + $this->num++; + } + function key() { + echo __METHOD__ . "\n"; + switch($this->num) { + case 0: return "1st"; + case 1: return "2nd"; + case 2: return "3rd"; + default: return "???"; + } + } +} + +class c implements IteratorAggregate { + + public $max = 3; + + function getIterator() { + echo __METHOD__ . "\n"; + return new c_iter($this); + } +} + +echo "===Array===\n"; + +$a = array(0,1,2); +foreach($a as $v) { + echo "array:$v\n"; +} + +echo "===Manual===\n"; +$t = new c(); +for ($iter = $t->getIterator(); $iter->hasMore(); $iter->next()) { + echo $iter->current() . "\n"; +} + +echo "===foreach/std===\n"; +foreach($t as $v) { + echo "object:$v\n"; +} + +echo "===foreach/rec===\n"; +foreach($t as $v) { + foreach($t as $w) { + echo "double:$v:$w\n"; + } +} + +echo "===foreach/key===\n"; +foreach($t as $i => $v) { + echo "object:$i=>$v\n"; +} + +print "Done\n"; +exit(0); +?> +--EXPECT-- +===Array=== +array:0 +array:1 +array:2 +===Manual=== +c::getIterator +c_iter::__construct +c_iter::hasMore = true +c_iter::current +0 +c_iter::next +c_iter::hasMore = true +c_iter::current +1 +c_iter::next +c_iter::hasMore = true +c_iter::current +2 +c_iter::next +c_iter::hasMore = false +===foreach/std=== +c::getIterator +c_iter::__construct +c_iter::hasMore = true +c_iter::current +c_iter::key +c_iter::next +object:0 +c_iter::hasMore = true +c_iter::current +c_iter::key +c_iter::next +object:1 +c_iter::hasMore = true +c_iter::current +c_iter::key +c_iter::next +object:2 +c_iter::hasMore = false +===foreach/rec=== +c::getIterator +c_iter::__construct +c_iter::hasMore = true +c_iter::current +c_iter::key +c_iter::next +c::getIterator +c_iter::__construct +c_iter::hasMore = true +c_iter::current +c_iter::key +c_iter::next +double:0:0 +c_iter::hasMore = true +c_iter::current +c_iter::key +c_iter::next +double:0:1 +c_iter::hasMore = true +c_iter::current +c_iter::key +c_iter::next +double:0:2 +c_iter::hasMore = false +c_iter::hasMore = true +c_iter::current +c_iter::key +c_iter::next +c::getIterator +c_iter::__construct +c_iter::hasMore = true +c_iter::current +c_iter::key +c_iter::next +double:1:0 +c_iter::hasMore = true +c_iter::current +c_iter::key +c_iter::next +double:1:1 +c_iter::hasMore = true +c_iter::current +c_iter::key +c_iter::next +double:1:2 +c_iter::hasMore = false +c_iter::hasMore = true +c_iter::current +c_iter::key +c_iter::next +c::getIterator +c_iter::__construct +c_iter::hasMore = true +c_iter::current +c_iter::key +c_iter::next +double:2:0 +c_iter::hasMore = true +c_iter::current +c_iter::key +c_iter::next +double:2:1 +c_iter::hasMore = true +c_iter::current +c_iter::key +c_iter::next +double:2:2 +c_iter::hasMore = false +c_iter::hasMore = false +===foreach/key=== +c::getIterator +c_iter::__construct +c_iter::hasMore = true +c_iter::current +c_iter::key +c_iter::next +object:1st=>0 +c_iter::hasMore = true +c_iter::current +c_iter::key +c_iter::next +object:2nd=>1 +c_iter::hasMore = true +c_iter::current +c_iter::key +c_iter::next +object:3rd=>2 +c_iter::hasMore = false +Done
\ No newline at end of file diff --git a/tests/classes/iterators_002.phpt b/tests/classes/iterators_002.phpt new file mode 100755 index 0000000000..54bb1a3c14 --- /dev/null +++ b/tests/classes/iterators_002.phpt @@ -0,0 +1,120 @@ +--TEST-- +ZE2 iterators and break +--SKIPIF-- +<?php if (version_compare(zend_version(), '2.0.0-dev', '<')) die('skip ZendEngine 2 needed'); ?> +<?php if (!class_exists('Iterator')) print "skip interface iterator doesn't exist"; ?> +--FILE-- +<?php +class c_iter implements Iterator { + + private $obj; + private $num = 0; + + function __construct($obj) { + echo __METHOD__ . "\n"; + $this->obj = $obj; + } + function rewind() { + echo __METHOD__ . "\n"; + $this->num = 0; + } + function hasMore() { + $more = $this->num < $this->obj->max; + echo __METHOD__ . ' = ' .($more ? 'true' : 'false') . "\n"; + return $more; + } + function current() { + echo __METHOD__ . "\n"; + return $this->num; + } + function next() { + echo __METHOD__ . "\n"; + $this->num++; + } + function key() { + echo __METHOD__ . "\n"; + switch($this->num) { + case 0: return "1st"; + case 1: return "2nd"; + case 2: return "3rd"; + default: return "???"; + } + } + function __destruct() { + echo __METHOD__ . "\n"; + } +} + +class c implements IteratorAggregate { + + public $max = 3; + + function getIterator() { + echo __METHOD__ . "\n"; + return new c_iter($this); + } + function __destruct() { + echo __METHOD__ . "\n"; + } +} + +$t = new c(); + +foreach($t as $v) { + foreach($t as $w) { + echo "double:$v:$w\n"; + break; + } +} + +unset($t); + +print "Done\n"; +?> +--EXPECT-- +c::getIterator +c_iter::__construct +c_iter::rewind +c_iter::hasMore = true +c_iter::current +c_iter::key +c_iter::next +c::getIterator +c_iter::__construct +c_iter::rewind +c_iter::hasMore = true +c_iter::current +c_iter::key +c_iter::next +double:0:0 +c_iter::__destruct +c_iter::hasMore = true +c_iter::current +c_iter::key +c_iter::next +c::getIterator +c_iter::__construct +c_iter::rewind +c_iter::hasMore = true +c_iter::current +c_iter::key +c_iter::next +double:1:0 +c_iter::__destruct +c_iter::hasMore = true +c_iter::current +c_iter::key +c_iter::next +c::getIterator +c_iter::__construct +c_iter::rewind +c_iter::hasMore = true +c_iter::current +c_iter::key +c_iter::next +double:2:0 +c_iter::__destruct +c_iter::hasMore = false +c_iter::__destruct +c::__destruct +Done diff --git a/tests/classes/iterators_003.phpt b/tests/classes/iterators_003.phpt new file mode 100755 index 0000000000..707458af6d --- /dev/null +++ b/tests/classes/iterators_003.phpt @@ -0,0 +1,119 @@ +--TEST-- +ZE2 iterators and break +--SKIPIF-- +<?php if (version_compare(zend_version(), '2.0.0-dev', '<')) die('skip ZendEngine 2 needed'); ?> +<?php if (!class_exists('Iterator')) print "skip interface iterator doesn't exist"; ?> +--FILE-- +<?php +class c_iter implements Iterator { + + private $obj; + private $num = 0; + + function __construct($obj) { + echo __METHOD__ . "\n"; + $this->obj = $obj; + } + function rewind() { + echo __METHOD__ . "\n"; + } + function hasMore() { + $more = $this->num < $this->obj->max; + echo __METHOD__ . ' = ' .($more ? 'true' : 'false') . "\n"; + return $more; + } + function current() { + echo __METHOD__ . "\n"; + return $this->num; + } + function next() { + echo __METHOD__ . "\n"; + $this->num++; + } + function key() { + return $this->num; + } +} + +class c implements IteratorAggregate { + + public $max = 4; + + function getIterator() { + echo __METHOD__ . "\n"; + return new c_iter($this); + } +} + +$t = new c(); + +foreach($t as $v) { + if ($v == 0) { + echo "continue outer\n"; + continue; + } + foreach($t as $w) { + if ($w == 1) { + echo "continue inner\n"; + continue; + } + if ($w == 2) { + echo "break inner\n"; + break; + } + echo "double:$v:$w\n"; + } + if ($v == 2) { + echo "break outer\n"; + break; + } +} + +print "Done\n"; +?> +--EXPECT-- +c::getIterator +c_iter::__construct +c_iter::rewind +c_iter::hasMore = true +c_iter::current +c_iter::next +continue outer +c_iter::hasMore = true +c_iter::current +c_iter::next +c::getIterator +c_iter::__construct +c_iter::rewind +c_iter::hasMore = true +c_iter::current +c_iter::next +double:1:0 +c_iter::hasMore = true +c_iter::current +c_iter::next +continue inner +c_iter::hasMore = true +c_iter::current +c_iter::next +break inner +c_iter::hasMore = true +c_iter::current +c_iter::next +c::getIterator +c_iter::__construct +c_iter::rewind +c_iter::hasMore = true +c_iter::current +c_iter::next +double:2:0 +c_iter::hasMore = true +c_iter::current +c_iter::next +continue inner +c_iter::hasMore = true +c_iter::current +c_iter::next +break inner +break outer +Done diff --git a/tests/classes/iterators_004.phpt b/tests/classes/iterators_004.phpt new file mode 100755 index 0000000000..b67a1634e9 --- /dev/null +++ b/tests/classes/iterators_004.phpt @@ -0,0 +1,62 @@ +--TEST-- +ZE2 iterators must be implemented +--SKIPIF-- +<?php if (version_compare(zend_version(), '2.0.0-dev', '<')) die('skip ZendEngine 2 needed'); ?> +<?php if (!class_exists('iterator')) print "skip interface iterator doesn't exist"; ?> +--FILE-- +<?php + +echo "1st try\n"; + +class c1 {} + +$obj = new c1(); + +foreach($obj as $w) { + echo "object:$w\n"; +} + +echo "2nd try\n"; + +class c2 { + + public $max = 3; + public $num = 0; + + function current() { + echo __METHOD__ . "\n"; + return $this->num; + } + function next() { + echo __METHOD__ . "\n"; + $this->num++; + } + function hasMore() { + echo __METHOD__ . "\n"; + return $this->num < $this->max; + } + function key() { + echo __METHOD__ . "\n"; + switch($this->num) { + case 0: return "1st"; + case 1: return "2nd"; + case 2: return "3rd"; + default: return "???"; + } + } +} + +$obj = new c2(); + +foreach($obj as $v => $w) { + echo "object:$v=>$w\n"; +} + +print "Done\n"; +?> +--EXPECTF-- +1st try +2nd try +object:max=>3 +object:num=>0 +Done diff --git a/tests/classes/iterators_005.phpt b/tests/classes/iterators_005.phpt new file mode 100755 index 0000000000..6c1dadcc1a --- /dev/null +++ b/tests/classes/iterators_005.phpt @@ -0,0 +1,20 @@ +--TEST-- +ZE2 iterators cannot implement Traversable alone +--SKIPIF-- +<?php if (version_compare(zend_version(), '2.0.0-dev', '<')) die('skip ZendEngine 2 needed'); ?> +<?php if (!class_exists('iterator')) print "skip interface iterator doesn't exist"; ?> +--FILE-- +<?php + +class test implements Traversable { +} + +$obj = new test; + +foreach($obj as $v); + +print "Done\n"; +/* the error doesn't show the filename but 'Unknown' */ +?> +--EXPECTF-- +Fatal error: Class test must implement interface Traversable as part of either Iterator or IteratorAggregate in %s on line %d |