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