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