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