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