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