summaryrefslogtreecommitdiff
path: root/ext/com_dotnet
diff options
context:
space:
mode:
authorWez Furlong <wez@php.net>2003-08-14 16:49:56 +0000
committerWez Furlong <wez@php.net>2003-08-14 16:49:56 +0000
commit6df5d5ba202b531de6bb563e2462e046d701e8d6 (patch)
tree02fd4627ab540467f21eb30f96fb10ce34a6f371 /ext/com_dotnet
parente6b916cb1543c26d830a6818b012c5ace09b3139 (diff)
downloadphp-git-6df5d5ba202b531de6bb563e2462e046d701e8d6.tar.gz
Add new COM (and .Net) extension for php5.
Not yet complete, but should work for most people.
Diffstat (limited to 'ext/com_dotnet')
-rw-r--r--ext/com_dotnet/README79
-rw-r--r--ext/com_dotnet/com_com.c429
-rw-r--r--ext/com_dotnet/com_dotnet.c195
-rw-r--r--ext/com_dotnet/com_extension.c242
-rw-r--r--ext/com_dotnet/com_handlers.c586
-rw-r--r--ext/com_dotnet/com_misc.c102
-rw-r--r--ext/com_dotnet/com_olechar.c103
-rw-r--r--ext/com_dotnet/com_typeinfo.c259
-rw-r--r--ext/com_dotnet/com_variant.c846
-rw-r--r--ext/com_dotnet/php_com_dotnet.h70
-rw-r--r--ext/com_dotnet/php_com_dotnet_internal.h143
-rw-r--r--ext/com_dotnet/tests/variants.phpt263
12 files changed, 3317 insertions, 0 deletions
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:
+
+<?php
+$word = new COM("word.application");
+print "Loaded Word, version {$word->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 <wez@thebrainroom.com> |
+ +----------------------------------------------------------------------+
+ */
+
+/* $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, "<b>Source:</b> %s<br/><b>Description:</b> %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 <wez@thebrainroom.com> |
+ +----------------------------------------------------------------------+
+ */
+
+/* $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 <mscoree.h>
+
+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, &params, &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 <wez@thebrainroom.com> |
+ +----------------------------------------------------------------------+
+ */
+
+/* $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 <wez@thebrainroom.com> |
+ +----------------------------------------------------------------------+
+ */
+
+/* $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 <wez@thebrainroom.com> |
+ +----------------------------------------------------------------------+
+ */
+
+/* $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 <wez@thebrainroom.com> |
+ | Harald Radi <h.radi@nme.at> |
+ +----------------------------------------------------------------------+
+ */
+
+/* $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 <wez@thebrainroom.com> |
+ | Harald Radi <h.radi@nme.at> |
+ +----------------------------------------------------------------------+
+ */
+
+/* $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 <wez@thebrainroom.com> |
+ +----------------------------------------------------------------------+
+ */
+
+/* $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",
+ &timestamp)) {
+ return;
+ }
+
+ VariantInit(&res);
+ tmv = gmtime(&timestamp);
+ 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 <wez@thebrainroom.com> |
+ +----------------------------------------------------------------------+
+ */
+
+/* $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 <wez@thebrainroom.com> |
+ +----------------------------------------------------------------------+
+ */
+
+/* $Id$ */
+
+#ifndef PHP_COM_DOTNET_INTERNAL_H
+#define PHP_COM_DOTNET_INTERNAL_H
+
+#define _WIN32_DCOM
+#define COBJMACROS
+#include <ocidl.h>
+#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--
+<?php # vim:ft=php
+if (!extension_loaded("com_dotnet")) print "skip"; ?>
+--FILE--
+<?php // $Id$
+error_reporting(E_ALL);
+
+$v = new VARIANT();
+if (VT_EMPTY != variant_get_type($v)) {
+ echo "VT_EMPTY: bork\n";
+}
+
+$values = array(VT_I4 => 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!