xref: /PHP-5.4/ext/com_dotnet/com_iterator.c (revision c0d060f5)
1 /*
2    +----------------------------------------------------------------------+
3    | PHP Version 5                                                        |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 1997-2014 The PHP Group                                |
6    +----------------------------------------------------------------------+
7    | This source file is subject to version 3.01 of the PHP license,      |
8    | that is bundled with this package in the file LICENSE, and is        |
9    | available through the world-wide-web at the following url:           |
10    | http://www.php.net/license/3_01.txt                                  |
11    | If you did not receive a copy of the PHP license and are unable to   |
12    | obtain it through the world-wide-web, please send a note to          |
13    | license@php.net so we can mail you a copy immediately.               |
14    +----------------------------------------------------------------------+
15    | Author: Wez Furlong <wez@thebrainroom.com>                           |
16    +----------------------------------------------------------------------+
17  */
18 
19 /* $Id$ */
20 
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24 
25 #include "php.h"
26 #include "php_ini.h"
27 #include "ext/standard/info.h"
28 #include "php_com_dotnet.h"
29 #include "php_com_dotnet_internal.h"
30 #include "Zend/zend_exceptions.h"
31 
32 struct php_com_iterator {
33 	zend_object_iterator iter;
34 	IEnumVARIANT *ev;
35 	ulong key;
36 	VARIANT v; /* cached element */
37 	int code_page;
38 	VARIANT safe_array;
39 	VARTYPE sa_type;
40 	LONG sa_max;
41 	zval *zdata;
42 };
43 
com_iter_dtor(zend_object_iterator * iter TSRMLS_DC)44 static void com_iter_dtor(zend_object_iterator *iter TSRMLS_DC)
45 {
46 	struct php_com_iterator *I = (struct php_com_iterator*)iter->data;
47 
48 	if (I->ev) {
49 		IEnumVARIANT_Release(I->ev);
50 	}
51 	VariantClear(&I->v);
52 	VariantClear(&I->safe_array);
53 	if (I->zdata) {
54 		zval_ptr_dtor((zval**)&I->zdata);
55 	}
56 	efree(I);
57 }
58 
com_iter_valid(zend_object_iterator * iter TSRMLS_DC)59 static int com_iter_valid(zend_object_iterator *iter TSRMLS_DC)
60 {
61 	struct php_com_iterator *I = (struct php_com_iterator*)iter->data;
62 
63 	if (I->zdata) {
64 		return SUCCESS;
65 	}
66 
67 	return FAILURE;
68 }
69 
com_iter_get_data(zend_object_iterator * iter,zval *** data TSRMLS_DC)70 static void com_iter_get_data(zend_object_iterator *iter, zval ***data TSRMLS_DC)
71 {
72 	struct php_com_iterator *I = (struct php_com_iterator*)iter->data;
73 
74 	*data = &I->zdata;
75 }
76 
com_iter_get_key(zend_object_iterator * iter,char ** str_key,uint * str_key_len,ulong * int_key TSRMLS_DC)77 static int com_iter_get_key(zend_object_iterator *iter, char **str_key, uint *str_key_len,
78 	ulong *int_key TSRMLS_DC)
79 {
80 	struct php_com_iterator *I = (struct php_com_iterator*)iter->data;
81 
82 	if (I->key == (ulong)-1) {
83 		return HASH_KEY_NON_EXISTANT;
84 	}
85 	*int_key = I->key;
86 	return HASH_KEY_IS_LONG;
87 }
88 
com_iter_move_forwards(zend_object_iterator * iter TSRMLS_DC)89 static int com_iter_move_forwards(zend_object_iterator *iter TSRMLS_DC)
90 {
91 	struct php_com_iterator *I = (struct php_com_iterator*)iter->data;
92 	unsigned long n_fetched;
93 	zval *ptr;
94 
95 	/* release current cached element */
96 	VariantClear(&I->v);
97 
98 	if (I->zdata) {
99 		zval_ptr_dtor((zval**)&I->zdata);
100 		I->zdata = NULL;
101 	}
102 
103 	if (I->ev) {
104 		/* Get the next element */
105 		if (SUCCEEDED(IEnumVARIANT_Next(I->ev, 1, &I->v, &n_fetched)) && n_fetched > 0) {
106 			I->key++;
107 		} else {
108 			/* indicate that there are no more items */
109 			I->key = (ulong)-1;
110 			return FAILURE;
111 		}
112 	} else {
113 		/* safe array */
114 		if (I->key >= (ULONG) I->sa_max) {
115 			I->key = (ulong)-1;
116 			return FAILURE;
117 		}
118 		I->key++;
119 		if (php_com_safearray_get_elem(&I->safe_array, &I->v, (LONG)I->key TSRMLS_CC) == 0) {
120 			I->key = (ulong)-1;
121 			return FAILURE;
122 		}
123 	}
124 
125 	MAKE_STD_ZVAL(ptr);
126 	php_com_zval_from_variant(ptr, &I->v, I->code_page TSRMLS_CC);
127 	/* php_com_wrap_variant(ptr, &I->v, I->code_page TSRMLS_CC); */
128 	I->zdata = ptr;
129 	return SUCCESS;
130 }
131 
132 
133 static zend_object_iterator_funcs com_iter_funcs = {
134 	com_iter_dtor,
135 	com_iter_valid,
136 	com_iter_get_data,
137 	com_iter_get_key,
138 	com_iter_move_forwards,
139 	NULL
140 };
141 
php_com_iter_get(zend_class_entry * ce,zval * object,int by_ref TSRMLS_DC)142 zend_object_iterator *php_com_iter_get(zend_class_entry *ce, zval *object, int by_ref TSRMLS_DC)
143 {
144 	php_com_dotnet_object *obj;
145 	struct php_com_iterator *I;
146 	IEnumVARIANT *iev = NULL;
147 	DISPPARAMS dp;
148 	VARIANT v;
149 	unsigned long n_fetched;
150 	zval *ptr;
151 
152 	if (by_ref) {
153 		zend_error(E_ERROR, "An iterator cannot be used with foreach by reference");
154 	}
155 
156 	obj = CDNO_FETCH(object);
157 
158 	if (V_VT(&obj->v) != VT_DISPATCH && !V_ISARRAY(&obj->v)) {
159 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "variant is not an object or array VT=%d", V_VT(&obj->v));
160 		return NULL;
161 	}
162 
163 	memset(&dp, 0, sizeof(dp));
164 	VariantInit(&v);
165 
166 	I = (struct php_com_iterator*)ecalloc(1, sizeof(*I));
167 	I->iter.funcs = &com_iter_funcs;
168 	I->iter.data = I;
169 	I->code_page = obj->code_page;
170 	I->zdata = NULL;
171 	VariantInit(&I->safe_array);
172 	VariantInit(&I->v);
173 
174 	if (V_ISARRAY(&obj->v)) {
175 		LONG bound;
176 		UINT dims;
177 
178 		dims = SafeArrayGetDim(V_ARRAY(&obj->v));
179 
180 		if (dims != 1) {
181 			php_error_docref(NULL TSRMLS_CC, E_WARNING,
182 				   "Can only handle single dimension variant arrays (this array has %d)", dims);
183 			goto fail;
184 		}
185 
186 		/* same semantics as foreach on a PHP array;
187 		 * make a copy and enumerate that copy */
188 		VariantCopy(&I->safe_array, &obj->v);
189 
190 		/* determine the key value for the array */
191 		SafeArrayGetLBound(V_ARRAY(&I->safe_array), 1, &bound);
192 		SafeArrayGetUBound(V_ARRAY(&I->safe_array), 1, &I->sa_max);
193 
194 		/* pre-fetch the element */
195 		if (php_com_safearray_get_elem(&I->safe_array, &I->v, bound TSRMLS_CC)) {
196 			I->key = bound;
197 			MAKE_STD_ZVAL(ptr);
198 			php_com_zval_from_variant(ptr, &I->v, I->code_page TSRMLS_CC);
199 			I->zdata = ptr;
200 		} else {
201 			I->key = (ulong)-1;
202 		}
203 
204 	} else {
205 		/* can we enumerate it? */
206 		if (FAILED(IDispatch_Invoke(V_DISPATCH(&obj->v), DISPID_NEWENUM,
207 						&IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_METHOD|DISPATCH_PROPERTYGET,
208 						&dp, &v, NULL, NULL))) {
209 			goto fail;
210 		}
211 
212 		/* get something useful out of it */
213 		if (V_VT(&v) == VT_UNKNOWN) {
214 			IUnknown_QueryInterface(V_UNKNOWN(&v), &IID_IEnumVARIANT, (void**)&iev);
215 		} else if (V_VT(&v) == VT_DISPATCH) {
216 			IDispatch_QueryInterface(V_DISPATCH(&v), &IID_IEnumVARIANT, (void**)&iev);
217 		}
218 
219 		VariantClear(&v);
220 
221 		if (iev == NULL) {
222 			goto fail;
223 		}
224 
225 		I->ev = iev;
226 
227 		/* Get the first element now */
228 		if (SUCCEEDED(IEnumVARIANT_Next(I->ev, 1, &I->v, &n_fetched)) && n_fetched > 0) {
229 			/* indicate that we have element 0 */
230 			I->key = 0;
231 			MAKE_STD_ZVAL(ptr);
232 			php_com_zval_from_variant(ptr, &I->v, I->code_page TSRMLS_CC);
233 			I->zdata = ptr;
234 		} else {
235 			/* indicate that there are no more items */
236 			I->key = (ulong)-1;
237 		}
238 	}
239 
240 	return &I->iter;
241 
242 fail:
243 	if (I) {
244 		VariantClear(&I->safe_array);
245 		VariantClear(&I->v);
246 		efree(I);
247 	}
248 	return NULL;
249 }
250 
251