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