xref: /PHP-5.6/ext/com_dotnet/com_iterator.c (revision 49493a2d)
1 /*
2    +----------------------------------------------------------------------+
3    | PHP Version 5                                                        |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 1997-2016 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,zval * key TSRMLS_DC)77 static void com_iter_get_key(zend_object_iterator *iter, zval *key TSRMLS_DC)
78 {
79 	struct php_com_iterator *I = (struct php_com_iterator*)iter->data;
80 
81 	if (I->key == (ulong)-1) {
82 		ZVAL_NULL(key);
83 	} else {
84 		ZVAL_LONG(key, I->key);
85 	}
86 }
87 
com_iter_move_forwards(zend_object_iterator * iter TSRMLS_DC)88 static int com_iter_move_forwards(zend_object_iterator *iter TSRMLS_DC)
89 {
90 	struct php_com_iterator *I = (struct php_com_iterator*)iter->data;
91 	unsigned long n_fetched;
92 	zval *ptr;
93 
94 	/* release current cached element */
95 	VariantClear(&I->v);
96 
97 	if (I->zdata) {
98 		zval_ptr_dtor((zval**)&I->zdata);
99 		I->zdata = NULL;
100 	}
101 
102 	if (I->ev) {
103 		/* Get the next element */
104 		if (SUCCEEDED(IEnumVARIANT_Next(I->ev, 1, &I->v, &n_fetched)) && n_fetched > 0) {
105 			I->key++;
106 		} else {
107 			/* indicate that there are no more items */
108 			I->key = (ulong)-1;
109 			return FAILURE;
110 		}
111 	} else {
112 		/* safe array */
113 		if (I->key >= (ULONG) I->sa_max) {
114 			I->key = (ulong)-1;
115 			return FAILURE;
116 		}
117 		I->key++;
118 		if (php_com_safearray_get_elem(&I->safe_array, &I->v, (LONG)I->key TSRMLS_CC) == 0) {
119 			I->key = (ulong)-1;
120 			return FAILURE;
121 		}
122 	}
123 
124 	MAKE_STD_ZVAL(ptr);
125 	php_com_zval_from_variant(ptr, &I->v, I->code_page TSRMLS_CC);
126 	/* php_com_wrap_variant(ptr, &I->v, I->code_page TSRMLS_CC); */
127 	I->zdata = ptr;
128 	return SUCCESS;
129 }
130 
131 
132 static zend_object_iterator_funcs com_iter_funcs = {
133 	com_iter_dtor,
134 	com_iter_valid,
135 	com_iter_get_data,
136 	com_iter_get_key,
137 	com_iter_move_forwards,
138 	NULL
139 };
140 
php_com_iter_get(zend_class_entry * ce,zval * object,int by_ref TSRMLS_DC)141 zend_object_iterator *php_com_iter_get(zend_class_entry *ce, zval *object, int by_ref TSRMLS_DC)
142 {
143 	php_com_dotnet_object *obj;
144 	struct php_com_iterator *I;
145 	IEnumVARIANT *iev = NULL;
146 	DISPPARAMS dp;
147 	VARIANT v;
148 	unsigned long n_fetched;
149 	zval *ptr;
150 
151 	if (by_ref) {
152 		zend_error(E_ERROR, "An iterator cannot be used with foreach by reference");
153 	}
154 
155 	obj = CDNO_FETCH(object);
156 
157 	if (V_VT(&obj->v) != VT_DISPATCH && !V_ISARRAY(&obj->v)) {
158 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "variant is not an object or array VT=%d", V_VT(&obj->v));
159 		return NULL;
160 	}
161 
162 	memset(&dp, 0, sizeof(dp));
163 	VariantInit(&v);
164 
165 	I = (struct php_com_iterator*)ecalloc(1, sizeof(*I));
166 	I->iter.funcs = &com_iter_funcs;
167 	I->iter.data = I;
168 	I->code_page = obj->code_page;
169 	I->zdata = NULL;
170 	VariantInit(&I->safe_array);
171 	VariantInit(&I->v);
172 
173 	if (V_ISARRAY(&obj->v)) {
174 		LONG bound;
175 		UINT dims;
176 
177 		dims = SafeArrayGetDim(V_ARRAY(&obj->v));
178 
179 		if (dims != 1) {
180 			php_error_docref(NULL TSRMLS_CC, E_WARNING,
181 				   "Can only handle single dimension variant arrays (this array has %d)", dims);
182 			goto fail;
183 		}
184 
185 		/* same semantics as foreach on a PHP array;
186 		 * make a copy and enumerate that copy */
187 		VariantCopy(&I->safe_array, &obj->v);
188 
189 		/* determine the key value for the array */
190 		SafeArrayGetLBound(V_ARRAY(&I->safe_array), 1, &bound);
191 		SafeArrayGetUBound(V_ARRAY(&I->safe_array), 1, &I->sa_max);
192 
193 		/* pre-fetch the element */
194 		if (php_com_safearray_get_elem(&I->safe_array, &I->v, bound TSRMLS_CC)) {
195 			I->key = bound;
196 			MAKE_STD_ZVAL(ptr);
197 			php_com_zval_from_variant(ptr, &I->v, I->code_page TSRMLS_CC);
198 			I->zdata = ptr;
199 		} else {
200 			I->key = (ulong)-1;
201 		}
202 
203 	} else {
204 		/* can we enumerate it? */
205 		if (FAILED(IDispatch_Invoke(V_DISPATCH(&obj->v), DISPID_NEWENUM,
206 						&IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_METHOD|DISPATCH_PROPERTYGET,
207 						&dp, &v, NULL, NULL))) {
208 			goto fail;
209 		}
210 
211 		/* get something useful out of it */
212 		if (V_VT(&v) == VT_UNKNOWN) {
213 			IUnknown_QueryInterface(V_UNKNOWN(&v), &IID_IEnumVARIANT, (void**)&iev);
214 		} else if (V_VT(&v) == VT_DISPATCH) {
215 			IDispatch_QueryInterface(V_DISPATCH(&v), &IID_IEnumVARIANT, (void**)&iev);
216 		}
217 
218 		VariantClear(&v);
219 
220 		if (iev == NULL) {
221 			goto fail;
222 		}
223 
224 		I->ev = iev;
225 
226 		/* Get the first element now */
227 		if (SUCCEEDED(IEnumVARIANT_Next(I->ev, 1, &I->v, &n_fetched)) && n_fetched > 0) {
228 			/* indicate that we have element 0 */
229 			I->key = 0;
230 			MAKE_STD_ZVAL(ptr);
231 			php_com_zval_from_variant(ptr, &I->v, I->code_page TSRMLS_CC);
232 			I->zdata = ptr;
233 		} else {
234 			/* indicate that there are no more items */
235 			I->key = (ulong)-1;
236 		}
237 	}
238 
239 	return &I->iter;
240 
241 fail:
242 	if (I) {
243 		VariantClear(&I->safe_array);
244 		VariantClear(&I->v);
245 		efree(I);
246 	}
247 	return NULL;
248 }
249 
250