From 6df5d5ba202b531de6bb563e2462e046d701e8d6 Mon Sep 17 00:00:00 2001 From: Wez Furlong Date: Thu, 14 Aug 2003 16:49:56 +0000 Subject: Add new COM (and .Net) extension for php5. Not yet complete, but should work for most people. --- ext/com_dotnet/README | 79 +++ ext/com_dotnet/com_com.c | 429 ++++++++++++++++ ext/com_dotnet/com_dotnet.c | 195 +++++++ ext/com_dotnet/com_extension.c | 242 +++++++++ ext/com_dotnet/com_handlers.c | 586 +++++++++++++++++++++ ext/com_dotnet/com_misc.c | 102 ++++ ext/com_dotnet/com_olechar.c | 103 ++++ ext/com_dotnet/com_typeinfo.c | 259 ++++++++++ ext/com_dotnet/com_variant.c | 846 +++++++++++++++++++++++++++++++ ext/com_dotnet/php_com_dotnet.h | 70 +++ ext/com_dotnet/php_com_dotnet_internal.h | 143 ++++++ ext/com_dotnet/tests/variants.phpt | 263 ++++++++++ main/config.w32.h | 4 + main/internal_functions_win32.c | 8 +- 14 files changed, 3326 insertions(+), 3 deletions(-) create mode 100644 ext/com_dotnet/README create mode 100644 ext/com_dotnet/com_com.c create mode 100644 ext/com_dotnet/com_dotnet.c create mode 100644 ext/com_dotnet/com_extension.c create mode 100644 ext/com_dotnet/com_handlers.c create mode 100644 ext/com_dotnet/com_misc.c create mode 100644 ext/com_dotnet/com_olechar.c create mode 100644 ext/com_dotnet/com_typeinfo.c create mode 100644 ext/com_dotnet/com_variant.c create mode 100644 ext/com_dotnet/php_com_dotnet.h create mode 100644 ext/com_dotnet/php_com_dotnet_internal.h create mode 100644 ext/com_dotnet/tests/variants.phpt diff --git a/ext/com_dotnet/README b/ext/com_dotnet/README new file mode 100644 index 0000000000..2a3bbbda1c --- /dev/null +++ b/ext/com_dotnet/README @@ -0,0 +1,79 @@ +This is the new php5 COM module. + +It is not 100% backwards compatible with php4 ext/com, but you should not miss +the "features" that have not been retained. + +This module exposes 3 classes: variant, com and dotnet(*). +com and dotnet classes are descendants of the variant class; the only +difference between the three are their constructors. Once instantiated, the +module doesn't make a distinction between them. + +COM errrors are mapped to exceptions; you should protect your COM code using +the try..catch construct if you want to be able to handle error conditions. + +Be warned that due to the way the ZE2 currently works, exceptions are only +"armed" at the time they are detected, but do not "detonate" until the end of +the statement. So, code like this: + + $obj->foo[43]->bar(); + +Where the foo[43] access triggers an exception will continue to call the bar() +method on a null object and cause a fatal php error. + +Default properties and array access: + +$obj = new COM("..."); +$obj[1]->foo(); + +The code above will use the type information for the object to determine its +default property and then access it. In php4, it was hard-coded to use the +"Items" member, which was wrong. + +The default property will also be used by the casting support to determine the +value for the object. + +Variants: + +This implementation of COM takes a simpler approach than the php4 version; +we only map a few native types to COM and vice-versa, leaving the more complex +things as variants. This allows greater consistency of data when passing +parameters to and from COM objects (no data will be lost). In addition, a +large number of the variant API has been mapped to PHP space so that you can +use it for working with the special variant decimal, currency and date time +types. This could be used as a replacement for the bcmath extension, for +example. + +You can use the new object casting hook to for a php-native representation of +a variant object: + +$a = new variant(4); +$b = new variant(6); +$c = variant_add($a, $b); +echo $c; // outputs 10 as a string, instead of Object + +Sample Script: + +Version}\n"; +$word->Visible = 1; +$word->Documents->Add(); +$word->Selection->TypeText("This is a test..."); +$word->Documents[1]->SaveAs("Useless test.doc"); +$word->Quit(); +?> + +TODO: + +- Dispatch wrapper for PHP objects. + The wrapper will enable you to sink events sourced from COM objects, + and allow ActiveScript sapi to function again. +- Indexed array function for (g|s)etting array-style properties that are not + actually arrays. +- SafeArray support for variant class. + Currently there is no way to access a safearray from php. +- A few other com_xxx functions need to be ported from the php4 extension. +- documentation + +* dotnet support requires that you have the mscoree.h header from the .net sdk + when you build the module. diff --git a/ext/com_dotnet/com_com.c b/ext/com_dotnet/com_com.c new file mode 100644 index 0000000000..71ef8486df --- /dev/null +++ b/ext/com_dotnet/com_com.c @@ -0,0 +1,429 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 4 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2003 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.0 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_0.txt. | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Wez Furlong | + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php.h" +#include "php_ini.h" +#include "ext/standard/info.h" +#include "php_com_dotnet.h" +#include "php_com_dotnet_internal.h" +#include "Zend/zend_default_classes.h" + +/* {{{ com_create_instance - ctor for COM class */ +PHP_FUNCTION(com_create_instance) +{ + zval *object = getThis(); + zval *server_params = NULL; + php_com_dotnet_object *obj; + char *module_name, *typelib_name = NULL, *server_name = NULL; + char *user_name = NULL, *domain_name = NULL, *password = NULL; + long module_name_len, typelib_name_len, server_name_len, + user_name_len, domain_name_len, password_len; + OLECHAR *moniker; + CLSID clsid; + CLSCTX ctx = CLSCTX_SERVER; + HRESULT res = E_FAIL; + int mode = COMG(autoreg_case_sensitive) ? CONST_CS : 0; + ITypeLib *TL = NULL; + + obj = CDNO_FETCH(object); + + if (FAILURE == zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, + ZEND_NUM_ARGS() TSRMLS_CC, "s|s!ls", + &module_name, &module_name_len, &server_name, &server_name_len, + &obj->code_page, &typelib_name, &typelib_name_len) && + FAILURE == zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, + ZEND_NUM_ARGS() TSRMLS_CC, "sa|ls", + &module_name, &module_name_len, &server_params, &obj->code_page, + &typelib_name, &typelib_name_len)) { + + php_com_throw_exception("Could not create COM object - invalid arguments!" TSRMLS_CC); + ZVAL_NULL(object); + return; + } + + if (server_name) { + ctx = CLSCTX_REMOTE_SERVER; + } else if (server_params) { + zval **tmp; + + /* decode the data from the array */ + + if (SUCCESS == zend_hash_find(HASH_OF(server_params), + "Server", sizeof("Server"), (void**)&tmp)) { + convert_to_string_ex(tmp); + server_name = Z_STRVAL_PP(tmp); + server_name_len = Z_STRLEN_PP(tmp); + ctx = CLSCTX_REMOTE_SERVER; + } + + if (SUCCESS == zend_hash_find(HASH_OF(server_params), + "Username", sizeof("Username"), (void**)&tmp)) { + convert_to_string_ex(tmp); + user_name = Z_STRVAL_PP(tmp); + user_name_len = Z_STRLEN_PP(tmp); + } + + if (SUCCESS == zend_hash_find(HASH_OF(server_params), + "Password", sizeof("Password"), (void**)&tmp)) { + convert_to_string_ex(tmp); + password = Z_STRVAL_PP(tmp); + password_len = Z_STRLEN_PP(tmp); + } + + if (SUCCESS == zend_hash_find(HASH_OF(server_params), + "Domain", sizeof("Domain"), (void**)&tmp)) { + convert_to_string_ex(tmp); + domain_name = Z_STRVAL_PP(tmp); + domain_name_len = Z_STRLEN_PP(tmp); + } + + if (SUCCESS == zend_hash_find(HASH_OF(server_params), + "Flags", sizeof("Flags"), (void**)&tmp)) { + convert_to_long_ex(tmp); + ctx = (CLSCTX)Z_LVAL_PP(tmp); + } + } + + if (server_name && !COMG(allow_dcom)) { + php_com_throw_exception("DCOM has been disabled by your administrator [com.allow_dcom=0]" TSRMLS_CC); + return; + } + + moniker = php_com_string_to_olestring(module_name, module_name_len, obj->code_page TSRMLS_CC); + + if (FAILED(CLSIDFromString(moniker, &clsid))) { + /* try to use it as a moniker */ + IBindCtx *pBindCtx = NULL; + IMoniker *pMoniker = NULL; + ULONG ulEaten; + + if (server_name == NULL) { + res = MK_E_SYNTAX; + } else if (SUCCEEDED(res = CreateBindCtx(0, &pBindCtx)) && + SUCCEEDED(res = MkParseDisplayName(pBindCtx, moniker, &ulEaten, &pMoniker))) { + res = IMoniker_BindToObject(pMoniker, pBindCtx, NULL, &IID_IDispatch, (LPVOID*)&V_DISPATCH(&obj->v)); + + if (SUCCEEDED(res)) { + V_VT(&obj->v) = VT_DISPATCH; + } + + IMoniker_Release(pMoniker); + } + if (pBindCtx) { + IBindCtx_Release(pBindCtx); + } + } else if (server_name) { + COSERVERINFO info; + MULTI_QI qi; + COAUTHIDENTITY authid; + COAUTHINFO authinfo = { + RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL, + RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_IMP_LEVEL_IMPERSONATE, + &authid, EOAC_NONE + }; + + info.dwReserved1 = 0; + info.dwReserved2 = 0; + info.pwszName = php_com_string_to_olestring(server_name, server_name_len, obj->code_page TSRMLS_CC); + + if (user_name) { + authid.User = php_com_string_to_olestring(user_name, -1, obj->code_page TSRMLS_CC); + authid.UserLength = user_name_len; + + if (password) { + authid.Password = (OLECHAR*)password; + authid.PasswordLength = password_len; + } else { + authid.Password = (OLECHAR*)""; + authid.PasswordLength = 0; + } + + if (domain_name) { + authid.Domain = (OLECHAR*)domain_name; + authid.DomainLength = domain_name_len; + } else { + authid.Domain = (OLECHAR*)""; + authid.DomainLength = 0; + } + authid.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI; + info.pAuthInfo = &authinfo; + } else { + info.pAuthInfo = NULL; + } + qi.pIID = &IID_IDispatch; + qi.pItf = NULL; + qi.hr = S_OK; + + res = CoCreateInstanceEx(&clsid, NULL, ctx, &info, 1, &qi); + efree(info.pwszName); + + if (SUCCEEDED(res)) { + res = qi.hr; + V_DISPATCH(&obj->v) = (IDispatch*)qi.pItf; + V_VT(&obj->v) = VT_DISPATCH; + } + + } else { + res = CoCreateInstance(&clsid, NULL, CLSCTX_SERVER, &IID_IDispatch, (LPVOID*)&V_DISPATCH(&obj->v)); + if (SUCCEEDED(res)) { + V_VT(&obj->v) = VT_DISPATCH; + } + } + + efree(moniker); + + if (FAILED(res)) { + char *werr, *msg; + + werr = php_win_err(res); + spprintf(&msg, 0, "Failed to create COM object `%s': %s", module_name, werr); + LocalFree(werr); + + php_com_throw_exception(msg TSRMLS_CC); + efree(msg); + ZVAL_NULL(object); + return; + } + + /* we got the object and it lives ! */ + + /* see if it has TypeInfo available */ + if (FAILED(IDispatch_GetTypeInfo(V_DISPATCH(&obj->v), 0, LANG_NEUTRAL, &obj->typeinfo)) && typelib_name) { + /* load up the library from the named file */ + int cached; + + TL = php_com_load_typelib_via_cache(typelib_name, mode, obj->code_page, &cached TSRMLS_CC); + + if (TL) { + if (COMG(autoreg_on) && !cached) { + php_com_import_typelib(TL, mode, obj->code_page TSRMLS_CC); + } + + /* cross your fingers... there is no guarantee that this ITypeInfo + * instance has any relation to this IDispatch instance... */ + ITypeLib_GetTypeInfo(TL, 0, &obj->typeinfo); + ITypeLib_Release(TL); + } + } else if (obj->typeinfo && COMG(autoreg_on)) { + int idx; + + if (SUCCEEDED(ITypeInfo_GetContainingTypeLib(obj->typeinfo, &TL, &idx))) { + /* check if the library is already in the cache by getting its name */ + BSTR name; + + if (SUCCEEDED(ITypeLib_GetDocumentation(TL, -1, &name, NULL, NULL, NULL))) { + typelib_name = php_com_olestring_to_string(name, &typelib_name_len, obj->code_page TSRMLS_CC); + + if (SUCCESS == zend_ts_hash_add(&php_com_typelibraries, typelib_name, typelib_name_len+1, (void*)&TL, sizeof(ITypeLib*), NULL)) { + php_com_import_typelib(TL, mode, obj->code_page TSRMLS_CC); + + /* add a reference for the hash */ + ITypeLib_AddRef(TL); + } + + } else { + /* try it anyway */ + php_com_import_typelib(TL, mode, obj->code_page TSRMLS_CC); + } + + ITypeLib_Release(TL); + } + } + +} +/* }}} */ + +/* Performs an Invoke on the given com object. + * returns a failure code and creates an exception if there was an error */ +HRESULT php_com_invoke_helper(php_com_dotnet_object *obj, DISPID id_member, + WORD flags, DISPPARAMS *disp_params, VARIANT *v TSRMLS_DC) +{ + HRESULT hr; + unsigned int arg_err; + EXCEPINFO e; + + if (obj->typeinfo) { + hr = ITypeInfo_Invoke(obj->typeinfo, V_DISPATCH(&obj->v), id_member, flags, disp_params, v, &e, &arg_err); + if (FAILED(hr) && (hr != DISP_E_EXCEPTION)) { + hr = IDispatch_Invoke(V_DISPATCH(&obj->v), id_member, + &IID_NULL, LOCALE_SYSTEM_DEFAULT, flags, disp_params, v, &e, &arg_err); + if (SUCCEEDED(hr)) { + /* fall back on using IDispatch directly */ + ITypeInfo_Release(obj->typeinfo); + obj->typeinfo = NULL; + } + } + } else { + hr = IDispatch_Invoke(V_DISPATCH(&obj->v), id_member, + &IID_NULL, LOCALE_SYSTEM_DEFAULT, flags, disp_params, v, &e, &arg_err); + } + + if (FAILED(hr)) { + char *source = NULL, *desc = NULL, *msg = NULL; + int source_len, desc_len; + + switch (hr) { + case DISP_E_EXCEPTION: + if (e.bstrSource) { + source = php_com_olestring_to_string(e.bstrSource, &source_len, obj->code_page TSRMLS_CC); + SysFreeString(e.bstrSource); + } + if (e.bstrDescription) { + desc = php_com_olestring_to_string(e.bstrDescription, &desc_len, obj->code_page TSRMLS_CC); + SysFreeString(e.bstrDescription); + } + if (PG(html_errors)) { + spprintf(&msg, 0, "Source: %s
Description: %s", + source ? source : "Unknown", + desc ? desc : "Unknown"); + } else { + spprintf(&msg, 0, "Source: %s\nDescription: %s", + source ? source : "Unknown", + desc ? desc : "Unknown"); + } + if (desc) { + efree(desc); + } + if (source) { + efree(source); + } + if (e.bstrHelpFile) { + SysFreeString(e.bstrHelpFile); + } + break; + + case DISP_E_PARAMNOTFOUND: + case DISP_E_TYPEMISMATCH: + desc = php_win_err(hr); + spprintf(&msg, 0, "Parameter %d: %s", arg_err, desc); + LocalFree(desc); + break; + default: + desc = php_win_err(hr); + spprintf(&msg, 0, "Error %s", desc); + LocalFree(desc); + break; + } + + if (msg) { + php_com_throw_exception(msg TSRMLS_CC); + efree(msg); + } + } + + return hr; +} + +/* map an ID to a name */ +HRESULT php_com_get_id_of_name(php_com_dotnet_object *obj, char *name, + int namelen, DISPID *dispid TSRMLS_DC) +{ + OLECHAR *olename; + HRESULT hr; + + olename = php_com_string_to_olestring(name, namelen, obj->code_page TSRMLS_CC); + + if (obj->typeinfo) { + hr = ITypeInfo_GetIDsOfNames(obj->typeinfo, &olename, 1, dispid); + if (FAILED(hr)) { + hr = IDispatch_GetIDsOfNames(V_DISPATCH(&obj->v), &IID_NULL, &olename, 1, LOCALE_SYSTEM_DEFAULT, dispid); + if (SUCCEEDED(hr)) { + /* fall back on IDispatch direct */ + ITypeInfo_Release(obj->typeinfo); + obj->typeinfo = NULL; + } + } + } else { + hr = IDispatch_GetIDsOfNames(V_DISPATCH(&obj->v), &IID_NULL, &olename, 1, LOCALE_SYSTEM_DEFAULT, dispid); + } + efree(olename); + + return hr; +} + +/* the core of COM */ +int php_com_do_invoke_by_id(php_com_dotnet_object *obj, DISPID dispid, + WORD flags, VARIANT *v, int nargs, zval **args TSRMLS_DC) +{ + DISPID altdispid; + DISPPARAMS disp_params; + HRESULT hr; + VARIANT *vargs = NULL; + int i; + + if (nargs) { + vargs = (VARIANT*)emalloc(sizeof(VARIANT) * nargs); + } + + /* Invoke'd args are in reverse order */ + for (i = 0; i < nargs; i++) { + php_com_variant_from_zval(&vargs[i], args[nargs - i - 1], obj->code_page TSRMLS_CC); + } + + disp_params.cArgs = nargs; + disp_params.cNamedArgs = 0; + disp_params.rgvarg = vargs; + disp_params.rgdispidNamedArgs = NULL; + + if (flags & DISPATCH_PROPERTYPUT) { + altdispid = DISPID_PROPERTYPUT; + disp_params.rgdispidNamedArgs = &altdispid; + disp_params.cNamedArgs = 1; + } + + /* this will create an exception if needed */ + hr = php_com_invoke_helper(obj, dispid, flags, &disp_params, v TSRMLS_CC); + + /* release variants */ + if (vargs) { + for (i = 0; i < nargs; i++) { + VariantClear(&vargs[i]); + } + efree(vargs); + } + + return SUCCEEDED(hr) ? SUCCESS : FAILURE; +} + +int php_com_do_invoke(php_com_dotnet_object *obj, char *name, int namelen, + WORD flags, VARIANT *v, int nargs, zval **args TSRMLS_DC) +{ + DISPID dispid; + HRESULT hr; + char *winerr = NULL; + char *msg = NULL; + + hr = php_com_get_id_of_name(obj, name, namelen, &dispid TSRMLS_CC); + + if (FAILED(hr)) { + winerr = php_win_err(hr); + spprintf(&msg, 0, "Unable to lookup `%s': %s", name, winerr); + LocalFree(winerr); + php_com_throw_exception(msg TSRMLS_CC); + efree(msg); + return FAILURE; + } + + return php_com_do_invoke_by_id(obj, dispid, flags, v, nargs, args TSRMLS_CC); +} diff --git a/ext/com_dotnet/com_dotnet.c b/ext/com_dotnet/com_dotnet.c new file mode 100644 index 0000000000..7d367ad6f8 --- /dev/null +++ b/ext/com_dotnet/com_dotnet.c @@ -0,0 +1,195 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 4 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2003 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.0 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_0.txt. | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Wez Furlong | + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php.h" + +#if HAVE_MSCOREE_H +# include "php_ini.h" +# include "ext/standard/info.h" +# include "php_com_dotnet.h" +# include "php_com_dotnet_internal.h" +# include "Zend/zend_default_classes.h" +# include + +struct dotnet_runtime_stuff { + ICorRuntimeHost *dotnet_host; + IDispatch *dotnet_domain; + DISPID create_instance; +}; + +/* Since there is no official public mscorlib.h header file, and since + * generating your own version from the binary .tlb file results in a 3MB + * header file (!), we opt for the Dispatch-able approach. This is slightly + * slower for creating new objects, but not too bad */ +static int dotnet_init(TSRMLS_D) +{ + HRESULT hr; + struct dotnet_runtime_stuff *stuff; + IUnknown *unk = NULL; + OLECHAR *olename; + + stuff = emalloc(sizeof(*stuff)); + memset(stuff, 0, sizeof(*stuff)); + + if (SUCCEEDED(CoCreateInstance(&CLSID_CorRuntimeHost, NULL, CLSCTX_ALL, + &IID_ICorRuntimeHost, (LPVOID*)&stuff->dotnet_host))) { + + /* fire up the host and get the domain object */ + if (SUCCEEDED(ICorRuntimeHost_Start(stuff->dotnet_host)) && + SUCCEEDED(ICorRuntimeHost_GetDefaultDomain(stuff->dotnet_host, &unk)) && + SUCCEEDED(IUnknown_QueryInterface(unk, &IID_IDispatch, (LPVOID*)&stuff->dotnet_domain))) { + + /* locate the create-instance member */ + olename = php_com_string_to_olestring("CreateInstance", sizeof("CreateInstance")-1, CP_ACP TSRMLS_CC); + hr = IDispatch_GetIDsOfNames(stuff->dotnet_domain, &IID_NULL, &olename, 1, LOCALE_SYSTEM_DEFAULT, &stuff->create_instance); + efree(olename); + + if (SUCCEEDED(hr)) { + COMG(dotnet_runtime_stuff) = stuff; + } + } + + if (unk) { + IUnknown_Release(unk); + } + } + + if (COMG(dotnet_runtime_stuff) == NULL) { + /* clean up */ + if (stuff->dotnet_domain) { + IDispatch_Release(stuff->dotnet_domain); + } + if (stuff->dotnet_host) { + ICorRuntimeHost_Stop(stuff->dotnet_host); + ICorRuntimeHost_Release(stuff->dotnet_host); + } + efree(stuff); + + return FAILURE; + } + + return SUCCESS; +} + +/* {{{ com_dotnet_create_instance - ctor for DOTNET class */ +PHP_FUNCTION(com_dotnet_create_instance) +{ + zval *object = getThis(); + php_com_dotnet_object *obj; + char *assembly_name, *datatype_name; + long assembly_name_len, datatype_name_len; + struct dotnet_runtime_stuff *stuff; + IObjectHandle *handle; + DISPPARAMS params; + VARIANT vargs[2]; + VARIANT retval; + HRESULT hr; + int ret = FAILURE; + + if (COMG(dotnet_runtime_stuff) == NULL) { + if (FAILURE == dotnet_init(TSRMLS_C)) { + com_throw_exception("Failed to initialize .Net runtime" TSRMLS_CC); + ZVAL_NULL(object); + return; + } + } + + stuff = (struct dotnet_runtime_stuff*)COMG(dotnet_runtime_stuff); + + obj = CDNO_FETCH(object); + + if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|l", + &assembly_name, &assembly_name_len, + &datatype_name, &datatype_name_len, + &obj->code_page)) { + com_throw_exception("Could not create .Net object - invalid arguments!" TSRMLS_CC); + ZVAL_NULL(object); + return; + } + + params.cArgs = 2; + params.cNamedArgs = 0; + params.rgdispidNamedArgs = NULL; + params.rgvarg = vargs; + + VariantInit(&vargs[0]); + VariantInit(&vargs[1]); + VariantInit(&retval); + + V_VT(&vargs[0]) = VT_BSTR; + V_BSTR(&vargs[0]) = php_com_string_to_olestring(datatype_name, datatype_name_len, obj->code_page TSRMLS_CC); + + V_VT(&vargs[1]) = VT_BSTR; + V_BSTR(&vargs[1]) = php_com_string_to_olestring(assembly_name, assembly_name_len, obj->code_page TSRMLS_CC); + + hr = IDispatch_Invoke(stuff->dotnet_domain, stuff->create_instance, &IID_NULL, LOCALE_SYSTEM_DEFAULT, + DISPATCH_METHOD, ¶ms, &retval, NULL, NULL); + + if (SUCCEEDED(hr)) { + /* retval should now be an IUnknown/IDispatch representation of an IObjectHandle interface */ + if ((V_VT(&retval) == VT_UNKNOWN || V_VT(&retval) == VT_DISPATCH) && + SUCCEEDED(IUnknown_QueryInterface(V_UNKNOWN(&retval), &IID_IObjectHandle, &handle))) { + VARIANT unwrapped; + + if (SUCCEEDED(IObjectHandle_Unwrap(handle, &unwrapped))) { + /* unwrapped is now the dispatch pointer we want */ + V_DISPATCH(&obj->v) = V_DISPATCH(&unwrapped); + V_VT(&obj->v) = VT_DISPATCH; + + /* get its type-info */ + IDispatch_GetTypeInfo(V_DISPATCH(&obj->v), 0, LANG_NEUTRAL, &obj->typeinfo); + + ret = SUCCESS; + } + IObjectHandle_Release(handle); + } + VariantClear(&retval); + } + + VariantClear(&vargs[0]); + VariantClear(&vargs[1]); + + if (ret == FAILURE) { + com_throw_exception("Failed to instantiate .Net object" TSRMLS_CC); + ZVAL_NULL(object); + return; + } +} +/* }}} */ + + +void php_com_dotnet_rshutdown(TSRMLS_D) +{ + struct dotnet_runtime_stuff *stuff = COMG(dotnet_runtime_stuff); + + IDispatch_Release(stuff->dotnet_domain); + ICorRuntimeHost_Stop(stuff->dotnet_host); + ICorRuntimeHost_Release(stuff->dotnet_host); + + efree(stuff); + + COMG(dotnet_runtime_stuff) = NULL; +} + +#endif /* HAVE_MSCOREE_H */ diff --git a/ext/com_dotnet/com_extension.c b/ext/com_dotnet/com_extension.c new file mode 100644 index 0000000000..7ed2d98407 --- /dev/null +++ b/ext/com_dotnet/com_extension.c @@ -0,0 +1,242 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 4 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2003 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.0 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_0.txt. | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Wez Furlong | + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php.h" +#include "php_ini.h" +#include "ext/standard/info.h" +#include "php_com_dotnet.h" +#include "php_com_dotnet_internal.h" +#include "Zend/zend_default_classes.h" + +ZEND_DECLARE_MODULE_GLOBALS(com_dotnet) +TsHashTable php_com_typelibraries; +zend_class_entry *php_com_variant_class_entry; + +function_entry com_dotnet_functions[] = { + PHP_FE(variant_set, NULL) + PHP_FE(variant_add, NULL) + PHP_FE(variant_cat, NULL) + PHP_FE(variant_sub, NULL) + PHP_FE(variant_mul, NULL) + PHP_FE(variant_and, NULL) + PHP_FE(variant_div, NULL) + PHP_FE(variant_eqv, NULL) + PHP_FE(variant_idiv, NULL) + PHP_FE(variant_imp, NULL) + PHP_FE(variant_mod, NULL) + PHP_FE(variant_or, NULL) + PHP_FE(variant_pow, NULL) + PHP_FE(variant_xor, NULL) + PHP_FE(variant_abs, NULL) + PHP_FE(variant_fix, NULL) + PHP_FE(variant_int, NULL) + PHP_FE(variant_neg, NULL) + PHP_FE(variant_not, NULL) + PHP_FE(variant_round, NULL) + PHP_FE(variant_cmp, NULL) + PHP_FE(variant_date_to_timestamp, NULL) + PHP_FE(variant_date_from_timestamp, NULL) + PHP_FE(variant_get_type, NULL) + PHP_FE(variant_set_type, NULL) + PHP_FE(variant_cast, NULL) + { NULL, NULL, NULL } +}; + +/* {{{ com_dotnet_module_entry + */ +zend_module_entry com_dotnet_module_entry = { + STANDARD_MODULE_HEADER, + "com_dotnet", + com_dotnet_functions, + PHP_MINIT(com_dotnet), + PHP_MSHUTDOWN(com_dotnet), + PHP_RINIT(com_dotnet), + PHP_RSHUTDOWN(com_dotnet), + PHP_MINFO(com_dotnet), + "0.1", + STANDARD_MODULE_PROPERTIES +}; +/* }}} */ + +#ifdef COMPILE_DL_COM_DOTNET +ZEND_GET_MODULE(com_dotnet) +#endif + +/* {{{ PHP_INI + */ +PHP_INI_BEGIN() + STD_PHP_INI_ENTRY("com.allow_dcom", "0", PHP_INI_SYSTEM, OnUpdateBool, allow_dcom, zend_com_dotnet_globals, com_dotnet_globals) + STD_PHP_INI_ENTRY("com.autoregister_verbose", "0", PHP_INI_ALL, OnUpdateBool, autoreg_verbose, zend_com_dotnet_globals, com_dotnet_globals) + STD_PHP_INI_ENTRY("com.autoregister_typelib", "0", PHP_INI_ALL, OnUpdateBool, autoreg_on, zend_com_dotnet_globals, com_dotnet_globals) + STD_PHP_INI_ENTRY("com.autoregister_casesensitive", "0", PHP_INI_ALL, OnUpdateBool, autoreg_case_sensitive, zend_com_dotnet_globals, com_dotnet_globals) +PHP_INI_END() +/* }}} */ + +/* {{{ php_com_dotnet_init_globals + */ +static void php_com_dotnet_init_globals(zend_com_dotnet_globals *com_dotnet_globals) +{ + memset(com_dotnet_globals, 0, sizeof(*com_dotnet_globals)); +} +/* }}} */ + +/* {{{ PHP_MINIT_FUNCTION + */ +PHP_MINIT_FUNCTION(com_dotnet) +{ + zend_class_entry ce; + + ZEND_INIT_MODULE_GLOBALS(com_dotnet, php_com_dotnet_init_globals, NULL); + REGISTER_INI_ENTRIES(); + + INIT_CLASS_ENTRY(ce, "variant", NULL); + ce.create_object = php_com_object_new; + php_com_variant_class_entry = zend_register_internal_class(&ce TSRMLS_CC); + + INIT_CLASS_ENTRY(ce, "com", NULL); + ce.create_object = php_com_object_new; + zend_register_internal_class_ex(&ce, php_com_variant_class_entry, "variant" TSRMLS_CC); + + zend_ts_hash_init(&php_com_typelibraries, 0, NULL, php_com_typelibrary_dtor, 0); + +#if HAVE_MSCOREE_H + INIT_CLASS_ENTRY(ce, "dotnet", NULL); + ce.create_object = php_com_object_new; + zend_register_internal_class_ex(&ce, php_com_variant_class_entry, "variant" TSRMLS_CC); +#endif + +#define COM_CONST(x) REGISTER_LONG_CONSTANT(#x, x, CONST_CS|CONST_PERSISTENT) + + COM_CONST(CLSCTX_INPROC_SERVER); + COM_CONST(CLSCTX_INPROC_HANDLER); + COM_CONST(CLSCTX_LOCAL_SERVER); + COM_CONST(CLSCTX_REMOTE_SERVER); + COM_CONST(CLSCTX_SERVER); + COM_CONST(CLSCTX_ALL); + + COM_CONST(DISPATCH_METHOD); + COM_CONST(DISPATCH_PROPERTYGET); + COM_CONST(DISPATCH_PROPERTYPUT); + + COM_CONST(VT_NULL); + COM_CONST(VT_EMPTY); + COM_CONST(VT_UI1); + COM_CONST(VT_I1); + COM_CONST(VT_UI2); + COM_CONST(VT_I2); + COM_CONST(VT_UI4); + COM_CONST(VT_I4); + COM_CONST(VT_R4); + COM_CONST(VT_R8); + COM_CONST(VT_BOOL); + COM_CONST(VT_ERROR); + COM_CONST(VT_CY); + COM_CONST(VT_DATE); + COM_CONST(VT_BSTR); + COM_CONST(VT_DECIMAL); + COM_CONST(VT_UNKNOWN); + COM_CONST(VT_DISPATCH); + COM_CONST(VT_VARIANT); + COM_CONST(VT_INT); + COM_CONST(VT_UINT); + COM_CONST(VT_ARRAY); + COM_CONST(VT_BYREF); + + COM_CONST(CP_ACP); + COM_CONST(CP_MACCP); + COM_CONST(CP_OEMCP); + COM_CONST(CP_UTF7); + COM_CONST(CP_UTF8); + COM_CONST(CP_SYMBOL); + COM_CONST(CP_THREAD_ACP); + + COM_CONST(VARCMP_LT); + COM_CONST(VARCMP_EQ); + COM_CONST(VARCMP_GT); + COM_CONST(VARCMP_NULL); + + return SUCCESS; +} +/* }}} */ + +/* {{{ PHP_MSHUTDOWN_FUNCTION + */ +PHP_MSHUTDOWN_FUNCTION(com_dotnet) +{ + UNREGISTER_INI_ENTRIES(); + zend_ts_hash_destroy(&php_com_typelibraries); + return SUCCESS; +} +/* }}} */ + +/* {{{ PHP_RINIT_FUNCTION + */ +PHP_RINIT_FUNCTION(com_dotnet) +{ + return SUCCESS; +} +/* }}} */ + +/* {{{ PHP_RSHUTDOWN_FUNCTION + */ +PHP_RSHUTDOWN_FUNCTION(com_dotnet) +{ +#if HAVE_MSCOREE_H + if (COMG(dotnet_runtime_stuff)) { + php_com_dotnet_rshutdown(TSRMLS_C); + } +#endif + return SUCCESS; +} +/* }}} */ + +/* {{{ PHP_MINFO_FUNCTION + */ +PHP_MINFO_FUNCTION(com_dotnet) +{ + php_info_print_table_start(); + + php_info_print_table_header(2, "COM support", "enabled"); + php_info_print_table_header(2, "DCOM support", COMG(allow_dcom) ? "enabled" : "disabled"); + +#if HAVE_MSCOREE_H + php_info_print_table_header(2, ".Net support", "enabled"); +#else + php_info_print_table_header(2, ".Net support", "not present in this build"); +#endif + + php_info_print_table_end(); + + DISPLAY_INI_ENTRIES(); +} +/* }}} */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/ext/com_dotnet/com_handlers.c b/ext/com_dotnet/com_handlers.c new file mode 100644 index 0000000000..baf129a91f --- /dev/null +++ b/ext/com_dotnet/com_handlers.c @@ -0,0 +1,586 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 4 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2003 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.0 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_0.txt. | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Wez Furlong | + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php.h" +#include "php_ini.h" +#include "ext/standard/info.h" +#include "php_com_dotnet.h" +#include "php_com_dotnet_internal.h" +#include "Zend/zend_default_classes.h" + +static zval *com_property_read(zval *object, zval *member, zend_bool silent TSRMLS_DC) +{ + zval *return_value; + php_com_dotnet_object *obj; + VARIANT v; + + MAKE_STD_ZVAL(return_value); + ZVAL_NULL(return_value); + + obj = CDNO_FETCH(object); + + if (V_VT(&obj->v) == VT_DISPATCH) { + VariantInit(&v); + + convert_to_string_ex(&member); + if (SUCCESS == php_com_do_invoke(obj, Z_STRVAL_P(member), Z_STRLEN_P(member), + DISPATCH_PROPERTYGET, &v, 0, NULL TSRMLS_CC)) { + php_com_zval_from_variant(return_value, &v, obj->code_page TSRMLS_CC); + VariantClear(&v); + } + } else { + if (!silent) { + php_com_throw_exception("this variant has no properties" TSRMLS_CC); + } + } + + return return_value; +} + +static void com_property_write(zval *object, zval *member, zval *value TSRMLS_DC) +{ + php_com_dotnet_object *obj; + VARIANT v; + + obj = CDNO_FETCH(object); + + if (V_VT(&obj->v) == VT_DISPATCH) { + VariantInit(&v); + + convert_to_string_ex(&member); + if (SUCCESS == php_com_do_invoke(obj, Z_STRVAL_P(member), Z_STRLEN_P(member), + DISPATCH_PROPERTYPUT, &v, 1, &value TSRMLS_CC)) { + VariantClear(&v); + } + } else { + php_com_throw_exception("this variant has no properties" TSRMLS_CC); + } +} + +static HRESULT com_get_default_binding(php_com_dotnet_object *obj TSRMLS_DC) +{ + VARDESC *vardesc; + int i; + + if (!obj->typeinfo) { + return FAILURE; + } + + for (i = 0; !obj->have_default_bind; i++) { + if (FAILED(ITypeInfo_GetVarDesc(obj->typeinfo, i, &vardesc))) { + return FAILURE; + } + + if (vardesc->wVarFlags & VARFLAG_FDEFAULTBIND) { + obj->default_bind = (DISPID)vardesc->memid; + obj->have_default_bind = 1; + } + + ITypeInfo_ReleaseVarDesc(obj->typeinfo, vardesc); + } + return obj->have_default_bind ? SUCCESS : FAILURE; +} + +static zval *com_read_dimension(zval *object, zval *offset TSRMLS_DC) +{ + zval *return_value; + php_com_dotnet_object *obj; + VARIANT v; + + MAKE_STD_ZVAL(return_value); + ZVAL_NULL(return_value); + + obj = CDNO_FETCH(object); + + if (V_VT(&obj->v) == VT_DISPATCH) { + if (!obj->have_default_bind && !com_get_default_binding(obj TSRMLS_CC)) { + php_com_throw_exception("this COM object has no default property" TSRMLS_CC); + return return_value; + } + + VariantInit(&v); + + if (SUCCESS == php_com_do_invoke_by_id(obj, obj->default_bind, + DISPATCH_METHOD|DISPATCH_PROPERTYGET, &v, 1, &offset TSRMLS_CC)) { + php_com_zval_from_variant(return_value, &v, obj->code_page TSRMLS_CC); + VariantClear(&v); + } + } else if (V_ISARRAY(&obj->v)) { + SAFEARRAY *sa = V_ARRAY(&obj->v); + UINT dims; + VARTYPE vt; + LONG bound_low = 0, bound_high = 0; + LONG indices[1]; + + dims = SafeArrayGetDim(sa); + + if (dims != 1) { + php_com_throw_exception("can only handle single dimension arrays" TSRMLS_CC); + return return_value; + } + + if (FAILED(SafeArrayGetVartype(sa, &vt)) || vt == VT_EMPTY) { + vt = V_VT(&obj->v) & ~VT_ARRAY; + } + SafeArrayGetUBound(sa, 1, &bound_high); + SafeArrayGetLBound(sa, 1, &bound_low); + + convert_to_long(offset); + + /* check bounds */ + if (Z_LVAL_P(offset) < bound_low || Z_LVAL_P(offset) > bound_high) { + php_com_throw_exception("index out of bounds" TSRMLS_CC); + return return_value; + } + + indices[0] = Z_LVAL_P(offset); + + VariantInit(&v); + V_VT(&v) = vt; + /* store the value into "lVal" member of the variant. + * This works because it is a union; since we know the variant + * type, we end up with a working variant */ + SafeArrayGetElement(sa, indices, &v.lVal); + + /* now we can set the return value from that element */ + php_com_wrap_variant(return_value, &v, obj->code_page TSRMLS_CC); + + VariantClear(&v); + + } else { + php_com_throw_exception("this variant is not an array type" TSRMLS_CC); + } + + return return_value; +} + +static void com_write_dimension(zval *object, zval *offset, zval *value TSRMLS_DC) +{ + php_com_dotnet_object *obj; + zval *args[2]; + VARIANT v; + + obj = CDNO_FETCH(object); + + if (V_VT(&obj->v) == VT_DISPATCH) { + if (!obj->have_default_bind && !com_get_default_binding(obj TSRMLS_CC)) { + php_com_throw_exception("this COM object has no default property" TSRMLS_CC); + return; + } + + args[0] = offset; + args[1] = value; + + VariantInit(&v); + + if (SUCCESS == php_com_do_invoke_by_id(obj, obj->default_bind, + DISPATCH_METHOD|DISPATCH_PROPERTYPUT, &v, 2, args TSRMLS_CC)) { + VariantClear(&v); + } + } else { + /* TODO: check for safearray */ + php_com_throw_exception("this variant is not an array type" TSRMLS_CC); + } +} + +static zval **com_property_get_ptr(zval *object, zval *member TSRMLS_DC) +{ + zval **prop_ptr; + + prop_ptr = emalloc(sizeof(zval **)); + *prop_ptr = com_property_read(object, member, 0 TSRMLS_CC); + return prop_ptr; +} + +static void com_object_set(zval **property, zval *value TSRMLS_DC) +{ + /* Not yet implemented in the engine */ +} + +static zval *com_object_get(zval *property TSRMLS_DC) +{ + /* Not yet implemented in the engine */ + return NULL; +} + +static int com_property_exists(zval *object, zval *member, int check_empty TSRMLS_DC) +{ + DISPID dispid; + php_com_dotnet_object *obj; + + obj = CDNO_FETCH(object); + + if (V_VT(&obj->v) == VT_DISPATCH) { + convert_to_string_ex(&member); + if (SUCCEEDED(php_com_get_id_of_name(obj, Z_STRVAL_P(member), Z_STRLEN_P(member), &dispid TSRMLS_CC))) { + /* TODO: distinguish between property and method! */ + return 1; + } + } else { + /* TODO: check for safearray */ + } + + return 0; +} + +static void com_property_delete(zval *object, zval *member TSRMLS_DC) +{ + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot delete properties from a COM object"); +} + +static void com_dimension_delete(zval *object, zval *offset TSRMLS_DC) +{ + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot delete properties from a COM object"); +} + +static HashTable *com_properties_get(zval *object TSRMLS_DC) +{ + /* TODO: use type-info to get all the names and values ? + * DANGER: if we do that, there is a strong possibility for + * infinite recursion when the hash is displayed via var_dump(). + * Perhaps it is best to leave it un-implemented. + */ + return NULL; +} + +static union _zend_function *com_method_get(zval *object, char *name, int len TSRMLS_DC) +{ + zend_internal_function *f; + php_com_dotnet_object *obj; + + /* TODO: cache this */ + + obj = CDNO_FETCH(object); + + if (V_VT(&obj->v) != VT_DISPATCH) { + return NULL; + } + + f = emalloc(sizeof(zend_internal_function)); + f->type = ZEND_OVERLOADED_FUNCTION; + f->num_args = 0; + f->arg_info = NULL; + f->scope = obj->ce; + f->fn_flags = 0; + f->function_name = estrndup(name, len); + + if (obj->typeinfo) { + /* look for byref params */ + ITypeComp *comp; + ITypeInfo *TI = NULL; + DESCKIND kind; + BINDPTR bindptr; + OLECHAR *olename; + ULONG lhash; + int i; + + if (SUCCEEDED(ITypeInfo_GetTypeComp(obj->typeinfo, &comp))) { + olename = php_com_string_to_olestring(name, len, obj->code_page TSRMLS_CC); + lhash = LHashValOfNameSys(SYS_WIN32, LOCALE_SYSTEM_DEFAULT, olename); + + if (SUCCEEDED(ITypeComp_Bind(comp, olename, lhash, INVOKE_FUNC, &TI, &kind, &bindptr))) { + switch (kind) { + case DESCKIND_FUNCDESC: + f->arg_info = ecalloc(bindptr.lpfuncdesc->cParams, sizeof(zend_arg_info)); + + for (i = 0; i < bindptr.lpfuncdesc->cParams; i++) { + f->arg_info[i].allow_null = 1; + if (bindptr.lpfuncdesc->lprgelemdescParam[i].paramdesc.wParamFlags & PARAMFLAG_FOUT) { + f->arg_info[i].pass_by_reference = 1; + } + } + + f->num_args = bindptr.lpfuncdesc->cParams; + + ITypeInfo_ReleaseFuncDesc(TI, bindptr.lpfuncdesc); + break; + + /* these should not happen, but *might* happen if the user + * screws up; lets avoid a leak in that case */ + case DESCKIND_VARDESC: + ITypeInfo_ReleaseVarDesc(TI, bindptr.lpvardesc); + break; + case DESCKIND_TYPECOMP: + ITypeComp_Release(bindptr.lptcomp); + break; + } + ITypeInfo_Release(TI); + } + ITypeComp_Release(comp); + efree(olename); + } + } + + return (union _zend_function*)f; +} + +static int com_call_method(char *method, INTERNAL_FUNCTION_PARAMETERS) +{ + zval **args = NULL; + php_com_dotnet_object *obj; + int nargs; + VARIANT v; + int ret = FAILURE; + + obj = CDNO_FETCH(getThis()); + + if (V_VT(&obj->v) != VT_DISPATCH) { + //php_com_throw_exception("call to member function of non-object"); + return FAILURE; + } + + nargs = ZEND_NUM_ARGS(); + + if (nargs) { + args = (zval **)emalloc(sizeof(zval *) * nargs); + zend_get_parameters_array(ht, nargs, args); + } + + VariantInit(&v); + + if (SUCCESS == php_com_do_invoke(obj, method, -1, DISPATCH_METHOD|DISPATCH_PROPERTYGET, &v, nargs, args TSRMLS_CC)) { + php_com_zval_from_variant(return_value, &v, obj->code_page TSRMLS_CC); + ret = SUCCESS; + VariantClear(&v); + } + + if (args) { + efree(args); + } + + return ret; +} + +static union _zend_function *com_constructor_get(zval *object TSRMLS_DC) +{ + php_com_dotnet_object *obj; + zend_internal_function *f; + + obj = CDNO_FETCH(object); + + /* TODO: this leaks */ + f = emalloc(sizeof(zend_internal_function)); + f->type = ZEND_INTERNAL_FUNCTION; + + f->function_name = obj->ce->name; + f->scope = obj->ce; + f->arg_info = NULL; + f->num_args = 0; + f->fn_flags = 0; +#if HAVE_MSCOREE_H + if (f->function_name[0] == 'd') { /* 'd'otnet */ + f->handler = ZEND_FN(com_dotnet_create_instance); + } else +#endif + if (f->function_name[0] == 'c') { /* 'c'om */ + f->handler = ZEND_FN(com_create_instance); + } else { /* 'v'ariant */ + f->handler = ZEND_FN(com_variant_create_instance); + } + + return (union _zend_function*)f; +} + +static zend_class_entry *com_class_entry_get(zval *object TSRMLS_DC) +{ + php_com_dotnet_object *obj; + obj = CDNO_FETCH(object); + + return obj->ce; +} + +static int com_class_name_get(zval *object, char **class_name, zend_uint *class_name_len, int parent TSRMLS_DC) +{ + php_com_dotnet_object *obj; + obj = CDNO_FETCH(object); + + *class_name = estrndup(obj->ce->name, obj->ce->name_length); + *class_name_len = obj->ce->name_length; + + return 0; +} + +/* This compares two variants for equality */ +static int com_objects_compare(zval *object1, zval *object2 TSRMLS_DC) +{ + php_com_dotnet_object *obja, *objb; + int ret; + /* strange header bug problem here... the headers define the proto without the + * flags parameter. However, the MSDN docs state that there is a flags parameter, + * and my VC6 won't link unless the code uses the version with 4 parameters. + * So, we have this declaration here to fix it */ + STDAPI VarCmp(LPVARIANT pvarLeft, LPVARIANT pvarRight, LCID lcid, DWORD flags); + + obja = CDNO_FETCH(object1); + objb = CDNO_FETCH(object2); + + switch (VarCmp(&obja->v, &objb->v, LOCALE_SYSTEM_DEFAULT, 0)) { + case VARCMP_LT: + ret = -1; + break; + case VARCMP_GT: + ret = 1; + break; + case VARCMP_EQ: + ret = 0; + break; + default: + /* either or both operands are NULL... + * not 100% sure how to handle this */ + ret = -2; + } + + return ret; +} + +static void com_object_cast(zval *readobj, zval *writeobj, int type, int should_free TSRMLS_DC) +{ + php_com_dotnet_object *obj; + VARIANT v; + VARTYPE vt = VT_EMPTY; + + if (should_free) { + zval_dtor(writeobj); + } + + ZVAL_NULL(writeobj); + + obj = CDNO_FETCH(readobj); + VariantInit(&v); + + if (V_VT(&obj->v) == VT_DISPATCH) { + + if (!obj->have_default_bind && !com_get_default_binding(obj TSRMLS_CC)) { + return; + } + + if (FAILURE == php_com_do_invoke_by_id(obj, obj->default_bind, + DISPATCH_METHOD|DISPATCH_PROPERTYGET, &v, 0, NULL TSRMLS_CC)) { + return; + } + } else { + VariantCopy(&v, &obj->v); + } + + switch(type) { + case IS_LONG: + vt = VT_INT; + break; + case IS_DOUBLE: + vt = VT_R8; + break; + case IS_BOOL: + vt = VT_BOOL; + break; + case IS_STRING: + vt = VT_BSTR; + break; + } + + if (vt != VT_EMPTY) { + VariantChangeType(&v, &v, 0, vt); + } + + php_com_zval_from_variant(writeobj, &v, obj->code_page TSRMLS_CC); + VariantClear(&v); +} + +zend_object_handlers php_com_object_handlers = { + ZEND_OBJECTS_STORE_HANDLERS, + com_property_read, + com_property_write, + com_read_dimension, + com_write_dimension, + com_property_get_ptr, + com_property_get_ptr, + com_object_get, + com_object_set, + com_property_exists, + com_property_delete, + com_dimension_delete, + com_properties_get, + com_method_get, + com_call_method, + com_constructor_get, + com_class_entry_get, + com_class_name_get, + com_objects_compare, + com_object_cast +}; + +void php_com_object_dtor(void *object, zend_object_handle handle TSRMLS_DC) +{ + php_com_dotnet_object *obj = (php_com_dotnet_object*)object; + + if (obj->typeinfo) { + ITypeInfo_Release(obj->typeinfo); + obj->typeinfo = NULL; + } + + VariantClear(&obj->v); + + efree(obj); +} + +void php_com_object_clone(void *object, void **clone_ptr TSRMLS_DC) +{ + php_com_dotnet_object *cloneobj, *origobject; + + origobject = (php_com_dotnet_object*)object; + cloneobj = (php_com_dotnet_object*)emalloc(sizeof(php_com_dotnet_object)); + + memcpy(cloneobj, origobject, sizeof(*cloneobj)); + + /* VariantCopy will perform VariantClear; we don't want to clobber + * the IDispatch that we memcpy'd, so we init a new variant in the + * clone structure */ + VariantInit(&cloneobj->v); + /* We use the Indirection-following version of the API since we + * want to clone as much as possible */ + VariantCopyInd(&cloneobj->v, &origobject->v); + + if (cloneobj->typeinfo) { + ITypeInfo_AddRef(cloneobj->typeinfo); + } + + *clone_ptr = cloneobj; +} + +zend_object_value php_com_object_new(zend_class_entry *ce TSRMLS_DC) +{ + php_com_dotnet_object *obj; + zend_object_value retval; + + obj = emalloc(sizeof(*obj)); + memset(obj, 0, sizeof(*obj)); + + VariantInit(&obj->v); + obj->code_page = CP_ACP; + obj->ce = ce; + + retval.handle = zend_objects_store_put(obj, php_com_object_dtor, php_com_object_clone TSRMLS_CC); + retval.handlers = &php_com_object_handlers; + + return retval; +} diff --git a/ext/com_dotnet/com_misc.c b/ext/com_dotnet/com_misc.c new file mode 100644 index 0000000000..01d29cd3cf --- /dev/null +++ b/ext/com_dotnet/com_misc.c @@ -0,0 +1,102 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 4 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2003 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.0 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_0.txt. | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Wez Furlong | + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php.h" +#include "php_ini.h" +#include "ext/standard/info.h" +#include "php_com_dotnet.h" +#include "php_com_dotnet_internal.h" +#include "Zend/zend_default_classes.h" + +zval *php_com_throw_exception(char *message TSRMLS_DC) +{ + zval *e, *tmp; + + ALLOC_ZVAL(e); + Z_TYPE_P(e) = IS_OBJECT; + object_init_ex(e, zend_exception_get_default()); + e->refcount = 1; + e->is_ref = 1; + + MAKE_STD_ZVAL(tmp); + ZVAL_STRING(tmp, message, 1); + zend_hash_update(Z_OBJPROP_P(e), "message", sizeof("message"), (void**)&tmp, sizeof(zval*), NULL); + + MAKE_STD_ZVAL(tmp); + ZVAL_STRING(tmp, zend_get_executed_filename(TSRMLS_C), 1); + zend_hash_update(Z_OBJPROP_P(e), "file", sizeof("file"), (void**)&tmp, sizeof(zval*), NULL); + + MAKE_STD_ZVAL(tmp); + ZVAL_LONG(tmp, zend_get_executed_lineno(TSRMLS_C)); + zend_hash_update(Z_OBJPROP_P(e), "line", sizeof("line"), (void**)&tmp, sizeof(zval*), NULL); + + EG(exception) = e; + + return e; +} + +PHPAPI void php_com_wrap_dispatch(zval *z, IDispatch *disp, + int codepage TSRMLS_DC) +{ + php_com_dotnet_object *obj; + + obj = emalloc(sizeof(*obj)); + memset(obj, 0, sizeof(*obj)); + obj->code_page = codepage; + obj->ce = php_com_variant_class_entry; + + VariantInit(&obj->v); + V_VT(&obj->v) = VT_DISPATCH; + V_DISPATCH(&obj->v) = disp; + + IDispatch_AddRef(V_DISPATCH(&obj->v)); + IDispatch_GetTypeInfo(V_DISPATCH(&obj->v), 0, LANG_NEUTRAL, &obj->typeinfo); + + Z_TYPE_P(z) = IS_OBJECT; + z->value.obj.handle = zend_objects_store_put(obj, php_com_object_dtor, php_com_object_clone TSRMLS_CC); + z->value.obj.handlers = &php_com_object_handlers; +} + +PHPAPI void php_com_wrap_variant(zval *z, VARIANT *v, + int codepage TSRMLS_DC) +{ + php_com_dotnet_object *obj; + + obj = emalloc(sizeof(*obj)); + memset(obj, 0, sizeof(*obj)); + obj->code_page = codepage; + obj->ce = php_com_variant_class_entry; + + VariantInit(&obj->v); + VariantCopy(&obj->v, v); + + if (V_VT(&obj->v) == VT_DISPATCH) { + IDispatch_GetTypeInfo(V_DISPATCH(&obj->v), 0, LANG_NEUTRAL, &obj->typeinfo); + } + + Z_TYPE_P(z) = IS_OBJECT; + + z->value.obj.handle = zend_objects_store_put(obj, php_com_object_dtor, php_com_object_clone TSRMLS_CC); + z->value.obj.handlers = &php_com_object_handlers; +} diff --git a/ext/com_dotnet/com_olechar.c b/ext/com_dotnet/com_olechar.c new file mode 100644 index 0000000000..bbd87d3053 --- /dev/null +++ b/ext/com_dotnet/com_olechar.c @@ -0,0 +1,103 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 4 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2003 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.0 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_0.txt. | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Wez Furlong | + | Harald Radi | + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php.h" +#include "php_ini.h" +#include "ext/standard/info.h" +#include "php_com_dotnet.h" +#include "php_com_dotnet_internal.h" + + +PHPAPI OLECHAR *php_com_string_to_olestring(char *string, uint string_len, int codepage TSRMLS_DC) +{ + OLECHAR *olestring = NULL; + DWORD flags = codepage == CP_UTF8 ? 0 : MB_PRECOMPOSED | MB_ERR_INVALID_CHARS; + BOOL ok; + + if (string_len == -1) { + /* determine required length for the buffer (includes NUL terminator) */ + string_len = MultiByteToWideChar(codepage, flags, string, -1, NULL, 0); + } else { + /* allow room for NUL terminator */ + string_len++; + } + + if (strlen > 0) { + olestring = (OLECHAR*)emalloc(sizeof(OLECHAR) * string_len); + ok = MultiByteToWideChar(codepage, flags, string, string_len, olestring, string_len); + } else { + ok = FALSE; + olestring = (OLECHAR*)emalloc(sizeof(OLECHAR)); + *olestring = 0; + } + + if (!ok) { + char *msg = php_win_err(GetLastError()); + + php_error_docref(NULL TSRMLS_CC, E_WARNING, + "Could not convert string to unicode: `%s'", msg); + + LocalFree(msg); + } + + return olestring; +} + +PHPAPI char *php_com_olestring_to_string(OLECHAR *olestring, uint *string_len, int codepage TSRMLS_DC) +{ + char *string; + uint length = 0; + BOOL ok; + LONG err; + + length = WideCharToMultiByte(codepage, 0, olestring, -1, NULL, 0, NULL, NULL); + + if (length) { + string = (char*)emalloc(sizeof(char) * length); + length = WideCharToMultiByte(codepage, 0, olestring, -1, string, length, NULL, NULL); + ok = length > 0; + } else { + err = GetLastError(); + string = (char*)emalloc(sizeof(char)); + *string = '\0'; + ok = FALSE; + length = 0; + } + + if (!ok) { + char *msg = php_win_err(err); + + php_error_docref(NULL TSRMLS_CC, E_WARNING, + "Could not convert string from unicode: `%s'", msg); + + LocalFree(msg); + } + + if (string_len) { + *string_len = length-1; + } + + return string; +} \ No newline at end of file diff --git a/ext/com_dotnet/com_typeinfo.c b/ext/com_dotnet/com_typeinfo.c new file mode 100644 index 0000000000..4acc88b6b0 --- /dev/null +++ b/ext/com_dotnet/com_typeinfo.c @@ -0,0 +1,259 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 4 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2003 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.0 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_0.txt. | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Wez Furlong | + | Harald Radi | + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php.h" +#include "php_ini.h" +#include "ext/standard/info.h" +#include "php_com_dotnet.h" +#include "php_com_dotnet_internal.h" + + +/* The search string can be either: + * a) a file name + * b) a CLSID, major, minor e.g. "{00000200-0000-0010-8000-00AA006D2EA4},2,0" + * c) a Type Library name e.g. "Microsoft OLE DB ActiveX Data Objects 1.0 Library" + */ +PHPAPI ITypeLib *php_com_load_typelib(char *search_string, int mode, + int codepage TSRMLS_DC) +{ + ITypeLib *TL = NULL; + char *strtok_buf, *major, *minor; + CLSID clsid; + OLECHAR *p; + HRESULT hr; + + search_string = php_strtok_r(search_string, ",", &strtok_buf); + + if (search_string == NULL) { + return NULL; + } + + major = php_strtok_r(NULL, ",", &strtok_buf); + minor = php_strtok_r(NULL, ",", &strtok_buf); + + p = php_com_string_to_olestring(search_string, strlen(search_string), codepage TSRMLS_CC); + + if (SUCCEEDED(CLSIDFromString(p, &clsid))) { + WORD major_i = 1, minor_i = 0; + + /* pick up the major/minor numbers; if none specified, default to 1,0 */ + if (major && minor) { + major_i = (WORD)atoi(major); + minor_i = (WORD)atoi(minor); + } + + /* Load the TypeLib by GUID */ + hr = LoadRegTypeLib((REFGUID)&clsid, major_i, minor_i, LANG_NEUTRAL, &TL); + + /* if that failed, assumed that the GUID is actually a CLSID and + * attemp to get the library via an instance of that class */ + if (FAILED(hr) && (major == NULL || minor == NULL)) { + IDispatch *disp = NULL; + ITypeInfo *info = NULL; + int idx; + + if (SUCCEEDED(hr = CoCreateInstance(&clsid, NULL, CLSCTX_SERVER, &IID_IDispatch, (LPVOID*)&disp)) && + SUCCEEDED(hr = IDispatch_GetTypeInfo(disp, 0, LANG_NEUTRAL, &info))) { + hr = ITypeInfo_GetContainingTypeLib(info, &TL, &idx); + } + + if (info) { + ITypeInfo_Release(info); + } + if (disp) { + IDispatch_Release(disp); + } + } + } else { + /* Try to load it from a file; if it fails, do a really painful search of + * the registry */ + if (FAILED(LoadTypeLib(p, &TL))) { + HKEY hkey, hsubkey; + DWORD SubKeys, MaxSubKeyLength; + char *keyname; + unsigned int i, j; + DWORD VersionCount; + char version[20]; + char *libname; + DWORD libnamelen; + + if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_CLASSES_ROOT, "TypeLib", 0, KEY_READ, &hkey) && + ERROR_SUCCESS == RegQueryInfoKey(hkey, NULL, NULL, NULL, &SubKeys, + &MaxSubKeyLength, NULL, NULL, NULL, NULL, NULL, NULL)) { + + MaxSubKeyLength++; /* make room for NUL */ + keyname = emalloc(MaxSubKeyLength); + libname = emalloc(strlen(search_string) + 1); + + for (i = 0; i < SubKeys && TL == NULL; i++) { + if (ERROR_SUCCESS == RegEnumKey(hkey, i, keyname, MaxSubKeyLength) && + ERROR_SUCCESS == RegOpenKeyEx(hkey, keyname, 0, KEY_READ, &hsubkey)) { + if (ERROR_SUCCESS == RegQueryInfoKey(hsubkey, NULL, NULL, NULL, &VersionCount, + NULL, NULL, NULL, NULL, NULL, NULL, NULL)) { + for (j = 0; j < VersionCount; j++) { + if (ERROR_SUCCESS != RegEnumKey(hsubkey, j, version, sizeof(version))) { + continue; + } + /* get the default value for this key and compare */ + libnamelen = strlen(search_string)+1; + if (ERROR_SUCCESS == RegQueryValue(hsubkey, version, libname, &libnamelen)) { + if (0 == ((mode & CONST_CS) ? strcmp(libname, search_string) : stricmp(libname, search_string))) { + char *str = NULL; + int major, minor; + + /* fetch the GUID and add the version numbers */ + if (2 != sscanf(version, "%d.%d", &major, &minor)) { + major = 1; + minor = 0; + } + spprintf(&str, 0, "%s,%d,%d", keyname, major, minor); + /* recurse */ + TL = php_com_load_typelib(str, mode, codepage TSRMLS_CC); + + efree(str); + break; + } + } + } + } + RegCloseKey(hsubkey); + } + } + RegCloseKey(hkey); + efree(keyname); + efree(libname); + } + } + } + + efree(p); + + return TL; +} + +/* Given a type-library, merge it into the current engine state */ +PHPAPI int php_com_import_typelib(ITypeLib *TL, int mode, int codepage TSRMLS_DC) +{ + int i, j, interfaces; + TYPEKIND pTKind; + ITypeInfo *TypeInfo; + VARDESC *pVarDesc; + UINT NameCount; + BSTR bstr_ids; + zend_constant c; + zval exists, results, value; + char *const_name; + + if (TL == NULL) { + return FAILURE; + } + + interfaces = ITypeLib_GetTypeInfoCount(TL); + for (i = 0; i < interfaces; i++) { + ITypeLib_GetTypeInfoType(TL, i, &pTKind); + if (pTKind == TKIND_ENUM) { + ITypeLib_GetTypeInfo(TL, i, &TypeInfo); + for (j = 0; ; j++) { + if (FAILED(ITypeInfo_GetVarDesc(TypeInfo, j, &pVarDesc))) { + break; + } + ITypeInfo_GetNames(TypeInfo, pVarDesc->memid, &bstr_ids, 1, &NameCount); + if (NameCount != 1) { + ITypeInfo_ReleaseVarDesc(TypeInfo, pVarDesc); + continue; + } + + const_name = php_com_olestring_to_string(bstr_ids, &c.name_len, codepage TSRMLS_CC); + c.name = zend_strndup(const_name, c.name_len); + efree(const_name); + c.name_len++; /* include NUL */ + SysFreeString(bstr_ids); + + /* sanity check for the case where the constant is already defined */ + if (zend_get_constant(c.name, c.name_len - 1, &exists TSRMLS_CC)) { + if (COMG(autoreg_verbose) && !compare_function(&results, &c.value, &exists TSRMLS_CC)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Type library constant %s is already defined", c.name); + } + free(c.name); + ITypeInfo_ReleaseVarDesc(TypeInfo, pVarDesc); + continue; + } + + /* register the constant */ + php_com_zval_from_variant(&value, pVarDesc->lpvarValue, codepage TSRMLS_CC); + if (Z_TYPE(value) == IS_LONG) { + c.flags = mode; + c.value.type = IS_LONG; + c.value.value.lval = Z_LVAL(value); + c.module_number = 0; + zend_register_constant(&c TSRMLS_CC); + } + ITypeInfo_ReleaseVarDesc(TypeInfo, pVarDesc); + } + ITypeInfo_Release(TypeInfo); + } + } + return SUCCESS; +} + +/* Type-library stuff */ +void php_com_typelibrary_dtor(void *pDest) +{ + ITypeLib *Lib = *(ITypeLib**)pDest; + ITypeLib_Release(Lib); +} + +PHPAPI ITypeLib *php_com_load_typelib_via_cache(char *search_string, + int mode, int codepage, int *cached TSRMLS_DC) +{ + ITypeLib **TL; + char *name_dup; + int l; + + l = strlen(search_string); + + if (zend_ts_hash_find(&php_com_typelibraries, search_string, l+1, + (void**)&TL) == SUCCESS) { + *cached = 1; + /* add a reference for the caller */ + ITypeLib_AddRef(*TL); + return *TL; + } + + *cached = 0; + name_dup = estrndup(search_string, l); + *TL = php_com_load_typelib(name_dup, mode, codepage TSRMLS_CC); + efree(name_dup); + + if (*TL) { + if (SUCCESS == zend_ts_hash_update(&php_com_typelibraries, + search_string, l+1, (void*)*TL, sizeof(ITypeLib*), NULL)) { + /* add a reference for the hash table */ + ITypeLib_AddRef(*TL); + } + } + + return *TL; +} diff --git a/ext/com_dotnet/com_variant.c b/ext/com_dotnet/com_variant.c new file mode 100644 index 0000000000..b8374bb7c1 --- /dev/null +++ b/ext/com_dotnet/com_variant.c @@ -0,0 +1,846 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 4 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2003 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.0 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_0.txt. | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Wez Furlong | + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php.h" +#include "php_ini.h" +#include "ext/standard/info.h" +#include "php_com_dotnet.h" +#include "php_com_dotnet_internal.h" + +PHPAPI void php_com_variant_from_zval(VARIANT *v, zval *z, int codepage TSRMLS_DC) +{ + OLECHAR *olestring; + php_com_dotnet_object *obj; + + switch (Z_TYPE_P(z)) { + case IS_NULL: + V_VT(v) = VT_NULL; + break; + + case IS_BOOL: + V_VT(v) = VT_BOOL; + V_BOOL(v) = Z_BVAL_P(z) ? VARIANT_TRUE : VARIANT_FALSE; + break; + + case IS_OBJECT: + if (php_com_is_valid_object(z TSRMLS_CC)) { + obj = CDNO_FETCH(z); + if (V_VT(&obj->v) == VT_DISPATCH) { + /* pass the underlying object */ + V_VT(v) = VT_DISPATCH; + if (V_DISPATCH(&obj->v)) { + IDispatch_AddRef(V_DISPATCH(&obj->v)); + } + V_DISPATCH(v) = V_DISPATCH(&obj->v); + } else { + /* pass the variant by reference */ + V_VT(v) = VT_VARIANT | VT_BYREF; + V_VARIANTREF(v) = &obj->v; + } + } else { + /* TODO: export the object using our COM wrapper */ + V_VT(v) = VT_NULL; + } + break; + + case IS_ARRAY: + V_VT(v) = VT_NULL; + /* TODO: map as safe array ? */ + break; + + case IS_LONG: + V_VT(v) = VT_I4; + V_I4(v) = Z_LVAL_P(z); + break; + + case IS_DOUBLE: + V_VT(v) = VT_R8; + V_R8(v) = Z_DVAL_P(z); + break; + + case IS_STRING: + V_VT(v) = VT_BSTR; + olestring = php_com_string_to_olestring(Z_STRVAL_P(z), Z_STRLEN_P(z), codepage TSRMLS_CC); + V_BSTR(v) = SysAllocStringByteLen((char*)olestring, Z_STRLEN_P(z) * sizeof(OLECHAR)); + efree(olestring); + break; + + case IS_RESOURCE: + case IS_CONSTANT: + case IS_CONSTANT_ARRAY: + default: + V_VT(v) = VT_NULL; + break; + } +} + +PHPAPI int php_com_zval_from_variant(zval *z, VARIANT *v, int codepage TSRMLS_DC) +{ + OLECHAR *olestring = NULL; + int ret = SUCCESS; + SYSTEMTIME systime; + struct tm tmv; + php_com_dotnet_object *obj; + + switch (V_VT(v)) { + case VT_EMPTY: + case VT_NULL: + case VT_VOID: + ZVAL_NULL(z); + break; + case VT_UI1: + ZVAL_LONG(z, (long)V_UI1(v)); + break; + case VT_I1: + ZVAL_LONG(z, (long)V_I1(v)); + break; + case VT_UI2: + ZVAL_LONG(z, (long)V_UI2(v)); + break; + case VT_I2: + ZVAL_LONG(z, (long)V_I2(v)); + break; + case VT_UI4: /* TODO: promote to double if large? */ + ZVAL_LONG(z, (long)V_UI4(v)); + break; + case VT_I4: + ZVAL_LONG(z, (long)V_I4(v)); + break; + case VT_INT: + ZVAL_LONG(z, V_INT(v)); + break; + case VT_UINT: /* TODO: promote to double if large? */ + ZVAL_LONG(z, (long)V_UINT(v)); + break; + case VT_R4: + ZVAL_DOUBLE(z, (double)V_R4(v)); + break; + case VT_R8: + ZVAL_DOUBLE(z, V_R8(v)); + break; + case VT_BOOL: + ZVAL_BOOL(z, V_BOOL(v) ? 1 : 0); + break; + case VT_BSTR: + olestring = V_BSTR(v); + if (olestring) { + Z_TYPE_P(z) = IS_STRING; + Z_STRVAL_P(z) = php_com_olestring_to_string(olestring, + &Z_STRLEN_P(z), codepage TSRMLS_CC); + olestring = NULL; + } + break; + case VT_UNKNOWN: + if (V_UNKNOWN(v) != NULL) { + IDispatch *disp; + + if (SUCCEEDED(IUnknown_QueryInterface(V_UNKNOWN(v), &IID_IDispatch, &disp))) { + php_com_wrap_dispatch(z, disp, codepage TSRMLS_CC); + IDispatch_Release(disp); + } else { + ret = FAILURE; + } + } + break; + + case VT_DISPATCH: + if (V_DISPATCH(v) != NULL) { + php_com_wrap_dispatch(z, V_DISPATCH(v), codepage TSRMLS_CC); + } + break; + + case VT_VARIANT: + default: + php_com_wrap_variant(z, v, codepage TSRMLS_CC); + } + + if (olestring) { + efree(olestring); + } + + if (ret == FAILURE) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "variant->zval: conversion from 0x%x ret=%d", V_VT(v), ret); + } + + return ret; +} + +/* {{{ com_variant_create_instance - ctor for new VARIANT() */ +PHP_FUNCTION(com_variant_create_instance) +{ + VARTYPE vt = VT_EMPTY; + long codepage = CP_ACP; + zval *object = getThis(); + php_com_dotnet_object *obj; + zval *zvalue = NULL; + HRESULT res; + + if (ZEND_NUM_ARGS() == 0) { + /* just leave things as-is - an empty variant */ + return; + } + + obj = CDNO_FETCH(object); + + if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, + "z!|ll", &zvalue, &vt, &codepage)) { + php_com_throw_exception("Invalid arguments" TSRMLS_CC); + return; + } + + if (ZEND_NUM_ARGS() == 3) { + obj->code_page = codepage; + } + + php_com_variant_from_zval(&obj->v, zvalue, obj->code_page TSRMLS_CC); + + if (ZEND_NUM_ARGS() >= 2) { + + res = VariantChangeType(&obj->v, &obj->v, 0, vt); + + if (FAILED(res)) { + char *werr, *msg; + + werr = php_win_err(res); + spprintf(&msg, 0, "Variant type conversion failed: %s", werr); + LocalFree(werr); + + php_com_throw_exception(msg TSRMLS_CC); + efree(msg); + } + } + + if (V_VT(&obj->v) != VT_DISPATCH && obj->typeinfo) { + ITypeInfo_Release(obj->typeinfo); + obj->typeinfo = NULL; + } +} +/* }}} */ + +/* {{{ proto void variant_set(object variant, mixed value) + Assigns a new value for a variant object (like "set" in VB) */ +PHP_FUNCTION(variant_set) +{ + zval *zobj, *zvalue = NULL; + php_com_dotnet_object *obj; + + if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, + "Oz!", &zobj, php_com_variant_class_entry, &zvalue)) { + return; + } + + obj = CDNO_FETCH(zobj); + + /* dtor the old value */ + if (obj->typeinfo) { + ITypeInfo_Release(obj->typeinfo); + obj->typeinfo = NULL; + } + + VariantClear(&obj->v); + + php_com_variant_from_zval(&obj->v, zvalue, obj->code_page TSRMLS_CC); +} +/* }}} */ + +enum variant_binary_opcode { + VOP_ADD, VOP_CAT, VOP_SUB, VOP_MUL, VOP_AND, VOP_DIV, + VOP_EQV, VOP_IDIV, VOP_IMP, VOP_MOD, VOP_OR, VOP_POW, + VOP_XOR +}; + +enum variant_unary_opcode { + VOP_ABS, VOP_FIX, VOP_INT, VOP_NEG, VOP_NOT +}; + +static void variant_binary_operation(enum variant_binary_opcode op, INTERNAL_FUNCTION_PARAMETERS) /* {{{ */ +{ + VARIANT vres; + VARIANT left_val, right_val; + VARIANT *vleft = NULL, *vright = NULL; + zval *zleft = NULL, *zright = NULL; + php_com_dotnet_object *obj; + HRESULT result; + int codepage = CP_ACP; + + VariantInit(&left_val); + VariantInit(&right_val); + VariantInit(&vres); + + if (SUCCESS == zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, + ZEND_NUM_ARGS() TSRMLS_CC, "OO", &zleft, php_com_variant_class_entry, + &zright, php_com_variant_class_entry)) { + obj = CDNO_FETCH(zleft); + vleft = &obj->v; + obj = CDNO_FETCH(zright); + vright = &obj->v; + } else if (SUCCESS == zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, + ZEND_NUM_ARGS() TSRMLS_CC, "Oz!", &zleft, php_com_variant_class_entry, + &zright)) { + obj = CDNO_FETCH(zleft); + vleft = &obj->v; + vright = &right_val; + php_com_variant_from_zval(vright, zright, codepage TSRMLS_CC); + } else if (SUCCESS == zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, + ZEND_NUM_ARGS() TSRMLS_CC, "z!O", &zleft, &zright, php_com_variant_class_entry)) { + obj = CDNO_FETCH(zright); + vright = &obj->v; + vleft = &left_val; + php_com_variant_from_zval(vleft, zleft, codepage TSRMLS_CC); + } else if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, + "z!z!", &zleft, &zright)) { + + vleft = &left_val; + php_com_variant_from_zval(vleft, zleft, codepage TSRMLS_CC); + + vright = &right_val; + php_com_variant_from_zval(vright, zright, codepage TSRMLS_CC); + + } else { + return; + } + + switch (op) { + case VOP_ADD: + result = VarAdd(vleft, vright, &vres); + break; + case VOP_CAT: + result = VarCat(vleft, vright, &vres); + break; + case VOP_SUB: + result = VarSub(vleft, vright, &vres); + break; + case VOP_MUL: + result = VarMul(vleft, vright, &vres); + break; + case VOP_AND: + result = VarAnd(vleft, vright, &vres); + break; + case VOP_DIV: + result = VarDiv(vleft, vright, &vres); + break; + case VOP_EQV: + result = VarEqv(vleft, vright, &vres); + break; + case VOP_IDIV: + result = VarIdiv(vleft, vright, &vres); + break; + case VOP_IMP: + result = VarImp(vleft, vright, &vres); + break; + case VOP_MOD: + result = VarMod(vleft, vright, &vres); + break; + case VOP_OR: + result = VarOr(vleft, vright, &vres); + break; + case VOP_POW: + result = VarPow(vleft, vright, &vres); + break; + case VOP_XOR: + result = VarXor(vleft, vright, &vres); + break; + } + + if (SUCCEEDED(result)) { + php_com_wrap_variant(return_value, &vres, codepage TSRMLS_CC); + } + + VariantClear(&vres); + VariantClear(&left_val); + VariantClear(&right_val); +} +/* }}} */ + +/* {{{ proto mixed variant_add(mixed left, mixed right) + "Adds" two variant values together and returns the result */ +PHP_FUNCTION(variant_add) +{ + variant_binary_operation(VOP_ADD, INTERNAL_FUNCTION_PARAM_PASSTHRU); +} +/* }}} */ + +/* {{{ proto mixed variant_cat(mixed left, mixed right) + concatenates two variant values together and returns the result */ +PHP_FUNCTION(variant_cat) +{ + variant_binary_operation(VOP_CAT, INTERNAL_FUNCTION_PARAM_PASSTHRU); +} +/* }}} */ + +/* {{{ proto mixed variant_sub(mixed left, mixed right) + subjects the value of the right variant from the left variant value and returns the result */ +PHP_FUNCTION(variant_sub) +{ + variant_binary_operation(VOP_SUB, INTERNAL_FUNCTION_PARAM_PASSTHRU); +} +/* }}} */ + +/* {{{ proto mixed variant_mul(mixed left, mixed right) + multiplies the values of the two variants and returns the result */ +PHP_FUNCTION(variant_mul) +{ + variant_binary_operation(VOP_MUL, INTERNAL_FUNCTION_PARAM_PASSTHRU); +} +/* }}} */ + +/* {{{ proto mixed variant_and(mixed left, mixed right) + performs a bitwise AND operation between two variants and returns the result */ +PHP_FUNCTION(variant_and) +{ + variant_binary_operation(VOP_AND, INTERNAL_FUNCTION_PARAM_PASSTHRU); +} +/* }}} */ + +/* {{{ proto mixed variant_div(mixed left, mixed right) + Returns the result from dividing two variants */ +PHP_FUNCTION(variant_div) +{ + variant_binary_operation(VOP_DIV, INTERNAL_FUNCTION_PARAM_PASSTHRU); +} +/* }}} */ + +/* {{{ proto mixed variant_eqv(mixed left, mixed right) + Performs a bitwise equivalence on two variants */ +PHP_FUNCTION(variant_eqv) +{ + variant_binary_operation(VOP_EQV, INTERNAL_FUNCTION_PARAM_PASSTHRU); +} +/* }}} */ + +/* {{{ proto mixed variant_idiv(mixed left, mixed right) + Converts variants to operands and then returns the result from dividing them */ +PHP_FUNCTION(variant_idiv) +{ + variant_binary_operation(VOP_IDIV, INTERNAL_FUNCTION_PARAM_PASSTHRU); +} +/* }}} */ + +/* {{{ proto mixed variant_imp(mixed left, mixed right) + Performs a bitwise implication on two variants */ +PHP_FUNCTION(variant_imp) +{ + variant_binary_operation(VOP_IMP, INTERNAL_FUNCTION_PARAM_PASSTHRU); +} +/* }}} */ + +/* {{{ proto mixed variant_mod(mixed left, mixed right) + Divides two variants and returns only the remainder */ +PHP_FUNCTION(variant_mod) +{ + variant_binary_operation(VOP_MOD, INTERNAL_FUNCTION_PARAM_PASSTHRU); +} +/* }}} */ + +/* {{{ proto mixed variant_or(mixed left, mixed right) + Performs a logical disjunction on two variants */ +PHP_FUNCTION(variant_or) +{ + variant_binary_operation(VOP_OR, INTERNAL_FUNCTION_PARAM_PASSTHRU); +} +/* }}} */ + +/* {{{ proto mixed variant_pow(mixed left, mixed right) + Returns the result of performing the power function with two variants */ +PHP_FUNCTION(variant_pow) +{ + variant_binary_operation(VOP_POW, INTERNAL_FUNCTION_PARAM_PASSTHRU); +} +/* }}} */ + +/* {{{ proto mixed variant_xor(mixed left, mixed right) + Performs a logical exclusion on two variants */ +PHP_FUNCTION(variant_xor) +{ + variant_binary_operation(VOP_XOR, INTERNAL_FUNCTION_PARAM_PASSTHRU); +} +/* }}} */ + +static void variant_unary_operation(enum variant_unary_opcode op, INTERNAL_FUNCTION_PARAMETERS) /* {{{ */ +{ + VARIANT vres; + VARIANT left_val; + VARIANT *vleft = NULL; + zval *zleft = NULL; + php_com_dotnet_object *obj; + HRESULT result; + int codepage = CP_ACP; + + VariantInit(&left_val); + VariantInit(&vres); + + if (SUCCESS == zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, + ZEND_NUM_ARGS() TSRMLS_CC, "O", &zleft, php_com_variant_class_entry)) { + obj = CDNO_FETCH(zleft); + vleft = &obj->v; + } else if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, + "z!", &zleft)) { + vleft = &left_val; + php_com_variant_from_zval(vleft, zleft, codepage TSRMLS_CC); + } else { + return; + } + + switch (op) { + case VOP_ABS: + result = VarAbs(vleft, &vres); + break; + case VOP_FIX: + result = VarFix(vleft, &vres); + break; + case VOP_INT: + result = VarInt(vleft, &vres); + break; + case VOP_NEG: + result = VarNeg(vleft, &vres); + break; + case VOP_NOT: + result = VarNot(vleft, &vres); + break; + } + + if (SUCCEEDED(result)) { + php_com_wrap_variant(return_value, &vres, codepage TSRMLS_CC); + } + + VariantClear(&vres); + VariantClear(&left_val); +} +/* }}} */ + +/* {{{ proto mixed variant_abs(mixed left) + Returns the absolute value of a variant */ +PHP_FUNCTION(variant_abs) +{ + variant_unary_operation(VOP_ABS, INTERNAL_FUNCTION_PARAM_PASSTHRU); +} +/* }}} */ + +/* {{{ proto mixed variant_fix(mixed left) + Returns the ? of a variant */ +PHP_FUNCTION(variant_fix) +{ + variant_unary_operation(VOP_FIX, INTERNAL_FUNCTION_PARAM_PASSTHRU); +} +/* }}} */ + +/* {{{ proto mixed variant_int(mixed left) + Returns the integer portion of a variant */ +PHP_FUNCTION(variant_int) +{ + variant_unary_operation(VOP_INT, INTERNAL_FUNCTION_PARAM_PASSTHRU); +} +/* }}} */ + +/* {{{ proto mixed variant_neg(mixed left) + Performs logical negation on a variant */ +PHP_FUNCTION(variant_neg) +{ + variant_unary_operation(VOP_NEG, INTERNAL_FUNCTION_PARAM_PASSTHRU); +} +/* }}} */ + +/* {{{ proto mixed variant_not(mixed left) + Performs bitwise not negation on a variant */ +PHP_FUNCTION(variant_not) +{ + variant_unary_operation(VOP_NOT, INTERNAL_FUNCTION_PARAM_PASSTHRU); +} +/* }}} */ + +/* {{{ proto mixed variant_round(mixed left, int decimals) + Rounds a variant to the specified number of decimal places */ +PHP_FUNCTION(variant_round) +{ + VARIANT vres; + VARIANT left_val; + VARIANT *vleft = NULL; + zval *zleft = NULL; + php_com_dotnet_object *obj; + HRESULT result; + int codepage = CP_ACP; + long decimals = 0; + + VariantInit(&left_val); + VariantInit(&vres); + + if (SUCCESS == zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, + ZEND_NUM_ARGS() TSRMLS_CC, "Ol", &zleft, php_com_variant_class_entry, &decimals)) { + obj = CDNO_FETCH(zleft); + vleft = &obj->v; + } else if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, + "z!l", &zleft, &decimals)) { + vleft = &left_val; + php_com_variant_from_zval(vleft, zleft, codepage TSRMLS_CC); + } else { + return; + } + + if (SUCCEEDED(VarRound(vleft, decimals, &vres))) { + php_com_wrap_variant(return_value, &vres, codepage TSRMLS_CC); + } + + VariantClear(&vres); + VariantClear(&left_val); +} +/* }}} */ + +/* {{{ proto int variant_cmp(mixed left, mixed right [, int lcid [, int flags]]) + Compares two variants */ +PHP_FUNCTION(variant_cmp) +{ + VARIANT left_val, right_val; + VARIANT *vleft = NULL, *vright = NULL; + zval *zleft = NULL, *zright = NULL; + php_com_dotnet_object *obj; + HRESULT result; + int codepage = CP_ACP; + long lcid = LOCALE_SYSTEM_DEFAULT; + long flags = 0; + /* it is safe to ignore the warning for this line; see the comments in com_handlers.c */ + STDAPI VarCmp(LPVARIANT pvarLeft, LPVARIANT pvarRight, LCID lcid, DWORD flags); + + VariantInit(&left_val); + VariantInit(&right_val); + + if (SUCCESS == zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, + ZEND_NUM_ARGS() TSRMLS_CC, "OO|ll", &zleft, php_com_variant_class_entry, + &zright, php_com_variant_class_entry, &lcid, &flags)) { + obj = CDNO_FETCH(zleft); + vleft = &obj->v; + obj = CDNO_FETCH(zright); + vright = &obj->v; + } else if (SUCCESS == zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, + ZEND_NUM_ARGS() TSRMLS_CC, "Oz!|ll", &zleft, php_com_variant_class_entry, + &zright, &lcid, &flags)) { + obj = CDNO_FETCH(zleft); + vleft = &obj->v; + vright = &right_val; + php_com_variant_from_zval(vright, zright, codepage TSRMLS_CC); + } else if (SUCCESS == zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, + ZEND_NUM_ARGS() TSRMLS_CC, "z!O|ll", &zleft, &zright, php_com_variant_class_entry, + &lcid, &flags)) { + obj = CDNO_FETCH(zright); + vright = &obj->v; + vleft = &left_val; + php_com_variant_from_zval(vleft, zleft, codepage TSRMLS_CC); + } else if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, + "z!z!|ll", &zleft, &zright, &lcid, &flags)) { + + vleft = &left_val; + php_com_variant_from_zval(vleft, zleft, codepage TSRMLS_CC); + + vright = &right_val; + php_com_variant_from_zval(vright, zright, codepage TSRMLS_CC); + + } else { + return; + } + + ZVAL_LONG(return_value, VarCmp(vleft, vright, lcid, flags)); + + VariantClear(&left_val); + VariantClear(&right_val); +} +/* }}} */ + +/* {{{ proto int variant_date_to_timestamp(object variant) + Converts a variant date/time value to unix timestamp */ +PHP_FUNCTION(variant_date_to_timestamp) +{ + VARIANT vres; + zval *zleft = NULL; + php_com_dotnet_object *obj; + + VariantInit(&vres); + + if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, + "O", &zleft, php_com_variant_class_entry)) { + return; + } + obj = CDNO_FETCH(zleft); + + if (SUCCEEDED(VariantChangeType(&vres, &obj->v, 0, VT_DATE))) { + SYSTEMTIME systime; + struct tm tmv; + + VariantTimeToSystemTime(V_DATE(&vres), &systime); + + memset(&tmv, 0, sizeof(tmv)); + tmv.tm_year = systime.wYear - 1900; + tmv.tm_mon = systime.wMonth - 1; + tmv.tm_mday = systime.wDay; + tmv.tm_hour = systime.wHour; + tmv.tm_min = systime.wMinute; + tmv.tm_sec = systime.wSecond; + tmv.tm_isdst = -1; + + tzset(); + RETVAL_LONG(mktime(&tmv)); + } + + VariantClear(&vres); +} +/* }}} */ + +/* {{{ proto object variant_date_from_timestamp(int timestamp) + Returns a variant date representation of a unix timestamp */ +PHP_FUNCTION(variant_date_from_timestamp) +{ + int timestamp; + SYSTEMTIME systime; + struct tm *tmv; + VARIANT res; + + if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", + ×tamp)) { + return; + } + + VariantInit(&res); + tmv = gmtime(×tamp); + memset(&systime, 0, sizeof(systime)); + + systime.wDay = tmv->tm_mday; + systime.wHour = tmv->tm_hour; + systime.wMinute = tmv->tm_min; + systime.wMonth = tmv->tm_mon + 1; + systime.wSecond = tmv->tm_sec; + systime.wYear = tmv->tm_year + 1900; + + V_VT(&res) = VT_DATE; + SystemTimeToVariantTime(&systime, &V_DATE(&res)); + + php_com_wrap_variant(return_value, &res, CP_ACP TSRMLS_CC); + + VariantClear(&res); +} +/* }}} */ + +/* {{{ proto int variant_get_type(object variant) + Returns the VT_XXX type code for a variant */ +PHP_FUNCTION(variant_get_type) +{ + zval *zobj; + php_com_dotnet_object *obj; + + if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, + "O", &zobj, php_com_variant_class_entry)) { + return; + } + obj = CDNO_FETCH(zobj); + + RETURN_LONG(V_VT(&obj->v)); +} +/* }}} */ + +/* {{{ proto void variant_set_type(object variant, int type) + Convert a variant into another type. Variant is modified "in-place" */ +PHP_FUNCTION(variant_set_type) +{ + zval *zobj; + php_com_dotnet_object *obj; + VARTYPE vt; + HRESULT res; + + if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, + "Ol", &zobj, php_com_variant_class_entry, &vt)) { + return; + } + obj = CDNO_FETCH(zobj); + + res = VariantChangeType(&obj->v, &obj->v, 0, vt); + + if (SUCCEEDED(res)) { + if (vt != VT_DISPATCH && obj->typeinfo) { + ITypeInfo_Release(obj->typeinfo); + obj->typeinfo = NULL; + } + } else { + char *werr, *msg; + + werr = php_win_err(res); + spprintf(&msg, 0, "Variant type conversion failed: %s", werr); + LocalFree(werr); + + php_com_throw_exception(msg TSRMLS_CC); + efree(msg); + } +} +/* }}} */ + +/* {{{ proto object variant_cast(object variant, int type) + Convert a variant into a new variant object of another type */ +PHP_FUNCTION(variant_cast) +{ + zval *zobj; + php_com_dotnet_object *obj; + VARTYPE vt; + VARIANT vres; + HRESULT res; + + if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, + "Ol", &zobj, php_com_variant_class_entry, &vt)) { + return; + } + obj = CDNO_FETCH(zobj); + + VariantInit(&vres); + res = VariantChangeType(&vres, &obj->v, 0, vt); + + if (SUCCEEDED(res)) { + php_com_wrap_variant(return_value, &vres, obj->code_page TSRMLS_CC); + } else { + char *werr, *msg; + + werr = php_win_err(res); + spprintf(&msg, 0, "Variant type conversion failed: %s", werr); + LocalFree(werr); + + php_com_throw_exception(msg TSRMLS_CC); + efree(msg); + } + + VariantClear(&vres); +} +/* }}} */ + +/* {{{ proto mixed variant_index_get(object variant, mixed index1 [, mixed index2 [, ...]]) + Get the value of a multi dimensional array property */ +PHP_FUNCTION(variant_index_get) +{ + zval *zobj; + php_com_dotnet_object *obj; + VARIANT vres; + HRESULT res; + + if (FAILURE == zend_parse_parameters(1 TSRMLS_CC, + "O", &zobj, php_com_variant_class_entry)) { + return; + } + obj = CDNO_FETCH(zobj); + + /* TODO: finish... */ +} +/* }}} */ diff --git a/ext/com_dotnet/php_com_dotnet.h b/ext/com_dotnet/php_com_dotnet.h new file mode 100644 index 0000000000..5d29c52594 --- /dev/null +++ b/ext/com_dotnet/php_com_dotnet.h @@ -0,0 +1,70 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 4 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2003 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.0 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_0.txt. | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Wez Furlong | + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ + +#ifndef PHP_COM_DOTNET_H +#define PHP_COM_DOTNET_H + +extern zend_module_entry com_dotnet_module_entry; +#define phpext_com_dotnet_ptr &com_dotnet_module_entry + +#ifdef PHP_WIN32 +# define PHP_COM_DOTNET_API __declspec(dllexport) +#else +# define PHP_COM_DOTNET_API +#endif + +#ifdef ZTS +#include "TSRM.h" +#endif + +PHP_MINIT_FUNCTION(com_dotnet); +PHP_MSHUTDOWN_FUNCTION(com_dotnet); +PHP_RINIT_FUNCTION(com_dotnet); +PHP_RSHUTDOWN_FUNCTION(com_dotnet); +PHP_MINFO_FUNCTION(com_dotnet); + +PHP_FUNCTION(confirm_com_dotnet_compiled); + +ZEND_BEGIN_MODULE_GLOBALS(com_dotnet) + zend_bool allow_dcom; + zend_bool autoreg_verbose; + zend_bool autoreg_on; + zend_bool autoreg_case_sensitive; + void *dotnet_runtime_stuff; /* opaque to avoid cluttering up other modules */ +ZEND_END_MODULE_GLOBALS(com_dotnet) + +#ifdef ZTS +# define COMG(v) TSRMG(com_dotnet_globals_id, zend_com_dotnet_globals *, v) +#else +# define COMG(v) (com_dotnet_globals.v) +#endif + +extern ZEND_DECLARE_MODULE_GLOBALS(com_dotnet); + +#endif /* PHP_COM_DOTNET_H */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/ext/com_dotnet/php_com_dotnet_internal.h b/ext/com_dotnet/php_com_dotnet_internal.h new file mode 100644 index 0000000000..90cc44760f --- /dev/null +++ b/ext/com_dotnet/php_com_dotnet_internal.h @@ -0,0 +1,143 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 4 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2003 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.0 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_0.txt. | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Wez Furlong | + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ + +#ifndef PHP_COM_DOTNET_INTERNAL_H +#define PHP_COM_DOTNET_INTERNAL_H + +#define _WIN32_DCOM +#define COBJMACROS +#include +#include "oleauto.h" +#include "win32/winutil.h" + +/* brain-death in winutil.h defines the macro to hide the useful function... */ +#undef php_win_err + +typedef struct _php_com_dotnet_object { + VARIANT v; + + ITypeInfo *typeinfo; + int code_page; + unsigned int have_default_bind:1; + + zend_class_entry *ce; + DISPID default_bind; /* default property for array accesses */ +} php_com_dotnet_object; + +static inline int php_com_is_valid_object(zval *zv TSRMLS_DC) +{ + zend_class_entry *ce = Z_OBJCE_P(zv); + return strcmp("com", ce->name) == 0 || + strcmp("dotnet", ce->name) == 0 || + strcmp("variant", ce->name) == 0; +} + +#define CDNO_FETCH(zv) (php_com_dotnet_object*)zend_object_store_get_object(zv TSRMLS_CC) +#define CDNO_FETCH_VERIFY(obj, zv) do { \ + if (!php_com_is_valid_object(zv TSRMLS_CC)) { \ + php_com_throw_exception("expected a variant object" TSRMLS_CC); \ + return; \ + } \ + obj = (php_com_dotnet_object*)zend_object_store_get_object(zv TSRMLS_CC); \ +} while(0) + +/* com_extension.c */ +TsHashTable php_com_typelibraries; +zend_class_entry *php_com_variant_class_entry; + +/* com_handlers.c */ +zend_object_value php_com_object_new(zend_class_entry *ce TSRMLS_DC); +void php_com_object_clone(void *object, void **clone_ptr TSRMLS_DC); +void php_com_object_dtor(void *object, zend_object_handle handle TSRMLS_DC); +zend_object_handlers php_com_object_handlers; + +/* com_olechar.c */ +PHPAPI char *php_com_olestring_to_string(OLECHAR *olestring, + uint *string_len, int codepage TSRMLS_DC); +PHPAPI OLECHAR *php_com_string_to_olestring(char *string, + uint string_len, int codepage TSRMLS_DC); + + +/* com_com.c */ +PHP_FUNCTION(com_create_instance); +HRESULT php_com_invoke_helper(php_com_dotnet_object *obj, DISPID id_member, + WORD flags, DISPPARAMS *disp_params, VARIANT *v TSRMLS_DC); +HRESULT php_com_get_id_of_name(php_com_dotnet_object *obj, char *name, + int namelen, DISPID *dispid TSRMLS_DC); +int php_com_do_invoke_by_id(php_com_dotnet_object *obj, DISPID dispid, + WORD flags, VARIANT *v, int nargs, zval **args TSRMLS_DC); +int php_com_do_invoke(php_com_dotnet_object *obj, char *name, int namelen, + WORD flags, VARIANT *v, int nargs, zval **args TSRMLS_DC); + +/* com_variant.c */ +PHP_FUNCTION(com_variant_create_instance); +PHP_FUNCTION(variant_set); +PHP_FUNCTION(variant_add); +PHP_FUNCTION(variant_cat); +PHP_FUNCTION(variant_sub); +PHP_FUNCTION(variant_mul); +PHP_FUNCTION(variant_and); +PHP_FUNCTION(variant_div); +PHP_FUNCTION(variant_eqv); +PHP_FUNCTION(variant_idiv); +PHP_FUNCTION(variant_imp); +PHP_FUNCTION(variant_mod); +PHP_FUNCTION(variant_or); +PHP_FUNCTION(variant_pow); +PHP_FUNCTION(variant_xor); +PHP_FUNCTION(variant_abs); +PHP_FUNCTION(variant_fix); +PHP_FUNCTION(variant_int); +PHP_FUNCTION(variant_neg); +PHP_FUNCTION(variant_not); +PHP_FUNCTION(variant_round); +PHP_FUNCTION(variant_cmp); +PHP_FUNCTION(variant_date_to_timestamp); +PHP_FUNCTION(variant_date_from_timestamp); +PHP_FUNCTION(variant_get_type); +PHP_FUNCTION(variant_set_type); +PHP_FUNCTION(variant_cast); + +PHPAPI void php_com_variant_from_zval_with_type(VARIANT *v, zval *z, VARTYPE type, int codepage TSRMLS_DC); +PHPAPI void php_com_variant_from_zval(VARIANT *v, zval *z, int codepage TSRMLS_DC); +PHPAPI int php_com_zval_from_variant(zval *z, VARIANT *v, int codepage TSRMLS_DC); + +/* com_dotnet.c */ +PHP_FUNCTION(com_dotnet_create_instance); +void php_com_dotnet_rshutdown(TSRMLS_D); + +/* com_misc.c */ +zval *php_com_throw_exception(char *message TSRMLS_DC); +PHPAPI void php_com_wrap_dispatch(zval *z, IDispatch *disp, + int codepage TSRMLS_DC); +PHPAPI void php_com_wrap_variant(zval *z, VARIANT *v, + int codepage TSRMLS_DC); + +/* com_typeinfo.c */ +PHPAPI ITypeLib *php_com_load_typelib_via_cache(char *search_string, + int mode, int codepage, int *cached TSRMLS_DC); +PHPAPI ITypeLib *php_com_load_typelib(char *search_string, int mode, + int codepage TSRMLS_DC); +PHPAPI int php_com_import_typelib(ITypeLib *TL, int mode, + int codepage TSRMLS_DC); +void php_com_typelibrary_dtor(void *pDest); + + +#endif diff --git a/ext/com_dotnet/tests/variants.phpt b/ext/com_dotnet/tests/variants.phpt new file mode 100644 index 0000000000..36258424da --- /dev/null +++ b/ext/com_dotnet/tests/variants.phpt @@ -0,0 +1,263 @@ +--TEST-- +COM: General variant tests +--SKIPIF-- + +--FILE-- + 42, VT_R8 => 3.5, VT_BSTR => "hello", VT_BOOL => false); +$binary_ops = array('add', 'cat', 'sub', 'mul', 'and', 'div', + 'eqv', 'idiv', 'imp', 'mod', 'or', 'pow', 'xor'); + +foreach ($values as $t => $val) { + $v = new VARIANT($val); + if ($t != variant_get_type($v)) { + printf("Bork: [%d] %d: %s\n", $t, variant_get_type($v), (string)$v); + } + $results = array(); + + foreach ($values as $op2) { + echo "--\n"; + foreach ($binary_ops as $op) { + echo "$op: " . call_user_func('variant_' . $op, $v, $op2) . "\n"; + } + } +} +$a = new VARIANT('10.0', VT_DECIMAL); +var_dump($a); + +echo "OK!"; +?> +--EXPECT-- +-- +add: 84 +cat: 4242 +sub: 0 +mul: 1764 +and: 42 +div: 1 +eqv: -1 +idiv: 1 +imp: -1 +mod: 0 +or: 42 +pow: 1.50130937545297E+68 +xor: 0 +-- +add: 45.5 +cat: 423.5 +sub: 38.5 +mul: 147 +and: 0 +div: 12 +eqv: -47 +idiv: 10 +imp: -43 +mod: 2 +or: 46 +pow: 480145.116863642 +xor: 46 +-- +add: +cat: 42hello +sub: +mul: +and: +div: +eqv: +idiv: +imp: +mod: +or: +pow: +xor: +-- +add: 42 +cat: 42False +sub: 42 +mul: 0 +and: 0 +div: +eqv: -43 +idiv: +imp: -43 +mod: +or: 42 +pow: 1 +xor: 42 +-- +add: 45.5 +cat: 3.542 +sub: -38.5 +mul: 147 +and: 0 +div: 8.33333333333333E-02 +eqv: -47 +idiv: 0 +imp: -5 +mod: 4 +or: 46 +pow: 7.09345573078604E+22 +xor: 46 +-- +add: 7 +cat: 3.53.5 +sub: 0 +mul: 12.25 +and: 4 +div: 1 +eqv: -1 +idiv: 1 +imp: -1 +mod: 0 +or: 4 +pow: 80.2117802289664 +xor: 0 +-- +add: +cat: 3.5hello +sub: +mul: +and: +div: +eqv: +idiv: +imp: +mod: +or: +pow: +xor: +-- +add: 3.5 +cat: 3.5False +sub: 3.5 +mul: 0 +and: 0 +div: +eqv: -5 +idiv: +imp: -5 +mod: +or: 4 +pow: 1 +xor: 4 +-- +add: +cat: hello42 +sub: +mul: +and: +div: +eqv: +idiv: +imp: +mod: +or: +pow: +xor: +-- +add: +cat: hello3.5 +sub: +mul: +and: +div: +eqv: +idiv: +imp: +mod: +or: +pow: +xor: +-- +add: hellohello +cat: hellohello +sub: +mul: +and: +div: +eqv: +idiv: +imp: +mod: +or: +pow: +xor: +-- +add: +cat: helloFalse +sub: +mul: +and: +div: +eqv: +idiv: +imp: +mod: +or: +pow: +xor: +-- +add: 42 +cat: False42 +sub: -42 +mul: 0 +and: 0 +div: 0 +eqv: -43 +idiv: 0 +imp: -1 +mod: 0 +or: 42 +pow: 0 +xor: 42 +-- +add: 3.5 +cat: False3.5 +sub: -3.5 +mul: 0 +and: 0 +div: 0 +eqv: -5 +idiv: 0 +imp: -1 +mod: 0 +or: 4 +pow: 0 +xor: 4 +-- +add: +cat: Falsehello +sub: +mul: +and: +div: +eqv: +idiv: +imp: +mod: +or: +pow: +xor: +-- +add: 0 +cat: FalseFalse +sub: 0 +mul: 0 +and: 0 +div: +eqv: -1 +idiv: +imp: -1 +mod: +or: 0 +pow: 1 +xor: 0 +OK! diff --git a/main/config.w32.h b/main/config.w32.h index c5fd2ba94d..fc4c88bf5b 100644 --- a/main/config.w32.h +++ b/main/config.w32.h @@ -47,6 +47,10 @@ /* #define HAVE_MBSTR_RU 0 */ /* #define HAVE_MBSTR_TW 0 */ +/* If you have the .Net SDK in your include path, define this + * to compile .Net support into your COM extension. */ +#define HAVE_MSCOREE_H 0 + /* Enable / Disable ODBC extension (default: enabled) */ #define HAVE_UODBC 1 diff --git a/main/internal_functions_win32.c b/main/internal_functions_win32.c index 8ca1e407e6..0fff4f8c80 100644 --- a/main/internal_functions_win32.c +++ b/main/internal_functions_win32.c @@ -97,6 +97,7 @@ #ifdef HAVE_SQLITE #include "ext/sqlite/php_sqlite.h" #endif +#include "ext/com_dotnet/php_com_dotnet.h" /* }}} */ /* {{{ php_builtin_extensions[] @@ -109,6 +110,7 @@ zend_module_entry *php_builtin_extensions[] = { #if HAVE_CALENDAR ,phpext_calendar_ptr #endif + ,phpext_com_dotnet_ptr #if HAVE_CTYPE ,phpext_ctype_ptr #endif @@ -135,14 +137,14 @@ zend_module_entry *php_builtin_extensions[] = { #endif #if HAVE_LIBXML #if HAVE_DOM - ,phpext_dom_ptr + phpext_dom_ptr #endif #if HAVE_SIMPLEXML - ,phpext_simplexml_ptr + phpext_simplexml_ptr #endif #endif #if HAVE_XML - ,phpext_xml_ptr + phpext_xml_ptr #endif #if HAVE_XML && HAVE_WDDX ,phpext_wddx_ptr -- cgit v1.2.1