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