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