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