xref: /PHP-8.1/ext/com_dotnet/com_variant.c (revision aff36587)
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 			&timestamp)) {
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