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