com_iterator.c 6.3 KB

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