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