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