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