Skip to content
Browse files

Added c-api for iterators

# After 4 Month work and endless discussions...
  • Loading branch information...
1 parent 08d1c99 commit 25aa8b715e2f7eb08a9e1287c1a479951759dbfd Marcus Boerger committed
Showing with 278 additions and 15 deletions.
  1. +8 −0 Zend/ZendTS.dsp
  2. +5 −1 Zend/zend.h
  3. +3 −1 Zend/zend_API.h
  4. +1 −0 Zend/zend_default_classes.c
  5. +1 −0 Zend/zend_exceptions.c
  6. +62 −12 Zend/zend_execute.c
  7. +107 −0 Zend/zend_iterators.c
  8. +89 −0 Zend/zend_iterators.h
  9. +2 −1 configure.in
View
8 Zend/ZendTS.dsp
@@ -200,6 +200,10 @@ SOURCE=.\zend_ini_scanner.c
# End Source File
# Begin Source File
+SOURCE=.\zend_iterators.c
+# End Source File
+# Begin Source File
+
SOURCE=".\zend_language_parser.c"
# End Source File
# Begin Source File
@@ -367,6 +371,10 @@ SOURCE=.\zend_ini_scanner.h
SOURCE=.\zend_istdiostream.h
# End Source File
# Begin Source File
+
+SOURCE=.\zend_iterators.h
+# End Source File
+# Begin Source File
SOURCE=".\zend_language_parser.h"
# End Source File
View
6 Zend/zend.h
@@ -308,6 +308,8 @@ union _zend_function;
#define ZEND_CE_ABSTRACT ZEND_ACC_ABSTRACT /* same as ZEND_ACC_ABSTRACT */
+#include "zend_iterators.h"
+
struct _zend_class_entry {
char type;
char *name;
@@ -331,9 +333,11 @@ struct _zend_class_entry {
union _zend_function *__set;
union _zend_function *__call;
-
+ zend_class_iterator_funcs *iterator_funcs;
+
/* handlers */
zend_object_value (*create_object)(zend_class_entry *class_type TSRMLS_DC);
+ zend_object_iterator *(*get_iterator)(zend_class_entry *ce, zval *object TSRMLS_DC);
zend_class_entry **interfaces;
zend_uint num_interfaces;
View
4 Zend/zend_API.h
@@ -124,7 +124,9 @@ typedef struct _zend_function_entry {
class_container.__call = handle_fcall; \
class_container.__get = handle_propget; \
class_container.__set = handle_propset; \
- class_container.num_interfaces = 0; \
+ class_container.num_interfaces = 0; \
+ class_container.get_iterator = NULL; \
+ class_container.iterator_funcs = NULL; \
}
int zend_next_free_module(void);
View
1 Zend/zend_default_classes.c
@@ -506,6 +506,7 @@ ZEND_API void zend_register_default_classes(TSRMLS_D)
{
zend_register_default_exception(TSRMLS_C);
zend_register_reflection_api(TSRMLS_C);
+ zend_register_iterator_wrapper(TSRMLS_C);
}
/*
View
1 Zend/zend_exceptions.c
@@ -506,6 +506,7 @@ ZEND_API void zend_register_default_classes(TSRMLS_D)
{
zend_register_default_exception(TSRMLS_C);
zend_register_reflection_api(TSRMLS_C);
+ zend_register_iterator_wrapper(TSRMLS_C);
}
/*
View
74 Zend/zend_execute.c
@@ -3534,11 +3534,20 @@ int zend_fe_reset_handler(ZEND_OPCODE_HANDLER_ARGS)
{
zval *array_ptr, **array_ptr_ptr;
HashTable *fe_ht;
+ zend_object_iterator *iter = NULL;
+ zend_class_entry *ce = NULL;
if (EX(opline)->extended_value) {
array_ptr_ptr = get_zval_ptr_ptr(&EX(opline)->op1, EX(Ts), BP_VAR_R);
if (array_ptr_ptr == NULL) {
MAKE_STD_ZVAL(array_ptr);
+ } else if (Z_TYPE_PP(array_ptr_ptr) == IS_OBJECT) {
+ ce = Z_OBJCE_PP(array_ptr_ptr);
+ if (!ce || ce->get_iterator == NULL) {
+ SEPARATE_ZVAL_IF_NOT_REF(array_ptr_ptr);
+ (*array_ptr_ptr)->refcount++;
+ }
+ array_ptr = *array_ptr_ptr;
} else {
SEPARATE_ZVAL_IF_NOT_REF(array_ptr_ptr);
array_ptr = *array_ptr_ptr;
@@ -3553,16 +3562,32 @@ int zend_fe_reset_handler(ZEND_OPCODE_HANDLER_ARGS)
*tmp = *array_ptr;
INIT_PZVAL(tmp);
array_ptr = tmp;
+ } else if (Z_TYPE_P(array_ptr) == IS_OBJECT) {
+ ce = Z_OBJCE_P(array_ptr);
} else {
array_ptr->refcount++;
}
}
+ if (ce && ce->get_iterator) {
+ iter = ce->get_iterator(ce, array_ptr TSRMLS_CC);
+
+ if (iter) {
+ array_ptr = zend_iterator_wrap(iter TSRMLS_CC);
+ } else {
+ array_ptr->refcount++;
+ }
+ }
+
PZVAL_LOCK(array_ptr);
EX_T(EX(opline)->result.u.var).var.ptr = array_ptr;
EX_T(EX(opline)->result.u.var).var.ptr_ptr = &EX_T(EX(opline)->result.u.var).var.ptr;
- if ((fe_ht = HASH_OF(array_ptr)) != NULL) {
+ if (iter) {
+ if (iter->funcs->rewind) {
+ iter->funcs->rewind(iter TSRMLS_CC);
+ }
+ } else if ((fe_ht = HASH_OF(array_ptr)) != NULL) {
/* probably redundant */
zend_hash_internal_pointer_reset(fe_ht);
} else {
@@ -3586,20 +3611,37 @@ int zend_fe_fetch_handler(ZEND_OPCODE_HANDLER_ARGS)
uint str_key_len;
ulong int_key;
HashTable *fe_ht;
+ zend_object_iterator *iter = NULL;
PZVAL_LOCK(array);
- fe_ht = HASH_OF(array);
- if (!fe_ht) {
- zend_error(E_WARNING, "Invalid argument supplied for foreach()");
- EX(opline) = op_array->opcodes+EX(opline)->op2.u.opline_num;
- return 0; /* CHECK_ME */
- } else if (zend_hash_get_current_data(fe_ht, (void **) &value)==FAILURE) {
- EX(opline) = op_array->opcodes+EX(opline)->op2.u.opline_num;
- return 0; /* CHECK_ME */
+ switch (zend_iterator_unwrap(array, &iter TSRMLS_CC)) {
+ case ZEND_ITER_INVALID:
+ zend_error(E_WARNING, "Invalid argument supplied for foreach()");
+ EX(opline) = op_array->opcodes+EX(opline)->op2.u.opline_num;
+ return 0; /* CHECK_ME */
+
+ case ZEND_ITER_PLAIN_ARRAY:
+ /* good old fashioned foreach on an array */
+ fe_ht = HASH_OF(array);
+ if (zend_hash_get_current_data(fe_ht, (void **) &value)==FAILURE) {
+ /* reached end of iteration */
+ EX(opline) = op_array->opcodes+EX(opline)->op2.u.opline_num;
+ return 0; /* CHECK_ME */
+ }
+ break;
+
+ case ZEND_ITER_OBJECT:
+ if (iter->funcs->has_more(iter TSRMLS_CC) == FAILURE) {
+ /* reached end of iteration */
+ EX(opline) = op_array->opcodes+EX(opline)->op2.u.opline_num;
+ return 0; /* CHECK_ME */
+ }
+ iter->funcs->get_current_data(iter, &value TSRMLS_CC);
+ break;
}
- array_init(result);
+ array_init(result);
if (EX(opline)->extended_value) {
SEPARATE_ZVAL_IF_NOT_REF(value);
@@ -3610,7 +3652,10 @@ int zend_fe_fetch_handler(ZEND_OPCODE_HANDLER_ARGS)
ALLOC_ZVAL(key);
INIT_PZVAL(key);
- switch (zend_hash_get_current_key_ex(fe_ht, &str_key, &str_key_len, &int_key, 1, NULL)) {
+
+ switch (iter ?
+ iter->funcs->get_current_key(iter, &str_key, &str_key_len, &int_key TSRMLS_CC) :
+ zend_hash_get_current_key_ex(fe_ht, &str_key, &str_key_len, &int_key, 1, NULL)) {
case HASH_KEY_IS_STRING:
key->value.str.val = str_key;
key->value.str.len = str_key_len-1;
@@ -3623,7 +3668,12 @@ int zend_fe_fetch_handler(ZEND_OPCODE_HANDLER_ARGS)
EMPTY_SWITCH_DEFAULT_CASE()
}
zend_hash_index_update(result->value.ht, 1, &key, sizeof(zval *), NULL);
- zend_hash_move_forward(fe_ht);
+
+ if (iter) {
+ iter->funcs->move_forward(iter TSRMLS_CC);
+ } else {
+ zend_hash_move_forward(fe_ht);
+ }
NEXT_OPCODE();
}
View
107 Zend/zend_iterators.c
@@ -0,0 +1,107 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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. |
+ +----------------------------------------------------------------------+
+ | Author: Wez Furlong <wez@thebrainroom.com> |
+ | Marcus Boerger <helly@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+#include "zend.h"
+#include "zend_API.h"
+
+static zend_class_entry zend_iterator_class_entry;
+
+static zend_class_entry *iter_handler_get_ce(zval *object TSRMLS_DC)
+{
+ return &zend_iterator_class_entry;
+}
+
+static zend_object_handlers iterator_object_handlers = {
+ ZEND_OBJECTS_STORE_HANDLERS,
+ NULL, /* prop read */
+ NULL, /* prop write */
+ NULL, /* read dim */
+ NULL, /* write dim */
+ NULL,
+ NULL, /* get */
+ NULL, /* set */
+ NULL, /* isset */
+ NULL, /* unset */
+ NULL, /* dim unset */
+ NULL, /* props get */
+ NULL, /* method get */
+ NULL, /* call */
+ NULL, /* get ctor */
+ iter_handler_get_ce,
+ NULL, /* get class name */
+ NULL, /* compare */
+ NULL /* cast */
+};
+
+ZEND_API void zend_register_iterator_wrapper(TSRMLS_D)
+{
+ INIT_CLASS_ENTRY(zend_iterator_class_entry, "__iterator_wrapper", NULL);
+}
+
+static void iter_wrapper_dtor(void *object, zend_object_handle handle TSRMLS_DC)
+{
+ zend_object_iterator *iter = (zend_object_iterator*)object;
+ iter->funcs->dtor(iter TSRMLS_CC);
+}
+
+ZEND_API zval *zend_iterator_wrap(zend_object_iterator *iter TSRMLS_DC)
+{
+ zval *wrapped;
+
+ MAKE_STD_ZVAL(wrapped);
+ Z_TYPE_P(wrapped) = IS_OBJECT;
+ wrapped->value.obj.handle = zend_objects_store_put(iter, iter_wrapper_dtor, NULL TSRMLS_CC);
+ wrapped->value.obj.handlers = &iterator_object_handlers;
+
+ return wrapped;
+}
+
+ZEND_API enum zend_object_iterator_kind zend_iterator_unwrap(
+ zval *array_ptr, zend_object_iterator **iter TSRMLS_DC)
+{
+ zend_class_entry *ce;
+
+ switch (Z_TYPE_P(array_ptr)) {
+ case IS_OBJECT:
+ ce = Z_OBJCE_P(array_ptr);
+ if (ce == &zend_iterator_class_entry) {
+ *iter = (zend_object_iterator *)zend_object_store_get_object(array_ptr TSRMLS_CC);
+ return ZEND_ITER_OBJECT;
+ }
+ return ZEND_ITER_INVALID;
+
+ case IS_ARRAY:
+ *iter = NULL;
+ return HASH_OF(array_ptr) ? ZEND_ITER_PLAIN_ARRAY : ZEND_ITER_INVALID;
+
+ default:
+ *iter = NULL;
+ return ZEND_ITER_INVALID;
+ }
+}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * indent-tabs-mode: t
+ * End:
+ */
View
89 Zend/zend_iterators.h
@@ -0,0 +1,89 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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. |
+ +----------------------------------------------------------------------+
+ | Author: Wez Furlong <wez@thebrainroom.com> |
+ | Marcus Boerger <helly@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+/* These iterators were designed to operate within the foreach()
+ * structures provided by the engine, but could be extended for use
+ * with other iterative engine opcodes.
+ * These methods have similar semantics to the zend_hash API functions
+ * with similar names.
+ * */
+
+typedef struct _zend_object_iterator zend_object_iterator;
+
+typedef struct _zend_object_iterator_funcs {
+ /* release all resources associated with this iterator instance */
+ void (*dtor)(zend_object_iterator *iter TSRMLS_DC);
+
+ /* rewind to start of data (optional, may be NULL) */
+ void (*rewind)(zend_object_iterator *iter TSRMLS_DC);
+
+ /* check for end of iteration (FAILURE or SUCCESS for more data) */
+ int (*has_more)(zend_object_iterator *iter TSRMLS_DC);
+
+ /* fetch the item data for the current element */
+ void (*get_current_data)(zend_object_iterator *iter, zval ***data TSRMLS_DC);
+
+ /* fetch the key for the current element (return HASH_KEY_IS_STRING or HASH_KEY_IS_LONG) */
+ int (*get_current_key)(zend_object_iterator *iter, char **str_key, uint *str_key_len, ulong *int_key TSRMLS_DC);
+
+ /* step forwards to next element */
+ void (*move_forward)(zend_object_iterator *iter TSRMLS_DC);
+} zend_object_iterator_funcs;
+
+struct _zend_object_iterator {
+ void *data;
+ zend_object_iterator_funcs *funcs;
+};
+
+typedef zval *(*zend_object_new_iterator_t)(zend_class_entry *ce, zval *object TSRMLS_DC);
+
+typedef struct _zend_class_iterator_funcs {
+ zend_object_iterator_funcs funcs;
+ zend_object_new_iterator_t new_iterator;
+ union _zend_function *zf_new_iterator;
+ union _zend_function *zf_has_more;
+ union _zend_function *zf_current;
+ union _zend_function *zf_key;
+ union _zend_function *zf_next;
+ union _zend_function *zf_rewind;
+} zend_class_iterator_funcs;
+
+enum zend_object_iterator_kind {
+ ZEND_ITER_INVALID,
+ ZEND_ITER_PLAIN_ARRAY,
+ ZEND_ITER_OBJECT
+};
+
+/* given a zval, returns stuff that can be used to iterate it. */
+ZEND_API enum zend_object_iterator_kind zend_iterator_unwrap(zval *array_ptr, zend_object_iterator **iter TSRMLS_DC);
+
+/* given an iterator, wrap it up as a zval for use by the engine opcodes */
+ZEND_API zval *zend_iterator_wrap(zend_object_iterator *iter TSRMLS_DC);
+
+ZEND_API void zend_register_iterator_wrapper(TSRMLS_D);
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * indent-tabs-mode: t
+ * End:
+ */
View
3 configure.in
@@ -1173,7 +1173,8 @@ PHP_ADD_SOURCES(Zend, \
zend_opcode.c zend_operators.c zend_ptr_stack.c zend_stack.c \
zend_variables.c zend.c zend_API.c zend_extensions.c zend_hash.c \
zend_list.c zend_indent.c zend_builtin_functions.c zend_sprintf.c \
- zend_ini.c zend_qsort.c zend_multibyte.c zend_ts_hash.c zend_stream.c)
+ zend_ini.c zend_qsort.c zend_multibyte.c zend_ts_hash.c zend_stream.c \
+ zend_iterators.c)
if test -r "$abs_srcdir/Zend/zend_objects.c"; then
PHP_ADD_SOURCES(Zend, zend_objects.c zend_object_handlers.c zend_objects_API.c zend_mm.c \

0 comments on commit 25aa8b7

Please sign in to comment.
Something went wrong with that request. Please try again.