/* +----------------------------------------------------------------------+ | PHP Version 5 | +----------------------------------------------------------------------+ | Copyright (c) 1997-2013 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP 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.php.net/license/3_01.txt | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Author: Wez Furlong | +----------------------------------------------------------------------+ */ /* $Id$ */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "php.h" #include "php_ini.h" #include "ext/standard/info.h" #include "php_com_dotnet.h" #include "php_com_dotnet_internal.h" #include "Zend/zend_exceptions.h" struct php_com_iterator { zend_object_iterator iter; IEnumVARIANT *ev; ulong key; VARIANT v; /* cached element */ int code_page; VARIANT safe_array; VARTYPE sa_type; LONG sa_max; zval *zdata; }; static void com_iter_dtor(zend_object_iterator *iter TSRMLS_DC) { struct php_com_iterator *I = (struct php_com_iterator*)iter->data; if (I->ev) { IEnumVARIANT_Release(I->ev); } VariantClear(&I->v); VariantClear(&I->safe_array); if (I->zdata) { zval_ptr_dtor((zval**)&I->zdata); } efree(I); } static int com_iter_valid(zend_object_iterator *iter TSRMLS_DC) { struct php_com_iterator *I = (struct php_com_iterator*)iter->data; if (I->zdata) { return SUCCESS; } return FAILURE; } static void com_iter_get_data(zend_object_iterator *iter, zval ***data TSRMLS_DC) { struct php_com_iterator *I = (struct php_com_iterator*)iter->data; *data = &I->zdata; } static int com_iter_get_key(zend_object_iterator *iter, char **str_key, uint *str_key_len, ulong *int_key TSRMLS_DC) { struct php_com_iterator *I = (struct php_com_iterator*)iter->data; if (I->key == (ulong)-1) { return HASH_KEY_NON_EXISTANT; } *int_key = I->key; return HASH_KEY_IS_LONG; } static int com_iter_move_forwards(zend_object_iterator *iter TSRMLS_DC) { struct php_com_iterator *I = (struct php_com_iterator*)iter->data; unsigned long n_fetched; zval *ptr; /* release current cached element */ VariantClear(&I->v); if (I->zdata) { zval_ptr_dtor((zval**)&I->zdata); I->zdata = NULL; } if (I->ev) { /* Get the next element */ if (SUCCEEDED(IEnumVARIANT_Next(I->ev, 1, &I->v, &n_fetched)) && n_fetched > 0) { I->key++; } else { /* indicate that there are no more items */ I->key = (ulong)-1; return FAILURE; } } else { /* safe array */ if (I->key >= (ULONG) I->sa_max) { I->key = (ulong)-1; return FAILURE; } I->key++; if (php_com_safearray_get_elem(&I->safe_array, &I->v, (LONG)I->key TSRMLS_CC) == 0) { I->key = (ulong)-1; return FAILURE; } } MAKE_STD_ZVAL(ptr); php_com_zval_from_variant(ptr, &I->v, I->code_page TSRMLS_CC); /* php_com_wrap_variant(ptr, &I->v, I->code_page TSRMLS_CC); */ I->zdata = ptr; return SUCCESS; } static zend_object_iterator_funcs com_iter_funcs = { com_iter_dtor, com_iter_valid, com_iter_get_data, com_iter_get_key, com_iter_move_forwards, NULL }; zend_object_iterator *php_com_iter_get(zend_class_entry *ce, zval *object, int by_ref TSRMLS_DC) { php_com_dotnet_object *obj; struct php_com_iterator *I; IEnumVARIANT *iev = NULL; DISPPARAMS dp; VARIANT v; unsigned long n_fetched; zval *ptr; if (by_ref) { zend_error(E_ERROR, "An iterator cannot be used with foreach by reference"); } obj = CDNO_FETCH(object); if (V_VT(&obj->v) != VT_DISPATCH && !V_ISARRAY(&obj->v)) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "variant is not an object or array VT=%d", V_VT(&obj->v)); return NULL; } memset(&dp, 0, sizeof(dp)); VariantInit(&v); I = (struct php_com_iterator*)ecalloc(1, sizeof(*I)); I->iter.funcs = &com_iter_funcs; I->iter.data = I; I->code_page = obj->code_page; I->zdata = NULL; VariantInit(&I->safe_array); VariantInit(&I->v); if (V_ISARRAY(&obj->v)) { LONG bound; UINT dims; dims = SafeArrayGetDim(V_ARRAY(&obj->v)); if (dims != 1) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Can only handle single dimension variant arrays (this array has %d)", dims); goto fail; } /* same semantics as foreach on a PHP array; * make a copy and enumerate that copy */ VariantCopy(&I->safe_array, &obj->v); /* determine the key value for the array */ SafeArrayGetLBound(V_ARRAY(&I->safe_array), 1, &bound); SafeArrayGetUBound(V_ARRAY(&I->safe_array), 1, &I->sa_max); /* pre-fetch the element */ if (php_com_safearray_get_elem(&I->safe_array, &I->v, bound TSRMLS_CC)) { I->key = bound; MAKE_STD_ZVAL(ptr); php_com_zval_from_variant(ptr, &I->v, I->code_page TSRMLS_CC); I->zdata = ptr; } else { I->key = (ulong)-1; } } else { /* can we enumerate it? */ if (FAILED(IDispatch_Invoke(V_DISPATCH(&obj->v), DISPID_NEWENUM, &IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_METHOD|DISPATCH_PROPERTYGET, &dp, &v, NULL, NULL))) { goto fail; } /* get something useful out of it */ if (V_VT(&v) == VT_UNKNOWN) { IUnknown_QueryInterface(V_UNKNOWN(&v), &IID_IEnumVARIANT, (void**)&iev); } else if (V_VT(&v) == VT_DISPATCH) { IDispatch_QueryInterface(V_DISPATCH(&v), &IID_IEnumVARIANT, (void**)&iev); } VariantClear(&v); if (iev == NULL) { goto fail; } I->ev = iev; /* Get the first element now */ if (SUCCEEDED(IEnumVARIANT_Next(I->ev, 1, &I->v, &n_fetched)) && n_fetched > 0) { /* indicate that we have element 0 */ I->key = 0; MAKE_STD_ZVAL(ptr); php_com_zval_from_variant(ptr, &I->v, I->code_page TSRMLS_CC); I->zdata = ptr; } else { /* indicate that there are no more items */ I->key = (ulong)-1; } } return &I->iter; fail: if (I) { VariantClear(&I->safe_array); VariantClear(&I->v); efree(I); } return NULL; }