com_saproxy.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560
  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. /* This module implements a SafeArray proxy which is used internally
  17. * by the engine when resolving multi-dimensional array accesses on
  18. * SafeArray types.
  19. * In addition, the proxy is now able to handle properties of COM objects
  20. * that smell like PHP arrays.
  21. * */
  22. #ifdef HAVE_CONFIG_H
  23. #include "config.h"
  24. #endif
  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. typedef struct {
  32. zend_object std;
  33. /* the object we a proxying for; we hold a refcount to it */
  34. php_com_dotnet_object *obj;
  35. /* how many dimensions we are indirecting to get into this element */
  36. LONG dimensions;
  37. /* this is an array whose size_is(dimensions) */
  38. zval *indices;
  39. } php_com_saproxy;
  40. typedef struct {
  41. zend_object_iterator iter;
  42. zval proxy_obj;
  43. zval data;
  44. php_com_saproxy *proxy;
  45. LONG key;
  46. LONG imin, imax;
  47. LONG *indices;
  48. } php_com_saproxy_iter;
  49. #define SA_FETCH(zv) (php_com_saproxy*)Z_OBJ_P(zv)
  50. static inline void clone_indices(php_com_saproxy *dest, php_com_saproxy *src, int ndims)
  51. {
  52. int i;
  53. for (i = 0; i < ndims; i++) {
  54. ZVAL_DUP(&dest->indices[i], &src->indices[i]);
  55. }
  56. }
  57. static zval *saproxy_property_read(zend_object *object, zend_string *member, int type, void **cache_slot, zval *rv)
  58. {
  59. ZVAL_NULL(rv);
  60. php_com_throw_exception(E_INVALIDARG, "safearray has no properties");
  61. return rv;
  62. }
  63. static zval *saproxy_property_write(zend_object *object, zend_string *member, zval *value, void **cache_slot)
  64. {
  65. php_com_throw_exception(E_INVALIDARG, "safearray has no properties");
  66. return value;
  67. }
  68. static zval *saproxy_read_dimension(zend_object *object, zval *offset, int type, zval *rv)
  69. {
  70. php_com_saproxy *proxy = (php_com_saproxy*) object;
  71. UINT dims, i;
  72. SAFEARRAY *sa;
  73. LONG ubound, lbound;
  74. HRESULT res;
  75. ZVAL_NULL(rv);
  76. if (V_VT(&proxy->obj->v) == VT_DISPATCH) {
  77. VARIANT v;
  78. zval *args;
  79. /* prop-get using first dimension as the property name,
  80. * all subsequent dimensions and the offset as parameters */
  81. args = safe_emalloc(proxy->dimensions + 1, sizeof(zval), 0);
  82. for (i = 1; i < (UINT) proxy->dimensions; i++) {
  83. args[i-1] = proxy->indices[i];
  84. }
  85. ZVAL_COPY_VALUE(&args[i-1], offset);
  86. if (!try_convert_to_string(&proxy->indices[0])) {
  87. efree(args);
  88. return rv;
  89. }
  90. VariantInit(&v);
  91. res = php_com_do_invoke(proxy->obj, Z_STRVAL(proxy->indices[0]),
  92. Z_STRLEN(proxy->indices[0]), DISPATCH_METHOD|DISPATCH_PROPERTYGET, &v,
  93. proxy->dimensions, args, 0);
  94. efree(args);
  95. if (res == SUCCESS) {
  96. php_com_zval_from_variant(rv, &v, proxy->obj->code_page);
  97. VariantClear(&v);
  98. } else if (res == DISP_E_BADPARAMCOUNT) {
  99. /* return another proxy */
  100. php_com_saproxy_create(object, rv, offset);
  101. }
  102. return rv;
  103. } else if (!V_ISARRAY(&proxy->obj->v)) {
  104. php_com_throw_exception(E_INVALIDARG, "invalid read from com proxy object");
  105. return rv;
  106. }
  107. /* the SafeArray case */
  108. /* offset/index must be an integer */
  109. convert_to_long(offset);
  110. sa = V_ARRAY(&proxy->obj->v);
  111. dims = SafeArrayGetDim(sa);
  112. if ((UINT) proxy->dimensions >= dims) {
  113. /* too many dimensions */
  114. php_com_throw_exception(E_INVALIDARG, "too many dimensions!");
  115. return rv;
  116. }
  117. /* bounds check */
  118. SafeArrayGetLBound(sa, proxy->dimensions, &lbound);
  119. SafeArrayGetUBound(sa, proxy->dimensions, &ubound);
  120. if (Z_LVAL_P(offset) < lbound || Z_LVAL_P(offset) > ubound) {
  121. php_com_throw_exception(DISP_E_BADINDEX, "index out of bounds");
  122. return rv;
  123. }
  124. if (dims - 1 == proxy->dimensions) {
  125. LONG *indices;
  126. VARTYPE vt;
  127. VARIANT v;
  128. VariantInit(&v);
  129. /* we can return a real value */
  130. indices = safe_emalloc(dims, sizeof(LONG), 0);
  131. /* copy indices from proxy */
  132. for (i = 0; i < dims; i++) {
  133. convert_to_long(&proxy->indices[i]);
  134. indices[i] = (LONG)Z_LVAL(proxy->indices[i]);
  135. }
  136. /* add user-supplied index */
  137. indices[dims-1] = (LONG)Z_LVAL_P(offset);
  138. /* now fetch the value */
  139. if (FAILED(SafeArrayGetVartype(sa, &vt)) || vt == VT_EMPTY) {
  140. vt = V_VT(&proxy->obj->v) & ~VT_ARRAY;
  141. }
  142. if (vt == VT_VARIANT) {
  143. res = SafeArrayGetElement(sa, indices, &v);
  144. } else {
  145. V_VT(&v) = vt;
  146. res = SafeArrayGetElement(sa, indices, &v.lVal);
  147. }
  148. efree(indices);
  149. if (SUCCEEDED(res)) {
  150. php_com_wrap_variant(rv, &v, proxy->obj->code_page);
  151. } else {
  152. php_com_throw_exception(res, NULL);
  153. }
  154. VariantClear(&v);
  155. } else {
  156. /* return another proxy */
  157. php_com_saproxy_create(object, rv, offset);
  158. }
  159. return rv;
  160. }
  161. static void saproxy_write_dimension(zend_object *object, zval *offset, zval *value)
  162. {
  163. php_com_saproxy *proxy = (php_com_saproxy*) object;
  164. UINT dims, i;
  165. HRESULT res;
  166. VARIANT v;
  167. if (V_VT(&proxy->obj->v) == VT_DISPATCH) {
  168. /* We do a prop-set using the first dimension as the property name,
  169. * all subsequent dimensions and offset as parameters, with value as
  170. * the final value */
  171. zval *args = safe_emalloc(proxy->dimensions + 2, sizeof(zval), 0);
  172. for (i = 1; i < (UINT) proxy->dimensions; i++) {
  173. ZVAL_COPY_VALUE(&args[i-1], &proxy->indices[i]);
  174. }
  175. ZVAL_COPY_VALUE(&args[i-1], offset);
  176. ZVAL_COPY_VALUE(&args[i], value);
  177. if (!try_convert_to_string(&proxy->indices[0])) {
  178. efree(args);
  179. return;
  180. }
  181. VariantInit(&v);
  182. if (SUCCESS == php_com_do_invoke(proxy->obj, Z_STRVAL(proxy->indices[0]),
  183. Z_STRLEN(proxy->indices[0]), DISPATCH_PROPERTYPUT, &v, proxy->dimensions + 1,
  184. args, 0)) {
  185. VariantClear(&v);
  186. }
  187. efree(args);
  188. } else if (V_ISARRAY(&proxy->obj->v)) {
  189. LONG *indices;
  190. VARTYPE vt;
  191. dims = SafeArrayGetDim(V_ARRAY(&proxy->obj->v));
  192. indices = safe_emalloc(dims, sizeof(LONG), 0);
  193. /* copy indices from proxy */
  194. for (i = 0; i < dims; i++) {
  195. convert_to_long(&proxy->indices[i]);
  196. indices[i] = (LONG)Z_LVAL(proxy->indices[i]);
  197. }
  198. /* add user-supplied index */
  199. convert_to_long(offset);
  200. indices[dims-1] = (LONG)Z_LVAL_P(offset);
  201. if (FAILED(SafeArrayGetVartype(V_ARRAY(&proxy->obj->v), &vt)) || vt == VT_EMPTY) {
  202. vt = V_VT(&proxy->obj->v) & ~VT_ARRAY;
  203. }
  204. VariantInit(&v);
  205. php_com_variant_from_zval(&v, value, proxy->obj->code_page);
  206. if (V_VT(&v) != vt) {
  207. VariantChangeType(&v, &v, 0, vt);
  208. }
  209. if (vt == VT_VARIANT) {
  210. res = SafeArrayPutElement(V_ARRAY(&proxy->obj->v), indices, &v);
  211. } else {
  212. res = SafeArrayPutElement(V_ARRAY(&proxy->obj->v), indices, &v.lVal);
  213. }
  214. efree(indices);
  215. VariantClear(&v);
  216. if (FAILED(res)) {
  217. php_com_throw_exception(res, NULL);
  218. }
  219. } else {
  220. php_com_throw_exception(E_NOTIMPL, "invalid write to com proxy object");
  221. }
  222. }
  223. static int saproxy_property_exists(zend_object *object, zend_string *member, int check_empty, void **cache_slot)
  224. {
  225. /* no properties */
  226. return 0;
  227. }
  228. static int saproxy_dimension_exists(zend_object *object, zval *member, int check_empty)
  229. {
  230. /* TODO Add support */
  231. zend_throw_error(NULL, "Cannot check dimension on a COM object");
  232. return 0;
  233. }
  234. static void saproxy_property_delete(zend_object *object, zend_string *member, void **cache_slot)
  235. {
  236. zend_throw_error(NULL, "Cannot delete properties from a COM object");
  237. }
  238. static void saproxy_dimension_delete(zend_object *object, zval *offset)
  239. {
  240. zend_throw_error(NULL, "Cannot delete dimension from a COM object");
  241. }
  242. static HashTable *saproxy_properties_get(zend_object *object)
  243. {
  244. /* no properties */
  245. return NULL;
  246. }
  247. static zend_function *saproxy_method_get(zend_object **object, zend_string *name, const zval *key)
  248. {
  249. /* no methods */
  250. return NULL;
  251. }
  252. static zend_function *saproxy_constructor_get(zend_object *object)
  253. {
  254. /* user cannot instantiate */
  255. return NULL;
  256. }
  257. static zend_string* saproxy_class_name_get(const zend_object *object)
  258. {
  259. return zend_string_copy(php_com_saproxy_class_entry->name);
  260. }
  261. static int saproxy_objects_compare(zval *object1, zval *object2)
  262. {
  263. ZEND_COMPARE_OBJECTS_FALLBACK(object1, object2);
  264. return -1;
  265. }
  266. static int saproxy_object_cast(zend_object *readobj, zval *writeobj, int type)
  267. {
  268. return FAILURE;
  269. }
  270. static int saproxy_count_elements(zend_object *object, zend_long *count)
  271. {
  272. php_com_saproxy *proxy = (php_com_saproxy*) object;
  273. LONG ubound, lbound;
  274. if (!V_ISARRAY(&proxy->obj->v)) {
  275. return FAILURE;
  276. }
  277. SafeArrayGetLBound(V_ARRAY(&proxy->obj->v), proxy->dimensions, &lbound);
  278. SafeArrayGetUBound(V_ARRAY(&proxy->obj->v), proxy->dimensions, &ubound);
  279. *count = ubound - lbound + 1;
  280. return SUCCESS;
  281. }
  282. static void saproxy_free_storage(zend_object *object)
  283. {
  284. php_com_saproxy *proxy = (php_com_saproxy *)object;
  285. //??? int i;
  286. //???
  287. //??? for (i = 0; i < proxy->dimensions; i++) {
  288. //??? if (proxy->indices) {
  289. //??? FREE_ZVAL(proxy->indices[i]);
  290. //??? }
  291. //??? }
  292. OBJ_RELEASE(&proxy->obj->zo);
  293. zend_object_std_dtor(object);
  294. efree(proxy->indices);
  295. }
  296. static zend_object* saproxy_clone(zend_object *object)
  297. {
  298. php_com_saproxy *proxy = (php_com_saproxy *) object;
  299. php_com_saproxy *cloneproxy;
  300. cloneproxy = emalloc(sizeof(*cloneproxy));
  301. memcpy(cloneproxy, proxy, sizeof(*cloneproxy));
  302. GC_ADDREF(&cloneproxy->obj->zo);
  303. cloneproxy->indices = safe_emalloc(cloneproxy->dimensions, sizeof(zval), 0);
  304. clone_indices(cloneproxy, proxy, proxy->dimensions);
  305. return &cloneproxy->std;
  306. }
  307. zend_object_handlers php_com_saproxy_handlers = {
  308. 0,
  309. saproxy_free_storage,
  310. zend_objects_destroy_object,
  311. saproxy_clone,
  312. saproxy_property_read,
  313. saproxy_property_write,
  314. saproxy_read_dimension,
  315. saproxy_write_dimension,
  316. NULL,
  317. saproxy_property_exists,
  318. saproxy_property_delete,
  319. saproxy_dimension_exists,
  320. saproxy_dimension_delete,
  321. saproxy_properties_get,
  322. saproxy_method_get,
  323. saproxy_constructor_get,
  324. saproxy_class_name_get,
  325. saproxy_object_cast,
  326. saproxy_count_elements,
  327. NULL, /* get_debug_info */
  328. NULL, /* get_closure */
  329. NULL, /* get_gc */
  330. NULL, /* do_operation */
  331. saproxy_objects_compare, /* compare */
  332. NULL, /* get_properties_for */
  333. };
  334. int php_com_saproxy_create(zend_object *com_object, zval *proxy_out, zval *index)
  335. {
  336. php_com_saproxy *proxy, *rel = NULL;
  337. proxy = ecalloc(1, sizeof(*proxy));
  338. proxy->dimensions = 1;
  339. if (com_object->ce == php_com_saproxy_class_entry) {
  340. rel = (php_com_saproxy*) com_object;
  341. proxy->obj = rel->obj;
  342. proxy->dimensions += rel->dimensions;
  343. } else {
  344. proxy->obj = (php_com_dotnet_object*) com_object;
  345. }
  346. GC_ADDREF(&proxy->obj->zo);
  347. proxy->indices = safe_emalloc(proxy->dimensions, sizeof(zval), 0);
  348. if (rel) {
  349. clone_indices(proxy, rel, rel->dimensions);
  350. }
  351. ZVAL_DUP(&proxy->indices[proxy->dimensions-1], index);
  352. zend_object_std_init(&proxy->std, php_com_saproxy_class_entry);
  353. proxy->std.handlers = &php_com_saproxy_handlers;
  354. ZVAL_OBJ(proxy_out, &proxy->std);
  355. return 1;
  356. }
  357. /* iterator */
  358. static void saproxy_iter_dtor(zend_object_iterator *iter)
  359. {
  360. php_com_saproxy_iter *I = (php_com_saproxy_iter*)Z_PTR(iter->data);
  361. zval_ptr_dtor(&I->proxy_obj);
  362. efree(I->indices);
  363. efree(I);
  364. }
  365. static int saproxy_iter_valid(zend_object_iterator *iter)
  366. {
  367. php_com_saproxy_iter *I = (php_com_saproxy_iter*)Z_PTR(iter->data);
  368. return (I->key < I->imax) ? SUCCESS : FAILURE;
  369. }
  370. static zval* saproxy_iter_get_data(zend_object_iterator *iter)
  371. {
  372. php_com_saproxy_iter *I = (php_com_saproxy_iter*)Z_PTR(iter->data);
  373. VARIANT v;
  374. VARTYPE vt;
  375. SAFEARRAY *sa;
  376. I->indices[I->proxy->dimensions-1] = I->key;
  377. sa = V_ARRAY(&I->proxy->obj->v);
  378. if (FAILED(SafeArrayGetVartype(sa, &vt)) || vt == VT_EMPTY) {
  379. vt = V_VT(&I->proxy->obj->v) & ~VT_ARRAY;
  380. }
  381. VariantInit(&v);
  382. if (vt == VT_VARIANT) {
  383. SafeArrayGetElement(sa, I->indices, &v);
  384. } else {
  385. V_VT(&v) = vt;
  386. SafeArrayGetElement(sa, I->indices, &v.lVal);
  387. }
  388. ZVAL_NULL(&I->data);
  389. php_com_wrap_variant(&I->data, &v, I->proxy->obj->code_page);
  390. VariantClear(&v);
  391. return &I->data;
  392. }
  393. static void saproxy_iter_get_key(zend_object_iterator *iter, zval *key)
  394. {
  395. php_com_saproxy_iter *I = (php_com_saproxy_iter*)Z_PTR(iter->data);
  396. if (I->key == -1) {
  397. ZVAL_NULL(key);
  398. } else {
  399. ZVAL_LONG(key, I->key);
  400. }
  401. }
  402. static void saproxy_iter_move_forwards(zend_object_iterator *iter)
  403. {
  404. php_com_saproxy_iter *I = (php_com_saproxy_iter*)Z_PTR(iter->data);
  405. if (++I->key >= I->imax) {
  406. I->key = -1;
  407. }
  408. }
  409. static const zend_object_iterator_funcs saproxy_iter_funcs = {
  410. saproxy_iter_dtor,
  411. saproxy_iter_valid,
  412. saproxy_iter_get_data,
  413. saproxy_iter_get_key,
  414. saproxy_iter_move_forwards,
  415. NULL,
  416. NULL, /* get_gc */
  417. };
  418. zend_object_iterator *php_com_saproxy_iter_get(zend_class_entry *ce, zval *object, int by_ref)
  419. {
  420. php_com_saproxy *proxy = SA_FETCH(object);
  421. php_com_saproxy_iter *I;
  422. int i;
  423. if (by_ref) {
  424. zend_throw_error(NULL, "An iterator cannot be used with foreach by reference");
  425. return NULL;
  426. }
  427. I = ecalloc(1, sizeof(*I));
  428. I->iter.funcs = &saproxy_iter_funcs;
  429. Z_PTR(I->iter.data) = I;
  430. I->proxy = proxy;
  431. Z_ADDREF_P(object);
  432. ZVAL_OBJ(&I->proxy_obj, Z_OBJ_P(object));
  433. I->indices = safe_emalloc(proxy->dimensions + 1, sizeof(LONG), 0);
  434. for (i = 0; i < proxy->dimensions; i++) {
  435. convert_to_long(&proxy->indices[i]);
  436. I->indices[i] = (LONG)Z_LVAL(proxy->indices[i]);
  437. }
  438. SafeArrayGetLBound(V_ARRAY(&proxy->obj->v), proxy->dimensions, &I->imin);
  439. SafeArrayGetUBound(V_ARRAY(&proxy->obj->v), proxy->dimensions, &I->imax);
  440. I->key = I->imin;
  441. return &I->iter;
  442. }