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