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