xref: /PHP-7.4/ext/com_dotnet/com_handlers.c (revision 29603011)
1 /*
2    +----------------------------------------------------------------------+
3    | PHP Version 7                                                        |
4    +----------------------------------------------------------------------+
5    | Copyright (c) The PHP Group                                          |
6    +----------------------------------------------------------------------+
7    | This source file is subject to version 3.01 of the PHP license,      |
8    | that is bundled with this package in the file LICENSE, and is        |
9    | available through the world-wide-web at the following url:           |
10    | http://www.php.net/license/3_01.txt                                  |
11    | If you did not receive a copy of the PHP license and are unable to   |
12    | obtain it through the world-wide-web, please send a note to          |
13    | license@php.net so we can mail you a copy immediately.               |
14    +----------------------------------------------------------------------+
15    | Author: Wez Furlong  <wez@thebrainroom.com>                          |
16    +----------------------------------------------------------------------+
17  */
18 
19 #ifdef HAVE_CONFIG_H
20 #include "config.h"
21 #endif
22 
23 #include "php.h"
24 #include "php_ini.h"
25 #include "ext/standard/info.h"
26 #include "php_com_dotnet.h"
27 #include "php_com_dotnet_internal.h"
28 #include "Zend/zend_exceptions.h"
29 
com_property_read(zval * object,zval * member,int type,void ** cahce_slot,zval * rv)30 static zval *com_property_read(zval *object, zval *member, int type, void **cahce_slot, zval *rv)
31 {
32 	php_com_dotnet_object *obj;
33 	VARIANT v;
34 	HRESULT res;
35 
36 	ZVAL_NULL(rv);
37 
38 	obj = CDNO_FETCH(object);
39 
40 	if (V_VT(&obj->v) == VT_DISPATCH) {
41 		if (!try_convert_to_string(member)) {
42 			return rv;
43 		}
44 
45 		VariantInit(&v);
46 
47 		res = php_com_do_invoke(obj, Z_STRVAL_P(member), Z_STRLEN_P(member),
48 				DISPATCH_METHOD|DISPATCH_PROPERTYGET, &v, 0, NULL, 1);
49 
50 		if (res == SUCCESS) {
51 			php_com_zval_from_variant(rv, &v, obj->code_page);
52 			VariantClear(&v);
53 		} else if (res == DISP_E_BADPARAMCOUNT) {
54 			php_com_saproxy_create(object, rv, member);
55 		}
56 	} else {
57 		php_com_throw_exception(E_INVALIDARG, "this variant has no properties");
58 	}
59 
60 	return rv;
61 }
62 
com_property_write(zval * object,zval * member,zval * value,void ** cache_slot)63 static zval *com_property_write(zval *object, zval *member, zval *value, void **cache_slot)
64 {
65 	php_com_dotnet_object *obj;
66 	VARIANT v;
67 
68 	obj = CDNO_FETCH(object);
69 
70 	if (V_VT(&obj->v) == VT_DISPATCH) {
71 		if (!try_convert_to_string(member)) {
72 			return value;
73 		}
74 
75 		VariantInit(&v);
76 
77 		if (SUCCESS == php_com_do_invoke(obj, Z_STRVAL_P(member), Z_STRLEN_P(member),
78 				DISPATCH_PROPERTYPUT|DISPATCH_PROPERTYPUTREF, &v, 1, value, 0)) {
79 			VariantClear(&v);
80 		}
81 	} else {
82 		php_com_throw_exception(E_INVALIDARG, "this variant has no properties");
83 	}
84 	return value;
85 }
86 
com_read_dimension(zval * object,zval * offset,int type,zval * rv)87 static zval *com_read_dimension(zval *object, zval *offset, int type, zval *rv)
88 {
89 	php_com_dotnet_object *obj;
90 	VARIANT v;
91 
92 	ZVAL_NULL(rv);
93 
94 	obj = CDNO_FETCH(object);
95 
96 	if (V_VT(&obj->v) == VT_DISPATCH) {
97 		VariantInit(&v);
98 
99 		if (SUCCESS == php_com_do_invoke_by_id(obj, DISPID_VALUE,
100 				DISPATCH_METHOD|DISPATCH_PROPERTYGET, &v, 1, offset, 0, 0)) {
101 			php_com_zval_from_variant(rv, &v, obj->code_page);
102 			VariantClear(&v);
103 		}
104 	} else if (V_ISARRAY(&obj->v)) {
105 		convert_to_long(offset);
106 
107 		if (SafeArrayGetDim(V_ARRAY(&obj->v)) == 1) {
108 			if (php_com_safearray_get_elem(&obj->v, &v, (LONG)Z_LVAL_P(offset))) {
109 				php_com_wrap_variant(rv, &v, obj->code_page);
110 				VariantClear(&v);
111 			}
112 		} else {
113 			php_com_saproxy_create(object, rv, offset);
114 		}
115 
116 	} else {
117 		php_com_throw_exception(E_INVALIDARG, "this variant is not an array type");
118 	}
119 
120 	return rv;
121 }
122 
com_write_dimension(zval * object,zval * offset,zval * value)123 static void com_write_dimension(zval *object, zval *offset, zval *value)
124 {
125 	php_com_dotnet_object *obj;
126 	zval args[2];
127 	VARIANT v;
128 	HRESULT res;
129 
130 	obj = CDNO_FETCH(object);
131 
132 	if (offset == NULL) {
133 		php_com_throw_exception(DISP_E_BADINDEX, "appending to variants is not supported");
134 		return;
135 	}
136 
137 	if (V_VT(&obj->v) == VT_DISPATCH) {
138 		ZVAL_COPY_VALUE(&args[0], offset);
139 		ZVAL_COPY_VALUE(&args[1], value);
140 
141 		VariantInit(&v);
142 
143 		if (SUCCESS == php_com_do_invoke_by_id(obj, DISPID_VALUE,
144 				DISPATCH_METHOD|DISPATCH_PROPERTYPUT, &v, 2, args, 0, 0)) {
145 			VariantClear(&v);
146 		}
147 	} else if (V_ISARRAY(&obj->v)) {
148 		LONG indices = 0;
149 		VARTYPE vt;
150 
151 		if (SafeArrayGetDim(V_ARRAY(&obj->v)) == 1) {
152 			if (FAILED(SafeArrayGetVartype(V_ARRAY(&obj->v), &vt)) || vt == VT_EMPTY) {
153 				vt = V_VT(&obj->v) & ~VT_ARRAY;
154 			}
155 
156 			convert_to_long(offset);
157 			indices = (LONG)Z_LVAL_P(offset);
158 
159 			VariantInit(&v);
160 			php_com_variant_from_zval(&v, value, obj->code_page);
161 
162 			if (V_VT(&v) != vt) {
163 				VariantChangeType(&v, &v, 0, vt);
164 			}
165 
166 			if (vt == VT_VARIANT) {
167 				res = SafeArrayPutElement(V_ARRAY(&obj->v), &indices, &v);
168 			} else {
169 				res = SafeArrayPutElement(V_ARRAY(&obj->v), &indices, &v.lVal);
170 			}
171 
172 			VariantClear(&v);
173 
174 			if (FAILED(res)) {
175 				php_com_throw_exception(res, NULL);
176 			}
177 
178 		} else {
179 			php_com_throw_exception(DISP_E_BADINDEX, "this variant has multiple dimensions; you can't set a new value without specifying *all* dimensions");
180 		}
181 
182 	} else {
183 		php_com_throw_exception(E_INVALIDARG, "this variant is not an array type");
184 	}
185 }
186 
com_get_property_ptr_ptr(zval * object,zval * member,int type,void ** cache_slot)187 static zval *com_get_property_ptr_ptr(zval *object, zval *member, int type, void **cache_slot)
188 {
189 	return NULL;
190 }
191 
192 #if 0
193 static void com_object_set(zval **property, zval *value)
194 {
195 	/* Not yet implemented in the engine */
196 }
197 
198 static zval *com_object_get(zval *property)
199 {
200 	/* Not yet implemented in the engine */
201 	return NULL;
202 }
203 #endif
204 
com_property_exists(zval * object,zval * member,int check_empty,void ** cache_slot)205 static int com_property_exists(zval *object, zval *member, int check_empty, void **cache_slot)
206 {
207 	DISPID dispid;
208 	php_com_dotnet_object *obj;
209 
210 	obj = CDNO_FETCH(object);
211 
212 	if (V_VT(&obj->v) == VT_DISPATCH) {
213 		if (!try_convert_to_string(member)) {
214 			return 0;
215 		}
216 		if (SUCCEEDED(php_com_get_id_of_name(obj, Z_STRVAL_P(member), Z_STRLEN_P(member), &dispid))) {
217 			/* TODO: distinguish between property and method! */
218 			return 1;
219 		}
220 	} else {
221 		/* TODO: check for safearray */
222 	}
223 
224 	return 0;
225 }
226 
com_dimension_exists(zval * object,zval * member,int check_empty)227 static int com_dimension_exists(zval *object, zval *member, int check_empty)
228 {
229 	php_error_docref(NULL, E_WARNING, "Operation not yet supported on a COM object");
230 	return 0;
231 }
232 
com_property_delete(zval * object,zval * member,void ** cache_slot)233 static void com_property_delete(zval *object, zval *member, void **cache_slot)
234 {
235 	php_error_docref(NULL, E_WARNING, "Cannot delete properties from a COM object");
236 }
237 
com_dimension_delete(zval * object,zval * offset)238 static void com_dimension_delete(zval *object, zval *offset)
239 {
240 	php_error_docref(NULL, E_WARNING, "Cannot delete properties from a COM object");
241 }
242 
com_properties_get(zval * object)243 static HashTable *com_properties_get(zval *object)
244 {
245 	/* TODO: use type-info to get all the names and values ?
246 	 * DANGER: if we do that, there is a strong possibility for
247 	 * infinite recursion when the hash is displayed via var_dump().
248 	 * Perhaps it is best to leave it un-implemented.
249 	 */
250 	return &zend_empty_array;
251 }
252 
com_get_gc(zval * object,zval ** table,int * n)253 static HashTable *com_get_gc(zval *object, zval **table, int *n)
254 {
255 	*table = NULL;
256 	*n = 0;
257 	return NULL;
258 }
259 
function_dtor(zval * zv)260 static void function_dtor(zval *zv)
261 {
262 	zend_internal_function *f = (zend_internal_function*)Z_PTR_P(zv);
263 
264 	zend_string_release_ex(f->function_name, 0);
265 	if (f->arg_info) {
266 		efree(f->arg_info);
267 	}
268 	efree(f);
269 }
270 
PHP_FUNCTION(com_method_handler)271 static PHP_FUNCTION(com_method_handler)
272 {
273 	zval *object = getThis();
274 
275 	Z_OBJ_HANDLER_P(object, call_method)(
276 			((zend_internal_function*)EX(func))->function_name,
277 			Z_OBJ_P(object),
278 			INTERNAL_FUNCTION_PARAM_PASSTHRU);
279 }
280 
com_method_get(zend_object ** object_ptr,zend_string * name,const zval * key)281 static zend_function *com_method_get(zend_object **object_ptr, zend_string *name, const zval *key)
282 {
283 	zend_internal_function f, *fptr = NULL;
284 	zend_function *func;
285 	DISPID dummy;
286 	php_com_dotnet_object *obj = (php_com_dotnet_object*)*object_ptr;
287 
288 	if (V_VT(&obj->v) != VT_DISPATCH) {
289 		return NULL;
290 	}
291 
292 	if (FAILED(php_com_get_id_of_name(obj, name->val, name->len, &dummy))) {
293 		return NULL;
294 	}
295 
296 	/* check cache */
297 	if (obj->method_cache == NULL || NULL == (fptr = zend_hash_find_ptr(obj->method_cache, name))) {
298 		f.type = ZEND_OVERLOADED_FUNCTION;
299 		f.num_args = 0;
300 		f.arg_info = NULL;
301 		f.scope = obj->ce;
302 		f.fn_flags = ZEND_ACC_CALL_VIA_HANDLER;
303 		f.function_name = zend_string_copy(name);
304 		f.handler = PHP_FN(com_method_handler);
305 
306 		fptr = &f;
307 
308 		if (obj->typeinfo) {
309 			/* look for byref params */
310 			ITypeComp *comp;
311 			ITypeInfo *TI = NULL;
312 			DESCKIND kind;
313 			BINDPTR bindptr;
314 			OLECHAR *olename;
315 			ULONG lhash;
316 			int i;
317 
318 			if (SUCCEEDED(ITypeInfo_GetTypeComp(obj->typeinfo, &comp))) {
319 				olename = php_com_string_to_olestring(name->val, name->len, obj->code_page);
320 				lhash = LHashValOfNameSys(SYS_WIN32, LOCALE_SYSTEM_DEFAULT, olename);
321 
322 				if (SUCCEEDED(ITypeComp_Bind(comp, olename, lhash, INVOKE_FUNC, &TI, &kind, &bindptr))) {
323 					switch (kind) {
324 						case DESCKIND_FUNCDESC:
325 							f.arg_info = ecalloc(bindptr.lpfuncdesc->cParams, sizeof(zend_arg_info));
326 
327 							for (i = 0; i < bindptr.lpfuncdesc->cParams; i++) {
328 								f.arg_info[i].type = ZEND_TYPE_ENCODE(0,1);
329 								if (bindptr.lpfuncdesc->lprgelemdescParam[i].paramdesc.wParamFlags & PARAMFLAG_FOUT) {
330 									f.arg_info[i].pass_by_reference = ZEND_SEND_BY_REF;
331 								}
332 							}
333 
334 							f.num_args = bindptr.lpfuncdesc->cParams;
335 
336 							ITypeInfo_ReleaseFuncDesc(TI, bindptr.lpfuncdesc);
337 							break;
338 
339 							/* these should not happen, but *might* happen if the user
340 							 * screws up; lets avoid a leak in that case */
341 						case DESCKIND_VARDESC:
342 							ITypeInfo_ReleaseVarDesc(TI, bindptr.lpvardesc);
343 							break;
344 						case DESCKIND_TYPECOMP:
345 							ITypeComp_Release(bindptr.lptcomp);
346 							break;
347 
348 						case DESCKIND_NONE:
349 							break;
350 					}
351 					if (TI) {
352 						ITypeInfo_Release(TI);
353 					}
354 				}
355 				ITypeComp_Release(comp);
356 				efree(olename);
357 			}
358 		}
359 
360 		zend_set_function_arg_flags((zend_function*)&f);
361 		/* save this method in the cache */
362 		if (!obj->method_cache) {
363 			ALLOC_HASHTABLE(obj->method_cache);
364 			zend_hash_init(obj->method_cache, 2, NULL, function_dtor, 0);
365 		}
366 
367 		zend_hash_update_mem(obj->method_cache, name, &f, sizeof(f));
368 	}
369 
370 	if (fptr) {
371 		/* duplicate this into a new chunk of emalloc'd memory,
372 		 * since the engine will efree it */
373 		func = emalloc(sizeof(*fptr));
374 		memcpy(func, fptr, sizeof(*fptr));
375 
376 		return func;
377 	}
378 
379 	return NULL;
380 }
381 
com_call_method(zend_string * method,zend_object * object,INTERNAL_FUNCTION_PARAMETERS)382 static int com_call_method(zend_string *method, zend_object *object, INTERNAL_FUNCTION_PARAMETERS)
383 {
384 	zval *args = NULL;
385 	php_com_dotnet_object *obj = (php_com_dotnet_object*)object;
386 	int nargs;
387 	VARIANT v;
388 	int ret = FAILURE;
389 
390 	if (V_VT(&obj->v) != VT_DISPATCH) {
391 		return FAILURE;
392 	}
393 
394 	nargs = ZEND_NUM_ARGS();
395 
396 	if (nargs) {
397 		args = (zval *)safe_emalloc(sizeof(zval), nargs, 0);
398 		zend_get_parameters_array_ex(nargs, args);
399 	}
400 
401 	VariantInit(&v);
402 
403 	if (SUCCESS == php_com_do_invoke_byref(obj, (zend_internal_function*)EX(func), DISPATCH_METHOD|DISPATCH_PROPERTYGET, &v, nargs, args)) {
404 		php_com_zval_from_variant(return_value, &v, obj->code_page);
405 		ret = SUCCESS;
406 		VariantClear(&v);
407 	}
408 
409 	if (args) {
410 		efree(args);
411 	}
412 
413 	return ret;
414 }
415 
com_constructor_get(zend_object * object)416 static zend_function *com_constructor_get(zend_object *object)
417 {
418 	php_com_dotnet_object *obj = (php_com_dotnet_object *) object;
419 	static zend_internal_function c, d, v;
420 
421 #define POPULATE_CTOR(f, fn)	\
422 	f.type = ZEND_INTERNAL_FUNCTION; \
423 	f.function_name = obj->ce->name; \
424 	f.scope = obj->ce; \
425 	f.arg_info = NULL; \
426 	f.num_args = 0; \
427 	f.fn_flags = 0; \
428 	f.handler = ZEND_FN(fn); \
429 	return (zend_function*)&f;
430 
431 	switch (obj->ce->name->val[0]) {
432 #if HAVE_MSCOREE_H
433 		case 'd':
434 			POPULATE_CTOR(d, com_dotnet_create_instance);
435 #endif
436 
437 		case 'c':
438 			POPULATE_CTOR(c, com_create_instance);
439 
440 		case 'v':
441 			POPULATE_CTOR(v, com_variant_create_instance);
442 
443 		default:
444 			return NULL;
445 	}
446 }
447 
com_class_name_get(const zend_object * object)448 static zend_string* com_class_name_get(const zend_object *object)
449 {
450 	php_com_dotnet_object *obj = (php_com_dotnet_object *)object;
451 
452 	return zend_string_copy(obj->ce->name);
453 }
454 
455 /* This compares two variants for equality */
com_objects_compare(zval * object1,zval * object2)456 static int com_objects_compare(zval *object1, zval *object2)
457 {
458 	php_com_dotnet_object *obja, *objb;
459 	int ret;
460 	/* strange header bug problem here... the headers define the proto without the
461 	 * flags parameter.  However, the MSDN docs state that there is a flags parameter,
462 	 * and my VC6 won't link unless the code uses the version with 4 parameters.
463 	 * So, we have this declaration here to fix it */
464 	STDAPI VarCmp(LPVARIANT pvarLeft, LPVARIANT pvarRight, LCID lcid, DWORD flags);
465 
466 	obja = CDNO_FETCH(object1);
467 	objb = CDNO_FETCH(object2);
468 
469 	switch (VarCmp(&obja->v, &objb->v, LOCALE_SYSTEM_DEFAULT, 0)) {
470 		case VARCMP_LT:
471 			ret = -1;
472 			break;
473 		case VARCMP_GT:
474 			ret = 1;
475 			break;
476 		case VARCMP_EQ:
477 			ret = 0;
478 			break;
479 		default:
480 			/* either or both operands are NULL...
481 			 * not 100% sure how to handle this */
482 			ret = -2;
483 	}
484 
485 	return ret;
486 }
487 
com_object_cast(zval * readobj,zval * writeobj,int type)488 static int com_object_cast(zval *readobj, zval *writeobj, int type)
489 {
490 	php_com_dotnet_object *obj;
491 	VARIANT v;
492 	VARTYPE vt = VT_EMPTY;
493 	HRESULT res = S_OK;
494 
495 	obj = CDNO_FETCH(readobj);
496 	ZVAL_NULL(writeobj);
497 	VariantInit(&v);
498 
499 	if (V_VT(&obj->v) == VT_DISPATCH) {
500 		if (SUCCESS != php_com_do_invoke_by_id(obj, DISPID_VALUE,
501 				DISPATCH_METHOD|DISPATCH_PROPERTYGET, &v, 0, NULL, 1, 0)) {
502 			VariantCopy(&v, &obj->v);
503 		}
504 	} else {
505 		VariantCopy(&v, &obj->v);
506 	}
507 
508 	switch(type) {
509 		case IS_LONG:
510 		case _IS_NUMBER:
511 			vt = VT_INT;
512 			break;
513 		case IS_DOUBLE:
514 			vt = VT_R8;
515 			break;
516 		case IS_FALSE:
517 		case IS_TRUE:
518 		case _IS_BOOL:
519 			vt = VT_BOOL;
520 			break;
521 		case IS_STRING:
522 			vt = VT_BSTR;
523 			break;
524 		default:
525 			;
526 	}
527 
528 	if (vt != VT_EMPTY && vt != V_VT(&v)) {
529 		res = VariantChangeType(&v, &v, 0, vt);
530 	}
531 
532 	if (SUCCEEDED(res)) {
533 		php_com_zval_from_variant(writeobj, &v, obj->code_page);
534 	}
535 
536 	VariantClear(&v);
537 
538 	if (SUCCEEDED(res)) {
539 		return SUCCESS;
540 	}
541 
542 	return zend_std_cast_object_tostring(readobj, writeobj, type);
543 }
544 
com_object_count(zval * object,zend_long * count)545 static int com_object_count(zval *object, zend_long *count)
546 {
547 	php_com_dotnet_object *obj;
548 	LONG ubound = 0, lbound = 0;
549 
550 	obj = CDNO_FETCH(object);
551 
552 	if (!V_ISARRAY(&obj->v)) {
553 		return FAILURE;
554 	}
555 
556 	SafeArrayGetLBound(V_ARRAY(&obj->v), 1, &lbound);
557 	SafeArrayGetUBound(V_ARRAY(&obj->v), 1, &ubound);
558 
559 	*count = ubound - lbound + 1;
560 
561 	return SUCCESS;
562 }
563 
564 zend_object_handlers php_com_object_handlers = {
565 	0,
566 	php_com_object_free_storage,
567 	zend_objects_destroy_object,
568 	php_com_object_clone,
569 	com_property_read,
570 	com_property_write,
571 	com_read_dimension,
572 	com_write_dimension,
573 	com_get_property_ptr_ptr,
574 	NULL, /* com_object_get, */
575 	NULL, /* com_object_set, */
576 	com_property_exists,
577 	com_property_delete,
578 	com_dimension_exists,
579 	com_dimension_delete,
580 	com_properties_get,
581 	com_method_get,
582 	com_call_method,
583 	com_constructor_get,
584 	com_class_name_get,
585 	com_objects_compare,
586 	com_object_cast,
587 	com_object_count,
588 	NULL,									/* get_debug_info */
589 	NULL,									/* get_closure */
590 	com_get_gc,								/* get_gc */
591 };
592 
php_com_object_enable_event_sink(php_com_dotnet_object * obj,int enable)593 void php_com_object_enable_event_sink(php_com_dotnet_object *obj, int enable)
594 {
595 	if (obj->sink_dispatch) {
596 		IConnectionPointContainer *cont;
597 		IConnectionPoint *point;
598 
599 		if (SUCCEEDED(IDispatch_QueryInterface(V_DISPATCH(&obj->v),
600 				&IID_IConnectionPointContainer, (void**)&cont))) {
601 
602 			if (SUCCEEDED(IConnectionPointContainer_FindConnectionPoint(cont,
603 					&obj->sink_id, &point))) {
604 
605 				if (enable) {
606 					IConnectionPoint_Advise(point, (IUnknown*)obj->sink_dispatch, &obj->sink_cookie);
607 				} else {
608 					IConnectionPoint_Unadvise(point, obj->sink_cookie);
609 				}
610 				IConnectionPoint_Release(point);
611 			}
612 			IConnectionPointContainer_Release(cont);
613 		}
614 	}
615 }
616 
php_com_object_free_storage(zend_object * object)617 void php_com_object_free_storage(zend_object *object)
618 {
619 	php_com_dotnet_object *obj = (php_com_dotnet_object*)object;
620 
621 	if (obj->typeinfo) {
622 		ITypeInfo_Release(obj->typeinfo);
623 		obj->typeinfo = NULL;
624 	}
625 
626 	if (obj->sink_dispatch) {
627 		php_com_object_enable_event_sink(obj, FALSE);
628 		IDispatch_Release(obj->sink_dispatch);
629 		obj->sink_dispatch = NULL;
630 	}
631 
632 	VariantClear(&obj->v);
633 
634 	if (obj->method_cache) {
635 		zend_hash_destroy(obj->method_cache);
636 		FREE_HASHTABLE(obj->method_cache);
637 	}
638 	if (obj->id_of_name_cache) {
639 		zend_hash_destroy(obj->id_of_name_cache);
640 		FREE_HASHTABLE(obj->id_of_name_cache);
641 	}
642 
643 	zend_object_std_dtor(object);
644 }
645 
php_com_object_clone(zval * object)646 zend_object* php_com_object_clone(zval *object)
647 {
648 	php_com_dotnet_object *cloneobj, *origobject;
649 
650 	origobject = (php_com_dotnet_object*)Z_OBJ_P(object);
651 	cloneobj = (php_com_dotnet_object*)emalloc(sizeof(php_com_dotnet_object));
652 
653 	memcpy(cloneobj, origobject, sizeof(*cloneobj));
654 
655 	/* VariantCopy will perform VariantClear; we don't want to clobber
656 	 * the IDispatch that we memcpy'd, so we init a new variant in the
657 	 * clone structure */
658 	VariantInit(&cloneobj->v);
659 	/* We use the Indirection-following version of the API since we
660 	 * want to clone as much as possible */
661 	VariantCopyInd(&cloneobj->v, &origobject->v);
662 
663 	if (cloneobj->typeinfo) {
664 		ITypeInfo_AddRef(cloneobj->typeinfo);
665 	}
666 
667 	return (zend_object*)cloneobj;
668 }
669 
php_com_object_new(zend_class_entry * ce)670 zend_object* php_com_object_new(zend_class_entry *ce)
671 {
672 	php_com_dotnet_object *obj;
673 
674 	php_com_initialize();
675 	obj = emalloc(sizeof(*obj));
676 	memset(obj, 0, sizeof(*obj));
677 
678 	VariantInit(&obj->v);
679 	obj->code_page = CP_ACP;
680 	obj->ce = ce;
681 
682 	zend_object_std_init(&obj->zo, ce);
683 	obj->zo.handlers = &php_com_object_handlers;
684 
685 	obj->typeinfo = NULL;
686 
687 	return (zend_object*)obj;
688 }
689