com_saproxy.c 15 KB

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