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