com_variant.c 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083
  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. #ifdef HAVE_CONFIG_H
  17. #include "config.h"
  18. #endif
  19. #include "php.h"
  20. #include "php_ini.h"
  21. #include "ext/standard/info.h"
  22. #include "php_com_dotnet.h"
  23. #include "php_com_dotnet_internal.h"
  24. /* create an automation SafeArray from a PHP array.
  25. * Only creates a single-dimensional array of variants.
  26. * The keys of the PHP hash MUST be numeric. If the array
  27. * is sparse, then the gaps will be filled with NULL variants */
  28. static void safe_array_from_zval(VARIANT *v, zval *z, int codepage)
  29. {
  30. SAFEARRAY *sa = NULL;
  31. SAFEARRAYBOUND bound;
  32. HashPosition pos;
  33. int keytype;
  34. zend_string *strindex;
  35. zend_ulong intindex = 0;
  36. VARIANT *va;
  37. zval *item;
  38. /* find the largest array index, and assert that all keys are integers */
  39. zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(z), &pos);
  40. for (;; zend_hash_move_forward_ex(Z_ARRVAL_P(z), &pos)) {
  41. keytype = zend_hash_get_current_key_ex(Z_ARRVAL_P(z), &strindex, &intindex, &pos);
  42. if (HASH_KEY_IS_STRING == keytype) {
  43. goto bogus;
  44. } else if (HASH_KEY_NON_EXISTENT == keytype) {
  45. break;
  46. } else if (intindex > UINT_MAX) {
  47. php_error_docref(NULL, E_WARNING, "COM: max number %u of elements in safe array exceeded", UINT_MAX);
  48. break;
  49. }
  50. }
  51. /* allocate the structure */
  52. bound.lLbound = 0;
  53. bound.cElements = zend_hash_num_elements(Z_ARRVAL_P(z));
  54. sa = SafeArrayCreate(VT_VARIANT, 1, &bound);
  55. /* get a lock on the array itself */
  56. SafeArrayAccessData(sa, &va);
  57. va = (VARIANT*)sa->pvData;
  58. /* now fill it in */
  59. zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(z), &pos);
  60. for (;; zend_hash_move_forward_ex(Z_ARRVAL_P(z), &pos)) {
  61. if (NULL == (item = zend_hash_get_current_data_ex(Z_ARRVAL_P(z), &pos))) {
  62. break;
  63. }
  64. zend_hash_get_current_key_ex(Z_ARRVAL_P(z), &strindex, &intindex, &pos);
  65. php_com_variant_from_zval(&va[intindex], item, codepage);
  66. }
  67. /* Unlock it and stuff it into our variant */
  68. SafeArrayUnaccessData(sa);
  69. V_VT(v) = VT_ARRAY|VT_VARIANT;
  70. V_ARRAY(v) = sa;
  71. return;
  72. bogus:
  73. php_error_docref(NULL, E_WARNING, "COM: converting from PHP array to VARIANT array; only arrays with numeric keys are allowed");
  74. V_VT(v) = VT_NULL;
  75. if (sa) {
  76. SafeArrayUnlock(sa);
  77. SafeArrayDestroy(sa);
  78. }
  79. }
  80. PHP_COM_DOTNET_API void php_com_variant_from_zval(VARIANT *v, zval *z, int codepage)
  81. {
  82. php_com_dotnet_object *obj;
  83. zend_uchar ztype = IS_NULL;
  84. if (z) {
  85. ZVAL_DEREF(z);
  86. ztype = Z_TYPE_P(z);
  87. }
  88. switch (ztype) {
  89. case IS_NULL:
  90. V_VT(v) = VT_NULL;
  91. break;
  92. case IS_FALSE:
  93. V_VT(v) = VT_BOOL;
  94. V_BOOL(v) = VARIANT_FALSE;
  95. break;
  96. case IS_TRUE:
  97. V_VT(v) = VT_BOOL;
  98. V_BOOL(v) = VARIANT_TRUE;
  99. break;
  100. case IS_OBJECT:
  101. if (php_com_is_valid_object(z)) {
  102. obj = CDNO_FETCH(z);
  103. if (V_VT(&obj->v) == VT_DISPATCH) {
  104. /* pass the underlying object */
  105. V_VT(v) = VT_DISPATCH;
  106. if (V_DISPATCH(&obj->v)) {
  107. IDispatch_AddRef(V_DISPATCH(&obj->v));
  108. }
  109. V_DISPATCH(v) = V_DISPATCH(&obj->v);
  110. } else {
  111. /* pass the variant by reference */
  112. V_VT(v) = VT_VARIANT | VT_BYREF;
  113. V_VARIANTREF(v) = &obj->v;
  114. }
  115. } else {
  116. /* export the PHP object using our COM wrapper */
  117. V_VT(v) = VT_DISPATCH;
  118. V_DISPATCH(v) = php_com_wrapper_export(z);
  119. }
  120. break;
  121. case IS_ARRAY:
  122. /* map as safe array */
  123. safe_array_from_zval(v, z, codepage);
  124. break;
  125. case IS_LONG:
  126. #if SIZEOF_ZEND_LONG == 4
  127. V_VT(v) = VT_I4;
  128. V_I4(v) = Z_LVAL_P(z);
  129. #else
  130. V_VT(v) = VT_I8;
  131. V_I8(v) = Z_LVAL_P(z);
  132. #endif
  133. break;
  134. case IS_DOUBLE:
  135. V_VT(v) = VT_R8;
  136. V_R8(v) = Z_DVAL_P(z);
  137. break;
  138. case IS_STRING:
  139. V_VT(v) = VT_BSTR;
  140. V_BSTR(v) = php_com_string_to_bstr(Z_STR_P(z), codepage);
  141. break;
  142. case IS_RESOURCE:
  143. case IS_CONSTANT_AST:
  144. default:
  145. V_VT(v) = VT_NULL;
  146. break;
  147. }
  148. }
  149. PHP_COM_DOTNET_API int php_com_zval_from_variant(zval *z, VARIANT *v, int codepage)
  150. {
  151. OLECHAR *olestring = NULL;
  152. int ret = SUCCESS;
  153. switch (V_VT(v)) {
  154. case VT_EMPTY:
  155. case VT_NULL:
  156. case VT_VOID:
  157. ZVAL_NULL(z);
  158. break;
  159. case VT_UI1:
  160. ZVAL_LONG(z, (zend_long)V_UI1(v));
  161. break;
  162. case VT_I1:
  163. ZVAL_LONG(z, (zend_long)V_I1(v));
  164. break;
  165. case VT_UI2:
  166. ZVAL_LONG(z, (zend_long)V_UI2(v));
  167. break;
  168. case VT_I2:
  169. ZVAL_LONG(z, (zend_long)V_I2(v));
  170. break;
  171. case VT_UI4: /* TODO: promote to double if large? */
  172. ZVAL_LONG(z, (long)V_UI4(v));
  173. break;
  174. case VT_I4:
  175. ZVAL_LONG(z, (long)V_I4(v));
  176. break;
  177. #if SIZEOF_ZEND_LONG == 8
  178. case VT_UI8:
  179. ZVAL_LONG(z, (zend_long)V_UI8(v));
  180. break;
  181. case VT_I8:
  182. ZVAL_LONG(z, (zend_long)V_I8(v));
  183. break;
  184. #endif
  185. case VT_INT:
  186. ZVAL_LONG(z, V_INT(v));
  187. break;
  188. case VT_UINT: /* TODO: promote to double if large? */
  189. ZVAL_LONG(z, (zend_long)V_UINT(v));
  190. break;
  191. case VT_R4:
  192. ZVAL_DOUBLE(z, (double)V_R4(v));
  193. break;
  194. case VT_R8:
  195. ZVAL_DOUBLE(z, V_R8(v));
  196. break;
  197. case VT_BOOL:
  198. ZVAL_BOOL(z, V_BOOL(v) ? 1 : 0);
  199. break;
  200. case VT_BSTR:
  201. olestring = V_BSTR(v);
  202. if (olestring) {
  203. zend_string *str = php_com_bstr_to_string(olestring, codepage);
  204. ZVAL_STR(z, str);
  205. olestring = NULL;
  206. }
  207. break;
  208. case VT_UNKNOWN:
  209. if (V_UNKNOWN(v) != NULL) {
  210. IDispatch *disp;
  211. if (SUCCEEDED(IUnknown_QueryInterface(V_UNKNOWN(v), &IID_IDispatch, &disp))) {
  212. php_com_wrap_dispatch(z, disp, codepage);
  213. IDispatch_Release(disp);
  214. } else {
  215. ret = FAILURE;
  216. }
  217. }
  218. break;
  219. case VT_DISPATCH:
  220. if (V_DISPATCH(v) != NULL) {
  221. php_com_wrap_dispatch(z, V_DISPATCH(v), codepage);
  222. }
  223. break;
  224. case VT_VARIANT:
  225. /* points to another variant */
  226. return php_com_zval_from_variant(z, V_VARIANTREF(v), codepage);
  227. default:
  228. php_com_wrap_variant(z, v, codepage);
  229. }
  230. if (olestring) {
  231. efree(olestring);
  232. }
  233. if (ret == FAILURE) {
  234. php_error_docref(NULL, E_WARNING, "variant->zval: conversion from 0x%x ret=%d", V_VT(v), ret);
  235. }
  236. return ret;
  237. }
  238. PHP_COM_DOTNET_API int php_com_copy_variant(VARIANT *dstvar, VARIANT *srcvar)
  239. {
  240. int ret = SUCCESS;
  241. switch (V_VT(dstvar) & ~VT_BYREF) {
  242. case VT_EMPTY:
  243. case VT_NULL:
  244. case VT_VOID:
  245. /* should not be possible */
  246. break;
  247. case VT_UI1:
  248. if (V_VT(dstvar) & VT_BYREF) {
  249. *V_UI1REF(dstvar) = V_UI1(srcvar);
  250. } else {
  251. V_UI1(dstvar) = V_UI1(srcvar);
  252. }
  253. break;
  254. case VT_I1:
  255. if (V_VT(dstvar) & VT_BYREF) {
  256. *V_I1REF(dstvar) = V_I1(srcvar);
  257. } else {
  258. V_I1(dstvar) = V_I1(srcvar);
  259. }
  260. break;
  261. case VT_UI2:
  262. if (V_VT(dstvar) & VT_BYREF) {
  263. *V_UI2REF(dstvar) = V_UI2(srcvar);
  264. } else {
  265. V_UI2(dstvar) = V_UI2(srcvar);
  266. }
  267. break;
  268. case VT_I2:
  269. if (V_VT(dstvar) & VT_BYREF) {
  270. *V_I2REF(dstvar) = V_I2(srcvar);
  271. } else {
  272. V_I2(dstvar) = V_I2(srcvar);
  273. }
  274. break;
  275. case VT_UI4:
  276. if (V_VT(dstvar) & VT_BYREF) {
  277. *V_UI4REF(dstvar) = V_UI4(srcvar);
  278. } else {
  279. V_UI4(dstvar) = V_UI4(srcvar);
  280. }
  281. break;
  282. case VT_I4:
  283. if (V_VT(dstvar) & VT_BYREF) {
  284. *V_I4REF(dstvar) = V_I4(srcvar);
  285. } else {
  286. V_I4(dstvar) = V_I4(srcvar);
  287. }
  288. break;
  289. #if SIZEOF_ZEND_LONG == 8
  290. case VT_UI8:
  291. if (V_VT(dstvar) & VT_BYREF) {
  292. *V_UI8REF(dstvar) = V_UI8(srcvar);
  293. } else {
  294. V_UI8(dstvar) = V_UI8(srcvar);
  295. }
  296. break;
  297. case VT_I8:
  298. if (V_VT(dstvar) & VT_BYREF) {
  299. *V_I8REF(dstvar) = V_I8(srcvar);
  300. } else {
  301. V_I8(dstvar) = V_I8(srcvar);
  302. }
  303. break;
  304. #endif
  305. case VT_INT:
  306. if (V_VT(dstvar) & VT_BYREF) {
  307. *V_INTREF(dstvar) = V_INT(srcvar);
  308. } else {
  309. V_INT(dstvar) = V_INT(srcvar);
  310. }
  311. break;
  312. case VT_UINT:
  313. if (V_VT(dstvar) & VT_BYREF) {
  314. *V_UINTREF(dstvar) = V_UINT(srcvar);
  315. } else {
  316. V_UINT(dstvar) = V_UINT(srcvar);
  317. }
  318. break;
  319. case VT_R4:
  320. if (V_VT(dstvar) & VT_BYREF) {
  321. *V_R4REF(dstvar) = V_R4(srcvar);
  322. } else {
  323. V_R4(dstvar) = V_R4(srcvar);
  324. }
  325. break;
  326. case VT_R8:
  327. if (V_VT(dstvar) & VT_BYREF) {
  328. *V_R8REF(dstvar) = V_R8(srcvar);
  329. } else {
  330. V_R8(dstvar) = V_R8(srcvar);
  331. }
  332. break;
  333. case VT_BOOL:
  334. if (V_VT(dstvar) & VT_BYREF) {
  335. *V_BOOLREF(dstvar) = V_BOOL(srcvar);
  336. } else {
  337. V_BOOL(dstvar) = V_BOOL(srcvar);
  338. }
  339. break;
  340. case VT_BSTR:
  341. if (V_VT(dstvar) & VT_BYREF) {
  342. *V_BSTRREF(dstvar) = V_BSTR(srcvar);
  343. } else {
  344. V_BSTR(dstvar) = V_BSTR(srcvar);
  345. }
  346. break;
  347. case VT_UNKNOWN:
  348. if (V_VT(dstvar) & VT_BYREF) {
  349. *V_UNKNOWNREF(dstvar) = V_UNKNOWN(srcvar);
  350. } else {
  351. V_UNKNOWN(dstvar) = V_UNKNOWN(srcvar);
  352. }
  353. break;
  354. case VT_DISPATCH:
  355. if (V_VT(dstvar) & VT_BYREF) {
  356. *V_DISPATCHREF(dstvar) = V_DISPATCH(srcvar);
  357. } else {
  358. V_DISPATCH(dstvar) = V_DISPATCH(srcvar);
  359. }
  360. break;
  361. case VT_VARIANT:
  362. return php_com_copy_variant(V_VARIANTREF(dstvar), srcvar);
  363. default:
  364. php_error_docref(NULL, E_WARNING, "variant->__construct: failed to copy from 0x%x to 0x%x", V_VT(dstvar), V_VT(srcvar));
  365. ret = FAILURE;
  366. }
  367. return ret;
  368. }
  369. /* {{{ com_variant_create_instance - ctor for new VARIANT() */
  370. PHP_METHOD(variant, __construct)
  371. {
  372. /* VARTYPE == unsigned short */ zend_long vt = VT_EMPTY;
  373. zend_long codepage = CP_ACP;
  374. zval *object = getThis();
  375. php_com_dotnet_object *obj;
  376. zval *zvalue = NULL;
  377. HRESULT res;
  378. if (ZEND_NUM_ARGS() == 0) {
  379. /* just leave things as-is - an empty variant */
  380. return;
  381. }
  382. if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(),
  383. "z!|ll", &zvalue, &vt, &codepage)) {
  384. RETURN_THROWS();
  385. }
  386. php_com_initialize();
  387. obj = CDNO_FETCH(object);
  388. if (ZEND_NUM_ARGS() == 3) {
  389. obj->code_page = (int)codepage;
  390. }
  391. if (zvalue) {
  392. php_com_variant_from_zval(&obj->v, zvalue, obj->code_page);
  393. }
  394. /* Only perform conversion if variant not already of type passed */
  395. if ((ZEND_NUM_ARGS() >= 2) && (vt != V_VT(&obj->v))) {
  396. /* If already an array and VT_ARRAY is passed then:
  397. - if only VT_ARRAY passed then do not perform a conversion
  398. - if VT_ARRAY plus other type passed then perform conversion
  399. but will probably fail (original behavior)
  400. */
  401. if ((vt & VT_ARRAY) && (V_VT(&obj->v) & VT_ARRAY)) {
  402. zend_long orig_vt = vt;
  403. vt &= ~VT_ARRAY;
  404. if (vt) {
  405. vt = orig_vt;
  406. }
  407. }
  408. if (vt) {
  409. res = VariantChangeType(&obj->v, &obj->v, 0, (VARTYPE)vt);
  410. if (FAILED(res)) {
  411. char *werr, *msg;
  412. werr = php_win32_error_to_msg(res);
  413. spprintf(&msg, 0, "Variant type conversion failed: %s", werr);
  414. php_win32_error_msg_free(werr);
  415. php_com_throw_exception(res, msg);
  416. efree(msg);
  417. }
  418. }
  419. }
  420. if (V_VT(&obj->v) != VT_DISPATCH && obj->typeinfo) {
  421. ITypeInfo_Release(obj->typeinfo);
  422. obj->typeinfo = NULL;
  423. }
  424. }
  425. /* }}} */
  426. /* {{{ Assigns a new value for a variant object */
  427. PHP_FUNCTION(variant_set)
  428. {
  429. zval *zobj, *zvalue = NULL;
  430. php_com_dotnet_object *obj;
  431. if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(),
  432. "Oz!", &zobj, php_com_variant_class_entry, &zvalue)) {
  433. RETURN_THROWS();
  434. }
  435. obj = CDNO_FETCH(zobj);
  436. /* dtor the old value */
  437. if (obj->typeinfo) {
  438. ITypeInfo_Release(obj->typeinfo);
  439. obj->typeinfo = NULL;
  440. }
  441. if (obj->sink_dispatch) {
  442. php_com_object_enable_event_sink(obj, FALSE);
  443. IDispatch_Release(obj->sink_dispatch);
  444. obj->sink_dispatch = NULL;
  445. }
  446. VariantClear(&obj->v);
  447. php_com_variant_from_zval(&obj->v, zvalue, obj->code_page);
  448. /* remember we modified this variant */
  449. obj->modified = 1;
  450. }
  451. /* }}} */
  452. enum variant_binary_opcode {
  453. VOP_ADD, VOP_CAT, VOP_SUB, VOP_MUL, VOP_AND, VOP_DIV,
  454. VOP_EQV, VOP_IDIV, VOP_IMP, VOP_MOD, VOP_OR, VOP_POW,
  455. VOP_XOR
  456. };
  457. enum variant_unary_opcode {
  458. VOP_ABS, VOP_FIX, VOP_INT, VOP_NEG, VOP_NOT
  459. };
  460. static void variant_binary_operation(enum variant_binary_opcode op, INTERNAL_FUNCTION_PARAMETERS) /* {{{ */
  461. {
  462. VARIANT vres;
  463. VARIANT left_val, right_val;
  464. VARIANT *vleft = NULL, *vright = NULL;
  465. zval *zleft = NULL, *zright = NULL;
  466. php_com_dotnet_object *obj;
  467. HRESULT result;
  468. int codepage = CP_ACP;
  469. VariantInit(&left_val);
  470. VariantInit(&right_val);
  471. VariantInit(&vres);
  472. if (SUCCESS == zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET,
  473. ZEND_NUM_ARGS(), "OO", &zleft, php_com_variant_class_entry,
  474. &zright, php_com_variant_class_entry)) {
  475. obj = CDNO_FETCH(zleft);
  476. vleft = &obj->v;
  477. obj = CDNO_FETCH(zright);
  478. vright = &obj->v;
  479. } else if (SUCCESS == zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET,
  480. ZEND_NUM_ARGS(), "Oz!", &zleft, php_com_variant_class_entry,
  481. &zright)) {
  482. obj = CDNO_FETCH(zleft);
  483. vleft = &obj->v;
  484. vright = &right_val;
  485. php_com_variant_from_zval(vright, zright, codepage);
  486. } else if (SUCCESS == zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET,
  487. ZEND_NUM_ARGS(), "z!O", &zleft, &zright, php_com_variant_class_entry)) {
  488. obj = CDNO_FETCH(zright);
  489. vright = &obj->v;
  490. vleft = &left_val;
  491. php_com_variant_from_zval(vleft, zleft, codepage);
  492. } else if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(),
  493. "z!z!", &zleft, &zright)) {
  494. vleft = &left_val;
  495. php_com_variant_from_zval(vleft, zleft, codepage);
  496. vright = &right_val;
  497. php_com_variant_from_zval(vright, zright, codepage);
  498. } else {
  499. RETURN_THROWS();
  500. }
  501. switch (op) {
  502. case VOP_ADD:
  503. result = VarAdd(vleft, vright, &vres);
  504. break;
  505. case VOP_CAT:
  506. result = VarCat(vleft, vright, &vres);
  507. break;
  508. case VOP_SUB:
  509. result = VarSub(vleft, vright, &vres);
  510. break;
  511. case VOP_MUL:
  512. result = VarMul(vleft, vright, &vres);
  513. break;
  514. case VOP_AND:
  515. result = VarAnd(vleft, vright, &vres);
  516. break;
  517. case VOP_DIV:
  518. result = VarDiv(vleft, vright, &vres);
  519. break;
  520. case VOP_EQV:
  521. result = VarEqv(vleft, vright, &vres);
  522. break;
  523. case VOP_IDIV:
  524. result = VarIdiv(vleft, vright, &vres);
  525. break;
  526. case VOP_IMP:
  527. result = VarImp(vleft, vright, &vres);
  528. break;
  529. case VOP_MOD:
  530. result = VarMod(vleft, vright, &vres);
  531. break;
  532. case VOP_OR:
  533. result = VarOr(vleft, vright, &vres);
  534. break;
  535. case VOP_POW:
  536. result = VarPow(vleft, vright, &vres);
  537. break;
  538. case VOP_XOR:
  539. result = VarXor(vleft, vright, &vres);
  540. break;
  541. /*Let say it fails as no valid op has been given */
  542. default:
  543. result = E_INVALIDARG;
  544. }
  545. if (SUCCEEDED(result)) {
  546. php_com_wrap_variant(return_value, &vres, codepage);
  547. } else {
  548. php_com_throw_exception(result, NULL);
  549. }
  550. VariantClear(&vres);
  551. VariantClear(&left_val);
  552. VariantClear(&right_val);
  553. }
  554. /* }}} */
  555. /* {{{ "Adds" two variant values together and returns the result */
  556. PHP_FUNCTION(variant_add)
  557. {
  558. variant_binary_operation(VOP_ADD, INTERNAL_FUNCTION_PARAM_PASSTHRU);
  559. }
  560. /* }}} */
  561. /* {{{ concatenates two variant values together and returns the result */
  562. PHP_FUNCTION(variant_cat)
  563. {
  564. variant_binary_operation(VOP_CAT, INTERNAL_FUNCTION_PARAM_PASSTHRU);
  565. }
  566. /* }}} */
  567. /* {{{ subtracts the value of the right variant from the left variant value and returns the result */
  568. PHP_FUNCTION(variant_sub)
  569. {
  570. variant_binary_operation(VOP_SUB, INTERNAL_FUNCTION_PARAM_PASSTHRU);
  571. }
  572. /* }}} */
  573. /* {{{ multiplies the values of the two variants and returns the result */
  574. PHP_FUNCTION(variant_mul)
  575. {
  576. variant_binary_operation(VOP_MUL, INTERNAL_FUNCTION_PARAM_PASSTHRU);
  577. }
  578. /* }}} */
  579. /* {{{ performs a bitwise AND operation between two variants and returns the result */
  580. PHP_FUNCTION(variant_and)
  581. {
  582. variant_binary_operation(VOP_AND, INTERNAL_FUNCTION_PARAM_PASSTHRU);
  583. }
  584. /* }}} */
  585. /* {{{ Returns the result from dividing two variants */
  586. PHP_FUNCTION(variant_div)
  587. {
  588. variant_binary_operation(VOP_DIV, INTERNAL_FUNCTION_PARAM_PASSTHRU);
  589. }
  590. /* }}} */
  591. /* {{{ Performs a bitwise equivalence on two variants */
  592. PHP_FUNCTION(variant_eqv)
  593. {
  594. variant_binary_operation(VOP_EQV, INTERNAL_FUNCTION_PARAM_PASSTHRU);
  595. }
  596. /* }}} */
  597. /* {{{ Converts variants to integers and then returns the result from dividing them */
  598. PHP_FUNCTION(variant_idiv)
  599. {
  600. variant_binary_operation(VOP_IDIV, INTERNAL_FUNCTION_PARAM_PASSTHRU);
  601. }
  602. /* }}} */
  603. /* {{{ Performs a bitwise implication on two variants */
  604. PHP_FUNCTION(variant_imp)
  605. {
  606. variant_binary_operation(VOP_IMP, INTERNAL_FUNCTION_PARAM_PASSTHRU);
  607. }
  608. /* }}} */
  609. /* {{{ Divides two variants and returns only the remainder */
  610. PHP_FUNCTION(variant_mod)
  611. {
  612. variant_binary_operation(VOP_MOD, INTERNAL_FUNCTION_PARAM_PASSTHRU);
  613. }
  614. /* }}} */
  615. /* {{{ Performs a logical disjunction on two variants */
  616. PHP_FUNCTION(variant_or)
  617. {
  618. variant_binary_operation(VOP_OR, INTERNAL_FUNCTION_PARAM_PASSTHRU);
  619. }
  620. /* }}} */
  621. /* {{{ Returns the result of performing the power function with two variants */
  622. PHP_FUNCTION(variant_pow)
  623. {
  624. variant_binary_operation(VOP_POW, INTERNAL_FUNCTION_PARAM_PASSTHRU);
  625. }
  626. /* }}} */
  627. /* {{{ Performs a logical exclusion on two variants */
  628. PHP_FUNCTION(variant_xor)
  629. {
  630. variant_binary_operation(VOP_XOR, INTERNAL_FUNCTION_PARAM_PASSTHRU);
  631. }
  632. /* }}} */
  633. static void variant_unary_operation(enum variant_unary_opcode op, INTERNAL_FUNCTION_PARAMETERS) /* {{{ */
  634. {
  635. VARIANT vres;
  636. VARIANT left_val;
  637. VARIANT *vleft = NULL;
  638. zval *zleft = NULL;
  639. php_com_dotnet_object *obj;
  640. HRESULT result;
  641. int codepage = CP_ACP;
  642. VariantInit(&left_val);
  643. VariantInit(&vres);
  644. if (SUCCESS == zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET,
  645. ZEND_NUM_ARGS(), "O", &zleft, php_com_variant_class_entry)) {
  646. obj = CDNO_FETCH(zleft);
  647. vleft = &obj->v;
  648. } else if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(),
  649. "z!", &zleft)) {
  650. vleft = &left_val;
  651. php_com_variant_from_zval(vleft, zleft, codepage);
  652. } else {
  653. RETURN_THROWS();
  654. }
  655. switch (op) {
  656. case VOP_ABS:
  657. result = VarAbs(vleft, &vres);
  658. break;
  659. case VOP_FIX:
  660. result = VarFix(vleft, &vres);
  661. break;
  662. case VOP_INT:
  663. result = VarInt(vleft, &vres);
  664. break;
  665. case VOP_NEG:
  666. result = VarNeg(vleft, &vres);
  667. break;
  668. case VOP_NOT:
  669. result = VarNot(vleft, &vres);
  670. break;
  671. default:
  672. result = E_INVALIDARG;
  673. }
  674. if (SUCCEEDED(result)) {
  675. php_com_wrap_variant(return_value, &vres, codepage);
  676. } else {
  677. php_com_throw_exception(result, NULL);
  678. }
  679. VariantClear(&vres);
  680. VariantClear(&left_val);
  681. }
  682. /* }}} */
  683. /* {{{ Returns the absolute value of a variant */
  684. PHP_FUNCTION(variant_abs)
  685. {
  686. variant_unary_operation(VOP_ABS, INTERNAL_FUNCTION_PARAM_PASSTHRU);
  687. }
  688. /* }}} */
  689. /* {{{ Returns the integer part ? of a variant */
  690. PHP_FUNCTION(variant_fix)
  691. {
  692. variant_unary_operation(VOP_FIX, INTERNAL_FUNCTION_PARAM_PASSTHRU);
  693. }
  694. /* }}} */
  695. /* {{{ Returns the integer portion of a variant */
  696. PHP_FUNCTION(variant_int)
  697. {
  698. variant_unary_operation(VOP_INT, INTERNAL_FUNCTION_PARAM_PASSTHRU);
  699. }
  700. /* }}} */
  701. /* {{{ Performs logical negation on a variant */
  702. PHP_FUNCTION(variant_neg)
  703. {
  704. variant_unary_operation(VOP_NEG, INTERNAL_FUNCTION_PARAM_PASSTHRU);
  705. }
  706. /* }}} */
  707. /* {{{ Performs bitwise not negation on a variant */
  708. PHP_FUNCTION(variant_not)
  709. {
  710. variant_unary_operation(VOP_NOT, INTERNAL_FUNCTION_PARAM_PASSTHRU);
  711. }
  712. /* }}} */
  713. /* {{{ Rounds a variant to the specified number of decimal places */
  714. PHP_FUNCTION(variant_round)
  715. {
  716. VARIANT vres;
  717. VARIANT left_val;
  718. VARIANT *vleft = NULL;
  719. zval *zleft = NULL;
  720. php_com_dotnet_object *obj;
  721. int codepage = CP_ACP;
  722. zend_long decimals = 0;
  723. VariantInit(&left_val);
  724. VariantInit(&vres);
  725. if (SUCCESS == zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET,
  726. ZEND_NUM_ARGS(), "Ol", &zleft, php_com_variant_class_entry, &decimals)) {
  727. obj = CDNO_FETCH(zleft);
  728. vleft = &obj->v;
  729. } else if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(),
  730. "z!l", &zleft, &decimals)) {
  731. vleft = &left_val;
  732. php_com_variant_from_zval(vleft, zleft, codepage);
  733. } else {
  734. RETURN_THROWS();
  735. }
  736. if (SUCCEEDED(VarRound(vleft, (int)decimals, &vres))) {
  737. php_com_wrap_variant(return_value, &vres, codepage);
  738. }
  739. VariantClear(&vres);
  740. VariantClear(&left_val);
  741. }
  742. /* }}} */
  743. /* {{{ Compares two variants */
  744. PHP_FUNCTION(variant_cmp)
  745. {
  746. VARIANT left_val, right_val;
  747. VARIANT *vleft = NULL, *vright = NULL;
  748. zval *zleft = NULL, *zright = NULL;
  749. php_com_dotnet_object *obj;
  750. int codepage = CP_ACP;
  751. zend_long lcid = LOCALE_SYSTEM_DEFAULT;
  752. zend_long flags = 0;
  753. /* it is safe to ignore the warning for this line; see the comments in com_handlers.c */
  754. STDAPI VarCmp(LPVARIANT pvarLeft, LPVARIANT pvarRight, LCID lcid, DWORD flags);
  755. VariantInit(&left_val);
  756. VariantInit(&right_val);
  757. if (SUCCESS == zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET,
  758. ZEND_NUM_ARGS(), "OO|ll", &zleft, php_com_variant_class_entry,
  759. &zright, php_com_variant_class_entry, &lcid, &flags)) {
  760. obj = CDNO_FETCH(zleft);
  761. vleft = &obj->v;
  762. obj = CDNO_FETCH(zright);
  763. vright = &obj->v;
  764. } else if (SUCCESS == zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET,
  765. ZEND_NUM_ARGS(), "Oz!|ll", &zleft, php_com_variant_class_entry,
  766. &zright, &lcid, &flags)) {
  767. obj = CDNO_FETCH(zleft);
  768. vleft = &obj->v;
  769. vright = &right_val;
  770. php_com_variant_from_zval(vright, zright, codepage);
  771. } else if (SUCCESS == zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET,
  772. ZEND_NUM_ARGS(), "z!O|ll", &zleft, &zright, php_com_variant_class_entry,
  773. &lcid, &flags)) {
  774. obj = CDNO_FETCH(zright);
  775. vright = &obj->v;
  776. vleft = &left_val;
  777. php_com_variant_from_zval(vleft, zleft, codepage);
  778. } else if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(),
  779. "z!z!|ll", &zleft, &zright, &lcid, &flags)) {
  780. vleft = &left_val;
  781. php_com_variant_from_zval(vleft, zleft, codepage);
  782. vright = &right_val;
  783. php_com_variant_from_zval(vright, zright, codepage);
  784. } else {
  785. RETURN_THROWS();
  786. }
  787. ZVAL_LONG(return_value, VarCmp(vleft, vright, (LCID)lcid, (ULONG)flags));
  788. VariantClear(&left_val);
  789. VariantClear(&right_val);
  790. }
  791. /* }}} */
  792. /* {{{ Converts a variant date/time value to unix timestamp */
  793. PHP_FUNCTION(variant_date_to_timestamp)
  794. {
  795. VARIANT vres;
  796. zval *zleft = NULL;
  797. php_com_dotnet_object *obj;
  798. VariantInit(&vres);
  799. if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(),
  800. "O", &zleft, php_com_variant_class_entry)) {
  801. RETURN_THROWS();
  802. }
  803. obj = CDNO_FETCH(zleft);
  804. if (SUCCEEDED(VariantChangeType(&vres, &obj->v, 0, VT_DATE))) {
  805. SYSTEMTIME systime;
  806. struct tm tmv;
  807. VariantTimeToSystemTime(V_DATE(&vres), &systime);
  808. memset(&tmv, 0, sizeof(tmv));
  809. tmv.tm_year = systime.wYear - 1900;
  810. tmv.tm_mon = systime.wMonth - 1;
  811. tmv.tm_mday = systime.wDay;
  812. tmv.tm_hour = systime.wHour;
  813. tmv.tm_min = systime.wMinute;
  814. tmv.tm_sec = systime.wSecond;
  815. tmv.tm_isdst = -1;
  816. tzset();
  817. RETVAL_LONG(mktime(&tmv));
  818. }
  819. VariantClear(&vres);
  820. }
  821. /* }}} */
  822. /* {{{ Returns a variant date representation of a unix timestamp */
  823. PHP_FUNCTION(variant_date_from_timestamp)
  824. {
  825. zend_long timestamp;
  826. time_t ttstamp;
  827. SYSTEMTIME systime;
  828. struct tm *tmv;
  829. VARIANT res;
  830. if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "l",
  831. &timestamp)) {
  832. RETURN_THROWS();
  833. }
  834. if (timestamp < 0) {
  835. zend_argument_value_error(1, "must be greater than or equal to 0");
  836. RETURN_THROWS();
  837. }
  838. VariantInit(&res);
  839. tzset();
  840. ttstamp = timestamp;
  841. tmv = localtime(&ttstamp);
  842. /* Invalid after 23:59:59, December 31, 3000, UTC */
  843. if (!tmv) {
  844. zend_argument_value_error(1, "must not go past 23:59:59, December 31, 3000, UTC");
  845. RETURN_THROWS();
  846. }
  847. memset(&systime, 0, sizeof(systime));
  848. systime.wDay = tmv->tm_mday;
  849. systime.wHour = tmv->tm_hour;
  850. systime.wMinute = tmv->tm_min;
  851. systime.wMonth = tmv->tm_mon + 1;
  852. systime.wSecond = tmv->tm_sec;
  853. systime.wYear = tmv->tm_year + 1900;
  854. V_VT(&res) = VT_DATE;
  855. SystemTimeToVariantTime(&systime, &V_DATE(&res));
  856. php_com_wrap_variant(return_value, &res, CP_ACP);
  857. VariantClear(&res);
  858. }
  859. /* }}} */
  860. /* {{{ Returns the VT_XXX type code for a variant */
  861. PHP_FUNCTION(variant_get_type)
  862. {
  863. zval *zobj;
  864. php_com_dotnet_object *obj;
  865. if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(),
  866. "O", &zobj, php_com_variant_class_entry)) {
  867. RETURN_THROWS();
  868. }
  869. obj = CDNO_FETCH(zobj);
  870. RETURN_LONG(V_VT(&obj->v));
  871. }
  872. /* }}} */
  873. /* {{{ Convert a variant into another type. Variant is modified "in-place" */
  874. PHP_FUNCTION(variant_set_type)
  875. {
  876. zval *zobj;
  877. php_com_dotnet_object *obj;
  878. /* VARTYPE == unsigned short */ zend_long vt;
  879. HRESULT res;
  880. if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(),
  881. "Ol", &zobj, php_com_variant_class_entry, &vt)) {
  882. RETURN_THROWS();
  883. }
  884. obj = CDNO_FETCH(zobj);
  885. res = VariantChangeType(&obj->v, &obj->v, 0, (VARTYPE)vt);
  886. if (SUCCEEDED(res)) {
  887. if (vt != VT_DISPATCH && obj->typeinfo) {
  888. ITypeInfo_Release(obj->typeinfo);
  889. obj->typeinfo = NULL;
  890. }
  891. } else {
  892. char *werr, *msg;
  893. werr = php_win32_error_to_msg(res);
  894. spprintf(&msg, 0, "Variant type conversion failed: %s", werr);
  895. php_win32_error_msg_free(werr);
  896. php_com_throw_exception(res, msg);
  897. efree(msg);
  898. }
  899. }
  900. /* }}} */
  901. /* {{{ Convert a variant into a new variant object of another type */
  902. PHP_FUNCTION(variant_cast)
  903. {
  904. zval *zobj;
  905. php_com_dotnet_object *obj;
  906. /* VARTYPE == unsigned short */ zend_long vt;
  907. VARIANT vres;
  908. HRESULT res;
  909. if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(),
  910. "Ol", &zobj, php_com_variant_class_entry, &vt)) {
  911. RETURN_THROWS();
  912. }
  913. obj = CDNO_FETCH(zobj);
  914. VariantInit(&vres);
  915. res = VariantChangeType(&vres, &obj->v, 0, (VARTYPE)vt);
  916. if (SUCCEEDED(res)) {
  917. php_com_wrap_variant(return_value, &vres, obj->code_page);
  918. } else {
  919. char *werr, *msg;
  920. werr = php_win32_error_to_msg(res);
  921. spprintf(&msg, 0, "Variant type conversion failed: %s", werr);
  922. php_win32_error_msg_free(werr);
  923. php_com_throw_exception(res, msg);
  924. efree(msg);
  925. }
  926. VariantClear(&vres);
  927. }
  928. /* }}} */