summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWez Furlong <wez@php.net>2002-05-20 01:35:29 +0000
committerWez Furlong <wez@php.net>2002-05-20 01:35:29 +0000
commite754361405395908abd687b1403215da2bd98b92 (patch)
treee695f256b31e3a721667dba65199d34869455948
parent50f7427e69741acf9094a0f63c35503bfa4afca6 (diff)
downloadphp-git-e754361405395908abd687b1403215da2bd98b92.tar.gz
Implement ActiveScript interfaces.
This allows use of PHP in: Client-side script in Internet Explorer Windows Scripting Host ASP and ASP.NET pages It's mostly working... give it a go. You will need to regsvr32 the php4activescript.dll manually.
-rw-r--r--sapi/activescript/classfactory.cpp361
-rw-r--r--sapi/activescript/php4activescript.c156
-rw-r--r--sapi/activescript/php4activescript.def5
-rw-r--r--sapi/activescript/php4activescript.dsp185
-rw-r--r--sapi/activescript/php4activescript.h26
-rw-r--r--sapi/activescript/php4as_classfactory.h52
-rw-r--r--sapi/activescript/php4as_scriptengine.h234
-rw-r--r--sapi/activescript/scriptengine.cpp1813
8 files changed, 2832 insertions, 0 deletions
diff --git a/sapi/activescript/classfactory.cpp b/sapi/activescript/classfactory.cpp
new file mode 100644
index 0000000000..3d4f256db6
--- /dev/null
+++ b/sapi/activescript/classfactory.cpp
@@ -0,0 +1,361 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 4 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2002 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 2.02 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available at through the world-wide-web at |
+ | http://www.php.net/license/2_02.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. |
+ +----------------------------------------------------------------------+
+ | Authors: Wez Furlong <wez@thebrainroom.com> |
+ +----------------------------------------------------------------------+
+ */
+/* $Id$ */
+
+/* IClassFactory Implementation, and DllXXX function implementation */
+
+#define INITGUID
+#define DEBUG_CLASS_FACTORY 0
+
+#include <windows.h>
+#include <initguid.h>
+
+extern "C" {
+HINSTANCE module_handle;
+}
+
+#include <comcat.h>
+#include "TSRM.h"
+#include "php4as_classfactory.h"
+#include "php4as_scriptengine.h"
+
+volatile LONG TPHPClassFactory::factory_count = 0;
+volatile LONG TPHPClassFactory::object_count = 0;
+
+/* {{{ Class Factory Implementation */
+TPHPClassFactory::TPHPClassFactory()
+{
+ m_refcount = 1;
+ InterlockedIncrement(&factory_count);
+}
+
+TPHPClassFactory::~TPHPClassFactory()
+{
+ InterlockedDecrement(&factory_count);
+}
+
+void TPHPClassFactory::AddToObjectCount(void)
+{
+ InterlockedIncrement(&object_count);
+}
+
+void TPHPClassFactory::RemoveFromObjectCount(void)
+{
+ InterlockedDecrement(&object_count);
+}
+
+STDMETHODIMP_(DWORD) TPHPClassFactory::AddRef(void)
+{
+ return InterlockedIncrement(&m_refcount);
+}
+
+STDMETHODIMP_(DWORD) TPHPClassFactory::Release(void)
+{
+ DWORD ret = InterlockedDecrement(&m_refcount);
+ if (ret == 0)
+ delete this;
+ return ret;
+}
+
+STDMETHODIMP TPHPClassFactory::QueryInterface(REFIID iid, void **ppvObject)
+{
+ *ppvObject = NULL;
+
+ if (IsEqualGUID(IID_IClassFactory, iid)) {
+ *ppvObject = (IClassFactory*)this;
+ } else if (IsEqualGUID(IID_IUnknown, iid)) {
+ *ppvObject = this;
+ }
+ if (*ppvObject) {
+ AddRef();
+ return S_OK;
+ }
+
+ return E_NOINTERFACE;
+}
+
+STDMETHODIMP TPHPClassFactory::LockServer(BOOL fLock)
+{
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP TPHPClassFactory::CreateInstance(IUnknown *pUnkOuter, REFIID iid, void **ppvObject)
+{
+ TPHPScriptingEngine *engine = new TPHPScriptingEngine;
+
+ HRESULT ret = engine->QueryInterface(iid, ppvObject);
+
+ engine->Release();
+
+ return ret;
+}
+
+int TPHPClassFactory::CanUnload(void)
+{
+ return (object_count + factory_count) == 0 ? 1 : 0;
+}
+
+/* }}} */
+
+/* {{{ Registration structures */
+struct reg_entry {
+ HKEY root_key;
+ char *sub_key;
+ char *value_name;
+ char *data;
+};
+
+struct reg_class {
+ /* REFIID here causes compilation errors */
+ const GUID *class_id;
+ char *class_name;
+ char *threading;
+ const struct reg_entry *reg;
+ const GUID **cats;
+};
+
+#define MAX_REG_SUBST 8
+struct reg_subst {
+ int count;
+ char *names[MAX_REG_SUBST];
+ char *values[MAX_REG_SUBST];
+};
+/* }}} */
+
+/* {{{ Registration information */
+/* This registers the sapi as a scripting engine that can be instantiated using it's progid */
+static const struct reg_entry engine_entries[] = {
+ { HKEY_CLASSES_ROOT, "CLSID\\[CLSID]", NULL, "[CLASSNAME]" },
+ { HKEY_CLASSES_ROOT, "CLSID\\[CLSID]\\InprocServer32", NULL, "[MODULENAME]" },
+ { HKEY_CLASSES_ROOT, "CLSID\\[CLSID]\\InprocServer32", "ThreadingModel", "[THREADING]" },
+ { HKEY_CLASSES_ROOT, "CLSID\\[CLSID]\\OLEScript", NULL, NULL },
+ { HKEY_CLASSES_ROOT, "CLSID\\[CLSID]\\ProgID", NULL, "ActivePHP" },
+ { HKEY_CLASSES_ROOT, "ActivePHP", NULL, "ActivePHP" },
+ { HKEY_CLASSES_ROOT, "ActivePHP\\CLSID", NULL, "[CLSID]"},
+ { HKEY_CLASSES_ROOT, "ActivePHP\\OLEScript", NULL, NULL},
+
+ { 0, NULL, NULL, NULL }
+};
+
+static const GUID *script_engine_categories[] = {
+ &CATID_ActiveScript,
+ &CATID_ActiveScriptParse,
+ NULL
+};
+
+static const struct reg_class classes_to_register[] = {
+ { &CLSID_PHPActiveScriptEngine, "PHP Active Script Engine", "Both", engine_entries, script_engine_categories },
+ { NULL, NULL, NULL, 0, NULL }
+};
+/* }}} */
+
+/* {{{ Registration code */
+static void substitute(struct reg_subst *subst, char *srcstring, char *deststring)
+{
+ int i;
+ char *srcpos = srcstring;
+ char *destpos = deststring;
+
+ while(1) {
+ char *repstart = strchr(srcpos, '[');
+
+ if (repstart == NULL) {
+ strcpy(destpos, srcpos);
+ break;
+ }
+
+ /* copy everything up until that character to the dest */
+ strncpy(destpos, srcpos, repstart - srcpos);
+ destpos += repstart - srcpos;
+ *destpos = 0;
+
+ /* search for replacement strings in the table */
+ for (i = 0; i < subst->count; i++) {
+ int len = strlen(subst->names[i]);
+ if (strncmp(subst->names[i], repstart + 1, len) == 0) {
+ /* append the replacement value to the buffer */
+ strcpy(destpos, subst->values[i]);
+ destpos += strlen(subst->values[i]);
+ srcpos = repstart + len + 2;
+
+ break;
+ }
+ }
+ }
+#if DEBUG_CLASS_FACTORY
+ MessageBox(0, deststring, srcstring, MB_ICONHAND);
+#endif
+}
+
+static int reg_string(BOOL do_reg, struct reg_subst *subst, const struct reg_entry *entry)
+{
+ char subbuf[MAX_PATH], valuebuf[MAX_PATH], databuf[MAX_PATH];
+ char *sub = NULL, *value = NULL, *data = NULL;
+ LRESULT res;
+ HKEY hkey;
+ DWORD disp;
+
+ if (entry->sub_key) {
+ substitute(subst, entry->sub_key, subbuf);
+ sub = subbuf;
+ }
+
+ if (entry->value_name) {
+ substitute(subst, entry->value_name, valuebuf);
+ value = valuebuf;
+ }
+
+ if (entry->data) {
+ substitute(subst, entry->data, databuf);
+ data = databuf;
+ }
+
+ if (do_reg) {
+ res = RegCreateKeyEx(entry->root_key, sub, 0, NULL, REG_OPTION_NON_VOLATILE,
+ KEY_WRITE, NULL, &hkey, &disp);
+ if (res == NOERROR) {
+ if (data)
+ res = RegSetValueEx(hkey, value, 0, REG_SZ, (LPBYTE)data, strlen(data) + 1);
+ RegCloseKey(hkey);
+ }
+ } else {
+ res = RegDeleteKey(entry->root_key, sub);
+ }
+ return res == NOERROR ? 1 : 0;
+}
+
+static int perform_registration(BOOL do_reg)
+{
+ char module_name[MAX_PATH];
+ char classid[40];
+ int ret = 0;
+ int i, j;
+ struct reg_subst subst = {0};
+ LPOLESTR classidw;
+ ICatRegister *catreg = NULL;
+ CATID cats[8];
+ int ncats;
+
+ CoInitialize(NULL);
+ CoCreateInstance(CLSID_StdComponentCategoriesMgr, NULL, CLSCTX_INPROC_SERVER, IID_ICatRegister, (LPVOID*)&catreg);
+
+ GetModuleFileName(module_handle, module_name, sizeof(module_name));
+
+ subst.names[0] = "CLSID";
+ subst.values[0] = classid;
+
+ subst.names[1] = "MODULENAME";
+ subst.values[1] = module_name;
+
+ subst.names[2] = "CLASSNAME";
+ subst.names[3] = "THREADING";
+
+ subst.count = 4;
+
+ for (i = 0; classes_to_register[i].class_id; i++) {
+ StringFromCLSID(*classes_to_register[i].class_id, &classidw);
+ WideCharToMultiByte(CP_ACP, 0, classidw, -1, classid, sizeof(classid), NULL, NULL);
+ classid[39] = 0;
+ CoTaskMemFree(classidw);
+
+ subst.values[2] = classes_to_register[i].class_name;
+ subst.values[3] = classes_to_register[i].threading;
+
+ if (catreg && classes_to_register[i].cats) {
+ ncats = 0;
+ while(classes_to_register[i].cats[ncats]) {
+ CopyMemory(&cats[ncats], classes_to_register[i].cats[ncats], sizeof(CATID));
+ ncats++;
+ }
+ }
+
+ if (do_reg) {
+ for (j = 0; classes_to_register[i].reg[j].root_key; j++) {
+ if (!reg_string(do_reg, &subst, &classes_to_register[i].reg[j])) {
+ ret = 0;
+ break;
+ }
+ }
+
+ if (catreg && ncats) {
+ catreg->RegisterClassImplCategories(*classes_to_register[i].class_id, ncats, cats);
+ }
+
+ } else {
+
+ if (catreg && ncats) {
+ catreg->UnRegisterClassImplCategories(*classes_to_register[i].class_id, ncats, cats);
+ }
+
+ /* do it in reverse order, so count the number of entries first */
+ for (j = 0; classes_to_register[i].reg[j].root_key; j++)
+ ;
+ while(j-- > 0) {
+ if (!reg_string(do_reg, &subst, &classes_to_register[i].reg[j])) {
+ ret = 0;
+ break;
+ }
+ }
+ }
+ }
+
+ if (catreg)
+ catreg->Release();
+ CoUninitialize();
+
+ return ret;
+}
+/* }}} */
+
+/* {{{ Standard COM server DllXXX entry points */
+
+STDAPI DllCanUnloadNow(void)
+{
+ if (TPHPClassFactory::CanUnload())
+ return S_OK;
+ return S_FALSE;
+}
+
+STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv)
+{
+ HRESULT ret = E_FAIL;
+
+ if (IsEqualCLSID(CLSID_PHPActiveScriptEngine, rclsid)) {
+ TPHPClassFactory *factory = new TPHPClassFactory();
+
+ if (factory) {
+ ret = factory->QueryInterface(riid, ppv);
+ factory->Release();
+ }
+ }
+ return ret;
+}
+
+
+
+STDAPI DllRegisterServer(void)
+{
+ return perform_registration(TRUE) ? S_OK : S_FALSE;
+}
+
+STDAPI DllUnregisterServer(void)
+{
+ return perform_registration(FALSE) ? S_OK : S_FALSE;
+}
+
+/* }}} */
diff --git a/sapi/activescript/php4activescript.c b/sapi/activescript/php4activescript.c
new file mode 100644
index 0000000000..eed1d7e52e
--- /dev/null
+++ b/sapi/activescript/php4activescript.c
@@ -0,0 +1,156 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 4 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2002 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 2.02 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available at through the world-wide-web at |
+ | http://www.php.net/license/2_02.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. |
+ +----------------------------------------------------------------------+
+ | Authors: Wez Furlong <wez@thebrainroom.com> |
+ +----------------------------------------------------------------------+
+ */
+/* $Id$ */
+
+#include "php.h"
+#include "php_main.h"
+#include "SAPI.h"
+#include "php_globals.h"
+#include "ext/standard/info.h"
+#include "php_variables.h"
+#include "php_ini.h"
+#include "php4activescript.h"
+
+/* SAPI definitions and DllMain */
+
+static int php_activescript_startup(sapi_module_struct *sapi_module)
+{
+ if (php_module_startup(sapi_module) == FAILURE ||
+ zend_startup_module(&php_activescript_module) == FAILURE) {
+ return FAILURE;
+ } else {
+ return SUCCESS;
+ }
+}
+
+static int sapi_activescript_ub_write(const char *str, uint str_length TSRMLS_DC)
+{
+ /* In theory, this is a blackhole. In practice, I wan't to see the output
+ * in the debugger! */
+
+ char buf[1024];
+ uint l, a = str_length;
+
+ while(a) {
+ l = a;
+ if (l > sizeof(buf) - 1)
+ l = sizeof(buf) - 1;
+ memcpy(buf, str, l);
+ buf[l] = 0;
+ OutputDebugString(buf);
+ a -= l;
+ }
+
+ return str_length;
+}
+
+static void sapi_activescript_register_server_variables(zval *track_vars_array TSRMLS_DC)
+{
+}
+
+static char *sapi_activescript_read_cookies(TSRMLS_D)
+{
+ return NULL;
+}
+
+static int sapi_activescript_header_handler(sapi_header_struct *sapi_header, sapi_headers_struct *sapi_headers TSRMLS_DC)
+{
+ return SAPI_HEADER_ADD;
+}
+
+static int sapi_activescript_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC)
+{
+ return SAPI_HEADER_SENT_SUCCESSFULLY;
+}
+
+zend_module_entry php_activescript_module = {
+ STANDARD_MODULE_HEADER,
+ "ActiveScript",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ STANDARD_MODULE_PROPERTIES
+};
+
+
+sapi_module_struct activescript_sapi_module = {
+ "activescript", /* name */
+ "Active Script", /* pretty name */
+
+ php_activescript_startup, /* startup */
+ php_module_shutdown_wrapper, /* shutdown */
+
+ NULL, /* activate */
+ NULL, /* deactivate */
+
+ sapi_activescript_ub_write, /* unbuffered write */
+ NULL, /* flush */
+ NULL, /* get uid */
+ NULL, /* getenv */
+
+ zend_error, /* error handler */
+
+ sapi_activescript_header_handler, /* header handler */
+ sapi_activescript_send_headers, /* send headers handler */
+ NULL, /* send header handler */
+
+ NULL, /* read POST data */
+ sapi_activescript_read_cookies, /* read Cookies */
+
+ sapi_activescript_register_server_variables, /* register server variables */
+ NULL, /* Log message */
+
+ NULL, /* Block interruptions */
+ NULL, /* Unblock interruptions */
+
+ STANDARD_SAPI_MODULE_PROPERTIES
+};
+
+BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
+{
+ switch (fdwReason) {
+ case DLL_PROCESS_ATTACH:
+ module_handle = hinstDLL;
+
+ tsrm_startup(128, 1, TSRM_ERROR_LEVEL_CORE, "C:\\TSRM.log");
+
+ sapi_startup(&activescript_sapi_module);
+ if (activescript_sapi_module.startup) {
+ activescript_sapi_module.startup(&sapi_module);
+ }
+ break;
+ case DLL_THREAD_ATTACH:
+ break;
+ case DLL_THREAD_DETACH:
+ ts_free_thread();
+ break;
+ case DLL_PROCESS_DETACH:
+ if (activescript_sapi_module.shutdown) {
+ activescript_sapi_module.shutdown(&sapi_module);
+ }
+ tsrm_shutdown();
+ break;
+ }
+ return TRUE;
+}
+
+
diff --git a/sapi/activescript/php4activescript.def b/sapi/activescript/php4activescript.def
new file mode 100644
index 0000000000..1f8e945838
--- /dev/null
+++ b/sapi/activescript/php4activescript.def
@@ -0,0 +1,5 @@
+EXPORTS
+DllCanUnloadNow @1 PRIVATE
+DllGetClassObject @2 PRIVATE
+DllRegisterServer @3 PRIVATE
+DllUnregisterServer @4 PRIVATE
diff --git a/sapi/activescript/php4activescript.dsp b/sapi/activescript/php4activescript.dsp
new file mode 100644
index 0000000000..cbfafa1272
--- /dev/null
+++ b/sapi/activescript/php4activescript.dsp
@@ -0,0 +1,185 @@
+# Microsoft Developer Studio Project File - Name="php4activescript" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=php4activescript - Win32 Debug_TS
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "php4activescript.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "php4activescript.mak" CFG="php4activescript - Win32 Debug_TS"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "php4activescript - Win32 Debug_TS" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "php4activescript - Win32 Release_TS" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "php4activescript - Win32 Release_TS_inline" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "php4activescript - Win32 Release_TSDbg" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "php4activescript - Win32 Debug_TS"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug_TS"
+# PROP BASE Intermediate_Dir "Debug_TS"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "..\..\Debug_TS"
+# PROP Intermediate_Dir "Debug_TS"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "PHP4ISAPI_EXPORTS" /YX /FD /GZ /c
+# ADD CPP /nologo /MDd /Ze /W3 /Gm /GX /ZI /Od /Ob0 /I "..\..\win32" /I "..\..\Zend" /I "..\.." /I "..\..\main" /I "..\..\TSRM" /D "_DEBUG" /D "COMPILE_LIBZEND" /D ZEND_DEBUG=1 /D "_WINDOWS" /D "_USRDLL" /D "PHP4ISAPI_EXPORTS" /D "MSVC5" /D "ZTS" /D "ZEND_WIN32" /D "PHP_WIN32" /D "WIN32" /D "_MBCS" /FR /YX /FD /GZ /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x40d /d "_DEBUG"
+# ADD RSC /l 0x40d /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 wsock32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib php4ts_debug.lib /nologo /version:4.0 /dll /debug /machine:I386 /nodefaultlib:"libcmt" /pdbtype:sept /libpath:"..\..\Debug_TS"
+
+!ELSEIF "$(CFG)" == "php4activescript - Win32 Release_TS"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release_TS"
+# PROP BASE Intermediate_Dir "Release_TS"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "..\..\Release_TS"
+# PROP Intermediate_Dir "Release_TS"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "PHP4ISAPI_EXPORTS" /YX /FD /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /I "...\..\include" /I "..\..\win32" /I "..\..\Zend" /I "..\.." /I "..\..\main" /I "..\..\TSRM" /D "NDEBUG" /D ZEND_DEBUG=0 /D "_WINDOWS" /D "_USRDLL" /D "PHP4ISAPI_EXPORTS" /D "MSVC5" /D "ZTS" /D "ZEND_WIN32" /D "PHP_WIN32" /D "WIN32" /D "_MBCS" /FR /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x40d /d "NDEBUG"
+# ADD RSC /l 0x40d /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib wsock32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib php4ts.lib /nologo /version:4.0 /dll /machine:I386 /libpath:"..\..\Release_TS"
+
+!ELSEIF "$(CFG)" == "php4activescript - Win32 Release_TS_inline"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "php4activescript___Win32_Release_TS_inline"
+# PROP BASE Intermediate_Dir "php4activescript___Win32_Release_TS_inline"
+# PROP BASE Ignore_Export_Lib 0
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "..\..\Release_TS_inline"
+# PROP Intermediate_Dir "Release_TS_inline"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MD /W3 /GX /O2 /I "...\..\include" /I "..\..\win32" /I "..\..\Zend" /I "..\.." /D "NDEBUG" /D "_WINDOWS" /D "_USRDLL" /D "PHP4ISAPI_EXPORTS" /D "MSVC5" /D "ZTS" /D "WIN32" /D "_MBCS" /D ZEND_DEBUG=0 /FR /YX /FD /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /I "...\..\include" /I "..\..\win32" /I "..\..\Zend" /I "..\.." /I "..\..\main" /I "..\..\TSRM" /D "NDEBUG" /D ZEND_DEBUG=0 /D "ZEND_WIN32_FORCE_INLINE" /D "_WINDOWS" /D "_USRDLL" /D "PHP4ISAPI_EXPORTS" /D "MSVC5" /D "ZTS" /D "ZEND_WIN32" /D "PHP_WIN32" /D "WIN32" /D "_MBCS" /FR /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x40d /d "NDEBUG"
+# ADD RSC /l 0x40d /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib wsock32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib php4ts.lib /nologo /dll /machine:I386 /libpath:"..\..\Release_TS"
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib wsock32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib php4ts.lib /nologo /version:4.0 /dll /machine:I386 /libpath:"..\..\Release_TS_inline"
+
+!ELSEIF "$(CFG)" == "php4activescript - Win32 Release_TSDbg"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "php4activescript___Win32_Release_TSDbg"
+# PROP BASE Intermediate_Dir "php4activescript___Win32_Release_TSDbg"
+# PROP BASE Ignore_Export_Lib 0
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "..\..\Release_TSDbg"
+# PROP Intermediate_Dir "Release_TSDbg"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MD /W3 /GX /O2 /I "...\..\include" /I "..\..\win32" /I "..\..\Zend" /I "..\.." /I "..\..\main" /I "..\..\TSRM" /D "NDEBUG" /D ZEND_DEBUG=0 /D "_WINDOWS" /D "_USRDLL" /D "PHP4ISAPI_EXPORTS" /D "MSVC5" /D "ZTS" /D "ZEND_WIN32" /D "PHP_WIN32" /D "WIN32" /D "_MBCS" /FR /YX /FD /c
+# ADD CPP /nologo /MD /W3 /GX /Zi /Od /I "...\..\include" /I "..\..\win32" /I "..\..\Zend" /I "..\.." /I "..\..\main" /I "..\..\TSRM" /D "NDEBUG" /D ZEND_DEBUG=0 /D "_WINDOWS" /D "_USRDLL" /D "PHP4ISAPI_EXPORTS" /D "MSVC5" /D "ZTS" /D "ZEND_WIN32" /D "PHP_WIN32" /D "WIN32" /D "_MBCS" /FR /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x40d /d "NDEBUG"
+# ADD RSC /l 0x40d /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib wsock32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib php4ts.lib /nologo /version:4.0 /dll /machine:I386 /libpath:"..\..\Release_TS"
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib wsock32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib php4ts.lib /nologo /version:4.0 /dll /debug /machine:I386 /libpath:"..\..\Release_TSDbg"
+
+!ENDIF
+
+# Begin Target
+
+# Name "php4activescript - Win32 Debug_TS"
+# Name "php4activescript - Win32 Release_TS"
+# Name "php4activescript - Win32 Release_TS_inline"
+# Name "php4activescript - Win32 Release_TSDbg"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\classfactory.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\php4activescript.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\php4activescript.def
+# End Source File
+# Begin Source File
+
+SOURCE=.\scriptengine.cpp
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\php4activescript.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\php4as_classfactory.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\php4as_scriptengine.h
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/sapi/activescript/php4activescript.h b/sapi/activescript/php4activescript.h
new file mode 100644
index 0000000000..423958fba0
--- /dev/null
+++ b/sapi/activescript/php4activescript.h
@@ -0,0 +1,26 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 4 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2002 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 2.02 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available at through the world-wide-web at |
+ | http://www.php.net/license/2_02.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. |
+ +----------------------------------------------------------------------+
+ | Authors: Wez Furlong <wez@thebrainroom.com> |
+ +----------------------------------------------------------------------+
+ */
+/* $Id$ */
+
+extern zend_module_entry php_activescript_module;
+extern sapi_module_struct activescript_sapi_module;
+extern HINSTANCE module_handle;
+extern void activescript_error_func(int type, const char *error_msg, ...);
+extern void activescript_error_handler(int type, const char *error_filename,
+ const uint error_lineno, const char *format, va_list args);
+
diff --git a/sapi/activescript/php4as_classfactory.h b/sapi/activescript/php4as_classfactory.h
new file mode 100644
index 0000000000..bc4d530c03
--- /dev/null
+++ b/sapi/activescript/php4as_classfactory.h
@@ -0,0 +1,52 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 4 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2002 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 2.02 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available at through the world-wide-web at |
+ | http://www.php.net/license/2_02.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. |
+ +----------------------------------------------------------------------+
+ | Authors: Wez Furlong <wez@thebrainroom.com> |
+ +----------------------------------------------------------------------+
+ */
+/* $Id$ */
+
+/* IClassFactory Implementation */
+
+#include <unknwn.h>
+
+// {A0AD8E7A-95EC-4819-986F-78D93895F2AE}
+DEFINE_GUID(CLSID_PHPActiveScriptEngine,
+0xa0ad8e7a, 0x95ec, 0x4819, 0x98, 0x6f, 0x78, 0xd9, 0x38, 0x95, 0xf2, 0xae);
+
+class TPHPClassFactory:
+ public IClassFactory
+{
+protected:
+ volatile LONG m_refcount;
+
+ static volatile LONG factory_count;
+ static volatile LONG object_count;
+
+public: /* IUnknown */
+ STDMETHODIMP QueryInterface(REFIID iid, void **ppvObject);
+ STDMETHODIMP_(DWORD) AddRef(void);
+ STDMETHODIMP_(DWORD) Release(void);
+public: /* IClassFactory */
+ STDMETHODIMP CreateInstance(IUnknown *pUnkOuter, REFIID iid, void **ppvObject);
+ STDMETHODIMP LockServer(BOOL fLock);
+
+ TPHPClassFactory();
+ ~TPHPClassFactory();
+
+ static void AddToObjectCount(void);
+ static void RemoveFromObjectCount(void);
+ static int CanUnload(void);
+};
+
diff --git a/sapi/activescript/php4as_scriptengine.h b/sapi/activescript/php4as_scriptengine.h
new file mode 100644
index 0000000000..1d2314b5e2
--- /dev/null
+++ b/sapi/activescript/php4as_scriptengine.h
@@ -0,0 +1,234 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 4 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2002 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 2.02 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available at through the world-wide-web at |
+ | http://www.php.net/license/2_02.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. |
+ +----------------------------------------------------------------------+
+ | Authors: Wez Furlong <wez@thebrainroom.com> |
+ +----------------------------------------------------------------------+
+ */
+/* $Id$ */
+
+#include <activscp.h>
+#include "zend.h"
+#include <setjmp.h>
+
+/* Definitions for thread messages */
+enum {
+ PHPSE_STATE_CHANGE = WM_USER + 20,
+ PHPSE_INIT_NEW,
+ PHPSE_PARSE_SCRIPT,
+ PHPSE_ADD_SCRIPTLET,
+ PHPSE_CLOSE,
+ PHPSE_CLONE,
+ PHPSE_ENTER,
+ PHPSE_LEAVE,
+ PHPSE_TERMINATE,
+ PHPSE_PARSE_PROC,
+ PHPSE_EXEC_PROC,
+ PHPSE_ADD_NAMED_ITEM,
+ PHPSE_SET_SITE,
+ PHPSE_ADD_TYPELIB,
+ PHPSE_TRIGGER_ERROR,
+ PHPSE_GET_DISPATCH
+};
+
+struct php_active_script_get_dispatch_info {
+ LPCOLESTR pstrItemName;
+ LPSTREAM dispatch;
+};
+
+struct php_active_script_add_named_item_info {
+ LPCOLESTR pstrName;
+ DWORD dwFlags;
+ IUnknown *punk;
+ ITypeInfo *ptyp;
+ IDispatch *pdisp;
+ LPSTREAM marshal;
+};
+
+struct php_active_script_add_scriptlet_info {
+ /* [in] */ LPCOLESTR pstrDefaultName;
+ /* [in] */ LPCOLESTR pstrCode;
+ /* [in] */ LPCOLESTR pstrItemName;
+ /* [in] */ LPCOLESTR pstrSubItemName;
+ /* [in] */ LPCOLESTR pstrEventName;
+ /* [in] */ LPCOLESTR pstrDelimiter;
+ /* [in] */ DWORD dwSourceContextCookie;
+ /* [in] */ ULONG ulStartingLineNumber;
+ /* [in] */ DWORD dwFlags;
+ /* [out] */ BSTR *pbstrName;
+ /* [out] */ EXCEPINFO *pexcepinfo;
+};
+
+struct php_active_script_parse_info {
+ /* [in] */ LPCOLESTR pstrCode;
+ /* [in] */ LPCOLESTR pstrItemName;
+ /* [in] */ IUnknown *punkContext;
+ /* [in] */ LPCOLESTR pstrDelimiter;
+ /* [in] */ DWORD dwSourceContextCookie;
+ /* [in] */ ULONG ulStartingLineNumber;
+ /* [in] */ DWORD dwFlags;
+ /* [out] */ VARIANT *pvarResult;
+ /* [out] */ EXCEPINFO *pexcepinfo;
+};
+
+struct php_active_script_parse_proc_info {
+ /* [in] */ LPCOLESTR pstrCode;
+ /* [in] */ LPCOLESTR pstrFormalParams;
+ /* [in] */ LPCOLESTR pstrProcedureName;
+ /* [in] */ LPCOLESTR pstrItemName;
+ /* [in] */ IUnknown *punkContext;
+ /* [in] */ LPCOLESTR pstrDelimiter;
+ /* [in] */ DWORD dwSourceContextCookie;
+ /* [in] */ ULONG ulStartingLineNumber;
+ /* [in] */ DWORD dwFlags;
+ /* [out] */ IDispatch **ppdisp;
+};
+
+struct php_active_script_add_tlb_info {
+ /* [in] */ const GUID * rguidTypeLib;
+ /* [in] */ DWORD dwMajor;
+ /* [in] */ DWORD dwMinor;
+ /* [in] */ DWORD dwFlags;
+};
+
+class TPHPScriptingEngine:
+ public IActiveScript,
+ public IActiveScriptParse,
+ public IActiveScriptParseProcedure
+{
+public:
+ volatile LONG m_refcount;
+ IActiveScriptSite *m_pass;
+ SCRIPTSTATE m_scriptstate;
+ MUTEX_T m_mutex;
+ HashTable m_script_dispatchers;
+ HANDLE m_engine_thread_handle;
+
+ HANDLE m_sync_thread_msg;
+ HRESULT m_sync_thread_ret;
+
+ /* This is hacky, but only used when the host queries us for a script dispatch */
+ void *** m_tsrm_hack;
+
+ void add_to_global_namespace(IDispatch *disp, DWORD flags, char *name TSRMLS_DC);
+
+ THREAD_T m_enginethread, m_basethread;
+ HashTable m_frags;
+ ULONG m_lambda_count;
+ IActiveScriptSite *m_pass_eng;
+
+ jmp_buf *m_err_trap;
+ int m_in_main, m_stop_main;
+
+ HRESULT SendThreadMessage(LONG msg, WPARAM wparam, LPARAM lparam);
+
+ void engine_thread_func(void);
+ HRESULT engine_thread_handler(LONG msg, WPARAM wParam, LPARAM lParam, int *handled TSRMLS_DC);
+
+public: /* IUnknown */
+ STDMETHODIMP QueryInterface(REFIID iid, void **ppvObject);
+ STDMETHODIMP_(DWORD) AddRef(void);
+ STDMETHODIMP_(DWORD) Release(void);
+public: /* IActiveScript */
+ STDMETHODIMP SetScriptSite(
+ /* [in] */ IActiveScriptSite *pass);
+
+ STDMETHODIMP GetScriptSite(
+ /* [in] */ REFIID riid,
+ /* [iid_is][out] */ void **ppvObject);
+
+ STDMETHODIMP SetScriptState(
+ /* [in] */ SCRIPTSTATE ss);
+
+ STDMETHODIMP GetScriptState(
+ /* [out] */ SCRIPTSTATE *pssState);
+
+ STDMETHODIMP Close( void);
+
+ STDMETHODIMP AddNamedItem(
+ /* [in] */ LPCOLESTR pstrName,
+ /* [in] */ DWORD dwFlags);
+
+ STDMETHODIMP AddTypeLib(
+ /* [in] */ REFGUID rguidTypeLib,
+ /* [in] */ DWORD dwMajor,
+ /* [in] */ DWORD dwMinor,
+ /* [in] */ DWORD dwFlags);
+
+ STDMETHODIMP GetScriptDispatch(
+ /* [in] */ LPCOLESTR pstrItemName,
+ /* [out] */ IDispatch **ppdisp);
+
+ STDMETHODIMP GetCurrentScriptThreadID(
+ /* [out] */ SCRIPTTHREADID *pstidThread);
+
+ STDMETHODIMP GetScriptThreadID(
+ /* [in] */ DWORD dwWin32ThreadId,
+ /* [out] */ SCRIPTTHREADID *pstidThread);
+
+ STDMETHODIMP GetScriptThreadState(
+ /* [in] */ SCRIPTTHREADID stidThread,
+ /* [out] */ SCRIPTTHREADSTATE *pstsState);
+
+ STDMETHODIMP InterruptScriptThread(
+ /* [in] */ SCRIPTTHREADID stidThread,
+ /* [in] */ const EXCEPINFO *pexcepinfo,
+ /* [in] */ DWORD dwFlags);
+
+ STDMETHODIMP Clone(
+ /* [out] */ IActiveScript **ppscript);
+
+public: /* IActiveScriptParse */
+ STDMETHODIMP InitNew( void);
+
+ STDMETHODIMP AddScriptlet(
+ /* [in] */ LPCOLESTR pstrDefaultName,
+ /* [in] */ LPCOLESTR pstrCode,
+ /* [in] */ LPCOLESTR pstrItemName,
+ /* [in] */ LPCOLESTR pstrSubItemName,
+ /* [in] */ LPCOLESTR pstrEventName,
+ /* [in] */ LPCOLESTR pstrDelimiter,
+ /* [in] */ DWORD dwSourceContextCookie,
+ /* [in] */ ULONG ulStartingLineNumber,
+ /* [in] */ DWORD dwFlags,
+ /* [out] */ BSTR *pbstrName,
+ /* [out] */ EXCEPINFO *pexcepinfo);
+
+ STDMETHODIMP ParseScriptText(
+ /* [in] */ LPCOLESTR pstrCode,
+ /* [in] */ LPCOLESTR pstrItemName,
+ /* [in] */ IUnknown *punkContext,
+ /* [in] */ LPCOLESTR pstrDelimiter,
+ /* [in] */ DWORD dwSourceContextCookie,
+ /* [in] */ ULONG ulStartingLineNumber,
+ /* [in] */ DWORD dwFlags,
+ /* [out] */ VARIANT *pvarResult,
+ /* [out] */ EXCEPINFO *pexcepinfo);
+public: /* IActiveScriptParseProcedure */
+ STDMETHODIMP ParseProcedureText(
+ /* [in] */ LPCOLESTR pstrCode,
+ /* [in] */ LPCOLESTR pstrFormalParams,
+ /* [in] */ LPCOLESTR pstrProcedureName,
+ /* [in] */ LPCOLESTR pstrItemName,
+ /* [in] */ IUnknown *punkContext,
+ /* [in] */ LPCOLESTR pstrDelimiter,
+ /* [in] */ DWORD dwSourceContextCookie,
+ /* [in] */ ULONG ulStartingLineNumber,
+ /* [in] */ DWORD dwFlags,
+ /* [out] */ IDispatch **ppdisp);
+public:
+ TPHPScriptingEngine();
+ ~TPHPScriptingEngine();
+
+};
+
diff --git a/sapi/activescript/scriptengine.cpp b/sapi/activescript/scriptengine.cpp
new file mode 100644
index 0000000000..b0dc40dcb5
--- /dev/null
+++ b/sapi/activescript/scriptengine.cpp
@@ -0,0 +1,1813 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 4 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2002 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 2.02 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available at through the world-wide-web at |
+ | http://www.php.net/license/2_02.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. |
+ +----------------------------------------------------------------------+
+ | Authors: Wez Furlong <wez@thebrainroom.com> |
+ +----------------------------------------------------------------------+
+ */
+/* $Id$ */
+
+/* Implementation Notes:
+ *
+ * PHP stores scripting engine state in thread-local storage. That means
+ * that we need to create a dedicated thread per-engine so that a host can
+ * use more than one engine object per thread.
+ *
+ * There are some interesting synchronization issues: Anything to do with
+ * running script in the PHP/Zend engine must take place on the engine
+ * thread. Likewise, calling back to the host must take place on the base
+ * thread - the thread that set the script site.
+ *
+ * For talking to the site from engine thread, we use an invisible window:
+ * the window processing is guaranteed to occur in the correct thread,
+ * and the message queue provides a useful synchronization device.
+ *
+ * For talking to the engine from any other thread, the engine thread waits
+ * for messages to arrive at it's message queue. Since the only API for
+ * dealing with thread messages is asynchronous, we use a mutex to ensure
+ * that only one thread can talk to the engine at a time, and an event
+ * object to signal to it that the processing is complete.
+ *
+ * */
+
+#define _WIN32_DCOM
+
+#include "php.h"
+extern "C" {
+#include "php_main.h"
+#include "SAPI.h"
+#include "zend.h"
+#include "zend_execute.h"
+#include "zend_compile.h"
+#include "php_globals.h"
+#include "php_variables.h"
+#include "php_ini.h"
+#include "php4activescript.h"
+#include "ext/com/com.h"
+#include "ext/com/php_COM.h"
+#include "ext/com/conversion.h"
+}
+#include "php4as_scriptengine.h"
+#include "php4as_classfactory.h"
+#include <objbase.h>
+
+enum fragtype {
+ FRAG_MAIN,
+ FRAG_SCRIPTLET,
+ FRAG_PROCEDURE
+};
+
+typedef struct {
+ enum fragtype fragtype;
+ zend_op_array *opcodes;
+ char *code;
+ int persistent; /* should be retained for Clone */
+ int executed; /* for "main" */
+ char *functionname;
+ unsigned int codelen;
+ unsigned int starting_line;
+ TPHPScriptingEngine *engine;
+ void *ptr;
+} code_frag;
+
+#define FRAG_CREATE_FUNC (char*)-1
+static code_frag *compile_code_fragment(
+ enum fragtype fragtype,
+ char *functionname,
+ LPCOLESTR code,
+ ULONG starting_line,
+ EXCEPINFO *excepinfo,
+ TPHPScriptingEngine *engine
+ TSRMLS_DC);
+
+static int execute_code_fragment(code_frag *frag,
+ VARIANT *varResult,
+ EXCEPINFO *excepinfo
+ TSRMLS_DC);
+static void free_code_fragment(code_frag *frag);
+static code_frag *clone_code_fragment(code_frag *frag, TPHPScriptingEngine *engine TSRMLS_DC);
+
+/* Magic for handling threading correctly */
+static inline HRESULT SEND_THREAD_MESSAGE(TPHPScriptingEngine *engine, LONG msg, WPARAM wparam, LPARAM lparam TSRMLS_DC)
+{
+ if (engine->m_enginethread == 0)
+ return E_UNEXPECTED;
+ if (tsrm_thread_id() == (engine)->m_enginethread)
+ return (engine)->engine_thread_handler((msg), (wparam), (lparam), NULL TSRMLS_CC);
+ return (engine)->SendThreadMessage((msg), (wparam), (lparam));
+}
+
+
+
+/* {{{ scriptstate_to_string */
+static const char *scriptstate_to_string(SCRIPTSTATE ss)
+{
+ switch(ss) {
+ case SCRIPTSTATE_UNINITIALIZED: return "SCRIPTSTATE_UNINITIALIZED";
+ case SCRIPTSTATE_INITIALIZED: return "SCRIPTSTATE_INITIALIZED";
+ case SCRIPTSTATE_STARTED: return "SCRIPTSTATE_STARTED";
+ case SCRIPTSTATE_CONNECTED: return "SCRIPTSTATE_CONNECTED";
+ case SCRIPTSTATE_DISCONNECTED: return "SCRIPTSTATE_DISCONNECTED";
+ case SCRIPTSTATE_CLOSED: return "SCRIPTSTATE_CLOSED";
+ default:
+ return "unknown";
+ }
+}
+/* }}} */
+
+/* {{{ trace */
+static inline void trace(char *fmt, ...)
+{
+ va_list ap;
+ char buf[4096];
+
+ sprintf(buf, "T=%08x ", tsrm_thread_id());
+ OutputDebugString(buf);
+
+ va_start(ap, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+
+ OutputDebugString(buf);
+
+ va_end(ap);
+}
+/* }}} */
+
+/* {{{ TWideString */
+/* This class helps manipulate strings from OLE.
+ * It does not use emalloc, so it is better suited for passing pointers
+ * between threads. */
+class TWideString {
+ public:
+ LPOLESTR m_ole;
+ char *m_ansi;
+ int m_ansi_strlen;
+
+ TWideString(LPOLESTR olestr) {
+ m_ole = olestr;
+ m_ansi = NULL;
+ }
+ TWideString(LPCOLESTR olestr) {
+ m_ole = (LPOLESTR)olestr;
+ m_ansi = NULL;
+ }
+
+ ~TWideString() {
+ if (m_ansi) {
+ CoTaskMemFree(m_ansi);
+ }
+ m_ansi = NULL;
+ }
+
+ char *safe_ansi_string() {
+ char *ret = ansi_string();
+ if (ret == NULL)
+ return "<NULL>";
+ return ret;
+ }
+
+ int ansi_len(void) { return m_ansi_strlen; }
+
+ static BSTR bstr_from_ansi(char *ansi) {
+ OLECHAR *ole = NULL;
+ BSTR bstr = NULL;
+
+ int req = MultiByteToWideChar(CP_ACP, 0, ansi, -1, NULL, 0);
+ if (req) {
+ ole = (OLECHAR*)CoTaskMemAlloc((req + 1) * sizeof(OLECHAR));
+ if (ole) {
+ req = MultiByteToWideChar(CP_ACP, 0, ansi, -1, ole, req);
+ req--;
+ ole[req] = 0;
+
+ bstr = SysAllocString(ole);
+ CoTaskMemFree(ole);
+ }
+ }
+ return bstr;
+ }
+
+ char *ansi_string(void)
+ {
+ if (m_ansi)
+ return m_ansi;
+
+ if (m_ole == NULL)
+ return NULL;
+
+ int bufrequired = WideCharToMultiByte(CP_ACP, 0, m_ole, -1, NULL, 0, NULL, NULL);
+ if (bufrequired) {
+
+ m_ansi = (char*)CoTaskMemAlloc(bufrequired + 1);
+ if (m_ansi) {
+ m_ansi_strlen = WideCharToMultiByte(CP_ACP, 0, m_ole, -1, m_ansi, bufrequired + 1, NULL, NULL);
+
+ if (m_ansi_strlen) {
+ m_ansi_strlen--;
+ m_ansi[m_ansi_strlen] = 0;
+
+ } else {
+ trace("conversion failed with return code %08x\n", GetLastError());
+ }
+ }
+ }
+ return m_ansi;
+ }
+};
+/* }}} */
+
+/* {{{ A generic stupid IDispatch implementation */
+class IDispatchImpl:
+ public IDispatch
+{
+protected:
+ volatile LONG m_refcount;
+public:
+ /* IUnknown */
+ STDMETHODIMP QueryInterface(REFIID iid, void **ppvObject) {
+ *ppvObject = NULL;
+
+ if (IsEqualGUID(IID_IDispatch, iid)) {
+ *ppvObject = (IDispatch*)this;
+ } else if (IsEqualGUID(IID_IUnknown, iid)) {
+ *ppvObject = this;
+ }
+ if (*ppvObject) {
+ AddRef();
+ return S_OK;
+ }
+ return E_NOINTERFACE;
+ }
+
+ STDMETHODIMP_(DWORD) AddRef(void) {
+ return InterlockedIncrement(&m_refcount);
+ }
+
+ STDMETHODIMP_(DWORD) Release(void) {
+ DWORD ret = InterlockedDecrement(&m_refcount);
+ trace("%08x: IDispatchImpl: release ref count is now %d\n", this, ret);
+ if (ret == 0)
+ delete this;
+ return ret;
+ }
+ /* IDispatch */
+ STDMETHODIMP GetTypeInfoCount(unsigned int * pctinfo) {
+ *pctinfo = 0;
+ trace("%08x: IDispatchImpl: GetTypeInfoCount\n", this);
+ return S_OK;
+ }
+ STDMETHODIMP GetTypeInfo( unsigned int iTInfo, LCID lcid, ITypeInfo **ppTInfo) {
+ trace("%08x: IDispatchImpl: GetTypeInfo\n", this);
+ return DISP_E_BADINDEX;
+ }
+ STDMETHODIMP GetIDsOfNames( REFIID riid, OLECHAR **rgszNames, unsigned int cNames, LCID lcid, DISPID *rgDispId)
+ {
+ unsigned int i;
+ trace("%08x: IDispatchImpl: GetIDsOfNames: \n", this);
+ for (i = 0; i < cNames; i++) {
+ TWideString name(rgszNames[i]);
+ trace(" %s\n", name.ansi_string());
+ }
+ trace("----\n");
+ return DISP_E_UNKNOWNNAME;
+ }
+ STDMETHODIMP Invoke( DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags,
+ DISPPARAMS FAR* pDispParams, VARIANT FAR* pVarResult, EXCEPINFO FAR* pExcepInfo,
+ unsigned int FAR* puArgErr)
+ {
+ trace("%08x: IDispatchImpl: Invoke dispid %08x\n", this, dispIdMember);
+ return S_OK;
+ }
+
+
+ IDispatchImpl() {
+ m_refcount = 1;
+ }
+
+ virtual ~IDispatchImpl() {
+ }
+};
+/* }}} */
+
+/* {{{ This object represents the PHP engine to the scripting host.
+ * Although the docs say it's implementation is optional, I found that
+ * the Windows Script host would crash if we did not provide it. */
+class ScriptDispatch:
+ public IDispatchImpl
+{
+public:
+ ScriptDispatch() {
+ m_refcount = 1;
+ }
+};
+/* }}} */
+
+/* {{{ This object is used in conjunction with IActiveScriptParseProcedure to
+ * allow scriptlets to be bound to events. IE uses this for declaring
+ * event handlers such as onclick="...".
+ * The compiled code is stored in this object; IE will call
+ * IDispatch::Invoke when the element is clicked.
+ * */
+class ScriptProcedureDispatch:
+ public IDispatchImpl
+{
+public:
+ code_frag *m_frag;
+ DWORD m_procflags;
+ TPHPScriptingEngine *m_engine;
+
+ STDMETHODIMP Invoke( DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags,
+ DISPPARAMS FAR* pDispParams, VARIANT FAR* pVarResult, EXCEPINFO FAR* pExcepInfo,
+ unsigned int FAR* puArgErr)
+ {
+ TSRMLS_FETCH();
+
+ if (m_frag) {
+ trace("%08x: Procedure Dispatch: Invoke dispid %08x\n", this, dispIdMember);
+ SEND_THREAD_MESSAGE(m_engine, PHPSE_EXEC_PROC, 0, (LPARAM)this TSRMLS_CC);
+ }
+ return S_OK;
+ }
+ ScriptProcedureDispatch() {
+ m_refcount = 1;
+ }
+};
+/* }}} */
+
+/* {{{ code fragment management */
+static code_frag *compile_code_fragment(
+ enum fragtype fragtype,
+ char *functionname,
+ LPCOLESTR code,
+ ULONG starting_line,
+ EXCEPINFO *excepinfo,
+ TPHPScriptingEngine *engine
+ TSRMLS_DC)
+{
+ zval pv;
+ int code_offs = 0;
+ char namebuf[256];
+
+ code_frag *frag = (code_frag*)CoTaskMemAlloc(sizeof(code_frag));
+ memset(frag, 0, sizeof(code_frag));
+
+ frag->engine = engine;
+
+ /* handle the function name */
+ if (functionname) {
+ int namelen;
+ if (functionname == FRAG_CREATE_FUNC) {
+ ULONG n = ++engine->m_lambda_count;
+
+ sprintf(namebuf, "__frag_%08x_%u", engine, n);
+ functionname = namebuf;
+ }
+
+ namelen = strlen(functionname);
+ code_offs = namelen + sizeof("function (){");
+
+ frag->functionname = (char*)CoTaskMemAlloc((namelen + 1) * sizeof(char));
+ memcpy(frag->functionname, functionname, namelen+1);
+ }
+
+ frag->functionname = functionname;
+
+trace("%08x: COMPILED FRAG\n", frag);
+
+ frag->codelen = WideCharToMultiByte(CP_ACP, 0, code, -1, NULL, 0, NULL, NULL);
+ frag->code = (char*)CoTaskMemAlloc(sizeof(char) * (frag->codelen + code_offs + 1));
+
+ if (functionname) {
+ sprintf(frag->code, "function %s(){ ", functionname);
+ }
+
+ frag->codelen = WideCharToMultiByte(CP_ACP, 0, code, -1, frag->code + code_offs, frag->codelen, NULL, NULL) - 1;
+
+ if (functionname) {
+ frag->codelen += code_offs + 1;
+ frag->code[frag->codelen-1] = '}';
+ frag->code[frag->codelen] = 0;
+ }
+
+trace("code to compile is:\ncode_offs=%d func=%s\n%s\n", code_offs, functionname, frag->code);
+
+ frag->fragtype = fragtype;
+ frag->starting_line = starting_line;
+
+ pv.type = IS_STRING;
+ pv.value.str.val = frag->code;
+ pv.value.str.len = frag->codelen;
+
+ frag->opcodes = compile_string(&pv, "fragment" TSRMLS_CC);
+
+ if (frag->opcodes == NULL) {
+ free_code_fragment(frag);
+
+ if (excepinfo) {
+ memset(excepinfo, 0, sizeof(EXCEPINFO));
+ excepinfo->wCode = 1000;
+ excepinfo->bstrSource = TWideString::bstr_from_ansi("fragment");
+ excepinfo->bstrDescription = TWideString::bstr_from_ansi("Problem while parsing/compiling");
+ }
+
+ return NULL;
+ }
+
+ return frag;
+}
+
+static void free_code_fragment(code_frag *frag)
+{
+ switch(frag->fragtype) {
+ case FRAG_PROCEDURE:
+ if (frag->ptr) {
+ ScriptProcedureDispatch *disp = (ScriptProcedureDispatch*)frag->ptr;
+ disp->Release();
+ CoDisconnectObject((IUnknown*)disp, 0);
+ frag->ptr = NULL;
+ }
+ break;
+ }
+
+ if (frag->opcodes)
+ destroy_op_array(frag->opcodes);
+ if (frag->functionname)
+ CoTaskMemFree(frag->functionname);
+ CoTaskMemFree(frag->code);
+ CoTaskMemFree(frag);
+}
+
+static code_frag *clone_code_fragment(code_frag *frag, TPHPScriptingEngine *engine TSRMLS_DC)
+{
+ zval pv;
+ code_frag *newfrag = (code_frag*)CoTaskMemAlloc(sizeof(code_frag));
+ memset(newfrag, 0, sizeof(code_frag));
+
+ newfrag->engine = engine;
+trace("%08x: CLONED FRAG\n", newfrag);
+
+ newfrag->persistent = frag->persistent;
+ newfrag->codelen = frag->codelen;
+ newfrag->code = (char*)CoTaskMemAlloc(sizeof(char) * frag->codelen + 1);
+ memcpy(newfrag->code, frag->code, frag->codelen + 1);
+
+ if (frag->functionname) {
+ int namelen = strlen(frag->functionname);
+ newfrag->functionname = (char*)CoTaskMemAlloc(sizeof(char) * (namelen + 1));
+ memcpy(newfrag->functionname, frag->functionname, namelen+1);
+ } else {
+ newfrag->functionname = NULL;
+ }
+
+ newfrag->fragtype = frag->fragtype;
+ newfrag->starting_line = frag->starting_line;
+
+ pv.type = IS_STRING;
+ pv.value.str.val = newfrag->code;
+ pv.value.str.len = newfrag->codelen;
+
+ newfrag->opcodes = compile_string(&pv, "fragment" TSRMLS_CC);
+
+ if (newfrag->opcodes == NULL) {
+ free_code_fragment(newfrag);
+/*
+ if (excepinfo) {
+ memset(excepinfo, 0, sizeof(EXCEPINFO));
+ excepinfo->wCode = 1000;
+ excepinfo->bstrSource = TWideString::bstr_from_ansi("fragment");
+ excepinfo->bstrDescription = TWideString::bstr_from_ansi("Problem while parsing/compiling");
+ }
+*/
+ return NULL;
+ }
+
+ return newfrag;
+
+}
+
+static int execute_code_fragment(code_frag *frag,
+ VARIANT *varResult,
+ EXCEPINFO *excepinfo
+ TSRMLS_DC)
+{
+ zval *retval_ptr = NULL;
+ jmp_buf *orig_jmpbuf;
+ jmp_buf err_trap;
+
+ if (frag->fragtype == FRAG_MAIN && frag->executed)
+ return 1;
+
+ orig_jmpbuf = frag->engine->m_err_trap;
+ frag->engine->m_err_trap = &err_trap;
+
+ if (setjmp(err_trap) == 0) {
+ trace("*** Executing code in thread %08x\n", tsrm_thread_id());
+
+ if (frag->functionname) {
+
+ zval fname;
+
+ fname.type = IS_STRING;
+ fname.value.str.val = frag->functionname;
+ fname.value.str.len = strlen(frag->functionname);
+
+ call_user_function_ex(CG(function_table), NULL, &fname, &retval_ptr, 0, NULL, 1, NULL TSRMLS_CC);
+
+ } else {
+ zend_op_array *active_op_array = EG(active_op_array);
+ zend_function_state *function_state_ptr = EG(function_state_ptr);
+ zval **return_value_ptr_ptr = EG(return_value_ptr_ptr);
+ zend_op **opline_ptr = EG(opline_ptr);
+
+ EG(return_value_ptr_ptr) = &retval_ptr;
+ EG(active_op_array) = frag->opcodes;
+ EG(no_extensions) = 1;
+
+ zend_execute(frag->opcodes TSRMLS_CC);
+
+ EG(no_extensions) = 0;
+ EG(opline_ptr) = opline_ptr;
+ EG(active_op_array) = active_op_array;
+ EG(function_state_ptr) = function_state_ptr;
+ EG(return_value_ptr_ptr) = return_value_ptr_ptr;
+ }
+ } else {
+ trace("*** --> caught error while executing\n");
+ if (frag->engine->m_in_main)
+ frag->engine->m_stop_main = 1;
+ }
+
+ frag->engine->m_err_trap = orig_jmpbuf;
+
+ if (frag->fragtype == FRAG_MAIN)
+ frag->executed = 1;
+
+ if (varResult)
+ VariantInit(varResult);
+
+ if (retval_ptr) {
+ if (varResult)
+ php_pval_to_variant(retval_ptr, varResult, CP_ACP TSRMLS_CC);
+ zval_ptr_dtor(&retval_ptr);
+ }
+
+ return 1;
+}
+
+static void frag_dtor(void *pDest)
+{
+ code_frag *frag = *(code_frag**)pDest;
+ free_code_fragment(frag);
+}
+/* }}} */
+
+/* glue for getting back into the OO land */
+static DWORD WINAPI begin_engine_thread(LPVOID param)
+{
+ TPHPScriptingEngine *engine = (TPHPScriptingEngine*)param;
+ engine->engine_thread_func();
+ trace("engine thread has really gone away!\n");
+ return 0;
+}
+
+TPHPScriptingEngine::TPHPScriptingEngine()
+{
+ m_scriptstate = SCRIPTSTATE_UNINITIALIZED;
+ m_pass = NULL;
+ m_in_main = 0;
+ m_stop_main = 0;
+ m_err_trap = NULL;
+ m_lambda_count = 0;
+ m_pass_eng = NULL;
+ m_refcount = 1;
+ m_basethread = tsrm_thread_id();
+ m_mutex = tsrm_mutex_alloc();
+ m_sync_thread_msg = CreateEvent(NULL, TRUE, FALSE, NULL);
+ TPHPClassFactory::AddToObjectCount();
+
+ m_engine_thread_handle = CreateThread(NULL, 0, begin_engine_thread, this, 0, &m_enginethread);
+ CloseHandle(m_engine_thread_handle);
+}
+
+/* Synchronize with the engine thread */
+HRESULT TPHPScriptingEngine::SendThreadMessage(LONG msg, WPARAM wparam, LPARAM lparam)
+{
+ HRESULT ret;
+
+ if (m_enginethread == 0)
+ return E_UNEXPECTED;
+
+ trace("I'm waiting for a mutex in SendThreadMessage\n this=%08x ethread=%08x msg=%08x\n",
+ this, m_enginethread, msg);
+
+ tsrm_mutex_lock(m_mutex);
+ ResetEvent(m_sync_thread_msg);
+
+ /* If we call PostThreadMessage before the thread has created the queue, the message
+ * posting fails. MSDN docs recommend the following course of action */
+ while (!PostThreadMessage(m_enginethread, msg, wparam, lparam)) {
+ Sleep(50);
+ if (m_enginethread == 0) {
+ tsrm_mutex_unlock(m_mutex);
+ trace("breaking out of dodgy busy wait\n");
+ return E_UNEXPECTED;
+ }
+ }
+
+ /* Wait for the event object to be signalled.
+ * This is a nice "blocking without blocking" wait; window messages are dispatched
+ * and everything works out quite nicely */
+ while(1) {
+ DWORD result = MsgWaitForMultipleObjects(1, &m_sync_thread_msg, FALSE, 4000, QS_ALLINPUT);
+
+ if (result == WAIT_OBJECT_0 + 1) {
+ /* Dispatch some messages */
+ MSG msg;
+ while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
+ //trace("dispatching message while waiting\n");
+ DispatchMessage(&msg);
+ }
+ } else if (result == WAIT_TIMEOUT) {
+ trace("timeout while waiting for thread reply\n");
+
+ } else {
+ /* the event was signalled */
+ break;
+ }
+ }
+ ret = m_sync_thread_ret;
+ ResetEvent(m_sync_thread_msg);
+ tsrm_mutex_unlock(m_mutex);
+ return ret;
+}
+
+TPHPScriptingEngine::~TPHPScriptingEngine()
+{
+ trace("\n\n *** Engine Destructor Called\n\n");
+ if (m_scriptstate != SCRIPTSTATE_UNINITIALIZED && m_scriptstate != SCRIPTSTATE_CLOSED && m_enginethread)
+ Close();
+
+ PostThreadMessage(m_enginethread, WM_QUIT, 0, 0);
+
+ TPHPClassFactory::RemoveFromObjectCount();
+ tsrm_mutex_free(m_mutex);
+}
+
+/* Set some executor globals and execute a zend_op_array.
+ * The declaration looks wierd because this can be invoked from
+ * zend_hash_apply_with_argument */
+static int execute_main(void *pDest, void *arg TSRMLS_DC)
+{
+ code_frag *frag = *(code_frag**)pDest;
+
+ if (frag->fragtype == FRAG_MAIN && !(frag->engine->m_in_main && frag->engine->m_stop_main))
+ execute_code_fragment(frag, NULL, NULL TSRMLS_CC);
+
+ return ZEND_HASH_APPLY_KEEP;
+}
+
+static int clone_frags(void *pDest, void *arg TSRMLS_DC)
+{
+ code_frag *frag, *src = *(code_frag**)pDest;
+ TPHPScriptingEngine *engine = (TPHPScriptingEngine*)arg;
+
+ if (src->persistent) {
+ frag = clone_code_fragment(src, engine TSRMLS_CC);
+ if (frag)
+ zend_hash_next_index_insert(&engine->m_frags, &frag, sizeof(code_frag*), NULL);
+ else
+ trace("WARNING: clone failed!\n");
+ }
+
+ return ZEND_HASH_APPLY_KEEP;
+}
+
+HRESULT TPHPScriptingEngine::engine_thread_handler(LONG msg, WPARAM wparam, LPARAM lParam, int *handled TSRMLS_DC)
+{
+ HRESULT ret = S_OK;
+
+ trace("engine_thread_handler: running in thread %08x, should be %08x msg=%08x this=%08x\n",
+ tsrm_thread_id(), m_enginethread, msg, this);
+
+ if (handled)
+ *handled = 1;
+
+ if (m_enginethread == 0)
+ return E_UNEXPECTED;
+
+ switch(msg) {
+ case PHPSE_ADD_TYPELIB:
+ {
+ struct php_active_script_add_tlb_info *info = (struct php_active_script_add_tlb_info*)lParam;
+ ITypeLib *TypeLib;
+
+ if (SUCCEEDED(LoadRegTypeLib(*info->rguidTypeLib, (USHORT)info->dwMajor,
+ (USHORT)info->dwMinor, LANG_NEUTRAL, &TypeLib))) {
+ php_COM_load_typelib(TypeLib, CONST_CS TSRMLS_CC);
+ TypeLib->Release();
+ }
+ }
+ break;
+ case PHPSE_STATE_CHANGE:
+ {
+ /* handle the state change here */
+ SCRIPTSTATE ss = (SCRIPTSTATE)lParam;
+ int start_running = 0;
+ trace("%08x: DoSetScriptState(current=%s, new=%s)\n",
+ this,
+ scriptstate_to_string(m_scriptstate),
+ scriptstate_to_string(ss));
+
+ if (m_scriptstate == SCRIPTSTATE_INITIALIZED && (ss == SCRIPTSTATE_STARTED || ss == SCRIPTSTATE_CONNECTED))
+ start_running = 1;
+
+ m_scriptstate = ss;
+
+ /* inform host/site of the change */
+ if (m_pass_eng)
+ m_pass_eng->OnStateChange(m_scriptstate);
+
+ if (start_running) {
+ /* run "main()", as described in the docs */
+ if (m_pass_eng)
+ m_pass_eng->OnEnterScript();
+ trace("%08x: apply execute main to m_frags\n", this);
+ m_in_main = 1;
+ m_stop_main = 0;
+ zend_hash_apply_with_argument(&m_frags, execute_main, this TSRMLS_CC);
+ m_in_main = 0;
+ trace("%08x: --- done execute main\n", this);
+ if (m_pass_eng)
+ m_pass_eng->OnLeaveScript();
+
+ /* docs are a bit ambiguous here, but it appears that we should
+ * inform the host that the main script execution has completed,
+ * and also what the return value is */
+ VARIANT varRes;
+
+ VariantInit(&varRes);
+ if (m_pass_eng)
+ m_pass_eng->OnScriptTerminate(&varRes, NULL);
+
+ /*
+ m_scriptstate = SCRIPTSTATE_INITIALIZED;
+ if (m_pass_eng)
+ m_pass_eng->OnStateChange(m_scriptstate);
+ */
+ }
+ }
+ break;
+ case PHPSE_INIT_NEW:
+ {
+ /* Prepare PHP/ZE for use */
+
+ trace("%08x: m_frags : INIT NEW\n", this);
+ zend_hash_init(&m_frags, 0, NULL, frag_dtor, TRUE);
+
+ SG(options) |= SAPI_OPTION_NO_CHDIR;
+ SG(server_context) = this;
+ /* override the default PHP error callback */
+ zend_error_cb = activescript_error_handler;
+
+ zend_alter_ini_entry("register_argc_argv", 19, "1", 1, PHP_INI_SYSTEM, PHP_INI_STAGE_ACTIVATE);
+ zend_alter_ini_entry("html_errors", 12, "0", 1, PHP_INI_SYSTEM, PHP_INI_STAGE_ACTIVATE);
+ zend_alter_ini_entry("implicit_flush", 15, "1", 1, PHP_INI_SYSTEM, PHP_INI_STAGE_ACTIVATE);
+ zend_alter_ini_entry("max_execution_time", 19, "0", 1, PHP_INI_SYSTEM, PHP_INI_STAGE_ACTIVATE);
+
+ php_request_startup(TSRMLS_C);
+ PG(during_request_startup) = 0;
+
+ }
+ break;
+ case PHPSE_CLOSE:
+ {
+ /* Close things down */
+ trace("%08x: m_frags : CLOSE/DESTROY\n", this);
+ m_scriptstate = SCRIPTSTATE_CLOSED;
+ if (m_pass_eng) {
+ m_pass_eng->OnStateChange(m_scriptstate);
+ trace("%08x: release site from this side\n", this);
+ m_pass_eng->Release();
+ m_pass_eng = NULL;
+ }
+ zend_hash_destroy(&m_frags);
+ php_request_shutdown(NULL);
+
+ break;
+ }
+ break;
+ case PHPSE_CLONE:
+ {
+ /* Clone the engine state. This is semantically equal to serializing all
+ * the parsed code from the source and unserializing it in the dest (this).
+ * IE doesn't appear to use it, but Windows Script Host does. I'd expect
+ * ASP/ASP.NET to do so also.
+ *
+ * FIXME: Probably won't work with IActiveScriptParseProcedure scriplets
+ * */
+
+ TPHPScriptingEngine *src = (TPHPScriptingEngine*)lParam;
+
+ trace("%08x: m_frags : CLONE\n", this);
+ zend_hash_apply_with_argument(&src->m_frags, clone_frags, this TSRMLS_CC);
+
+ }
+ break;
+ case PHPSE_ADD_SCRIPTLET:
+ {
+ /* Parse/compile a chunk of script that will act as an event handler.
+ * If the host supports IActiveScriptParseProcedure, this code will
+ * not be called.
+ * The docs are (typically) vague: AFAICT, once the code has been
+ * compiled, we are supposed to arrange for an IConnectionPoint
+ * advisory connection to the item/subitem, once the script
+ * moves into SCRIPTSTATE_CONNECTED.
+ * That's a lot of work!
+ *
+ * FIXME: this is currently almost useless
+ * */
+
+ struct php_active_script_add_scriptlet_info *info = (struct php_active_script_add_scriptlet_info*)lParam;
+
+ TWideString
+ default_name(info->pstrDefaultName),
+ code(info->pstrCode),
+ item_name(info->pstrItemName),
+ sub_item_name(info->pstrSubItemName),
+ event_name(info->pstrEventName),
+ delimiter(info->pstrDelimiter);
+
+ /* lets invent a function name for the scriptlet */
+ char sname[256];
+
+ /* should check if the name is already used! */
+ if (info->pstrDefaultName)
+ strcpy(sname, default_name.ansi_string());
+ else {
+ sname[0] = 0;
+ strcat(sname, "__");
+ if (info->pstrItemName) {
+ strcat(sname, item_name.ansi_string());
+ strcat(sname, "_");
+ }
+ if (info->pstrSubItemName) {
+ strcat(sname, sub_item_name.ansi_string());
+ strcat(sname, "_");
+ }
+ if (info->pstrEventName)
+ strcat(sname, event_name.ansi_string());
+ }
+
+
+ trace("%08x: AddScriptlet:\n state=%s\n name=%s\n code=%s\n item=%s\n subitem=%s\n event=%s\n delim=%s\n line=%d\n",
+ this, scriptstate_to_string(m_scriptstate),
+ default_name.safe_ansi_string(), code.safe_ansi_string(), item_name.safe_ansi_string(),
+ sub_item_name.safe_ansi_string(), event_name.safe_ansi_string(), delimiter.safe_ansi_string(),
+ info->ulStartingLineNumber);
+
+
+ code_frag *frag = compile_code_fragment(
+ FRAG_SCRIPTLET,
+ sname,
+ info->pstrCode,
+ info->ulStartingLineNumber,
+ info->pexcepinfo,
+ this
+ TSRMLS_CC);
+
+ if (frag) {
+
+ frag->persistent = (info->dwFlags & SCRIPTTEXT_ISPERSISTENT);
+
+ zend_hash_next_index_insert(&m_frags, &frag, sizeof(code_frag*), NULL);
+
+ /*
+ ScriptProcedureDispatch *disp = new ScriptProcedureDispatch;
+
+ disp->AddRef();
+ disp->m_frag = frag;
+ disp->m_procflags = info->dwFlags;
+ disp->m_engine = this;
+ frag->ptr = disp;
+
+ *info->ppdisp = disp;
+ */
+ ret = S_OK;
+ } else {
+ ret = DISP_E_EXCEPTION;
+ }
+
+ *info->pbstrName = TWideString::bstr_from_ansi(sname);
+
+ trace("%08x: done with scriptlet %s\n", this, sname);
+
+ }
+ break;
+ case PHPSE_GET_DISPATCH:
+ {
+ struct php_active_script_get_dispatch_info *info = (struct php_active_script_get_dispatch_info *)lParam;
+ IDispatch *disp = NULL;
+ char *itemname;
+ unsigned int itemlen;
+
+ if (info->pstrItemName != NULL) {
+ zval **tmp;
+
+ itemname = php_OLECHAR_to_char((OLECHAR*)info->pstrItemName, &itemlen, CP_ACP TSRMLS_CC);
+
+ /* Get that item from the global namespace.
+ * If it is an object, export it as a dispatchable object.
+ * */
+
+ if (zend_hash_find(&EG(symbol_table), itemname, itemlen+1, (void**)&tmp) == SUCCESS) {
+ if (Z_TYPE_PP(tmp) == IS_OBJECT) {
+ disp = php_COM_export_object(*tmp TSRMLS_CC);
+ }
+ }
+ trace("%08x: GetScriptDispatch(%s --> %08x)\n", this, itemname, disp);
+
+ efree(itemname);
+
+ } else {
+#if 0
+ zval *obj;
+
+ MAKE_STD_ZVAL(obj);
+ object_init(obj);
+ disp = php_COM_export_object(obj TSRMLS_CC);
+#else
+
+ disp = (IDispatch*) new ScriptDispatch;
+#endif
+ trace("%08x: GetScriptDispatch(NULL --> %08x)\n", this, disp);
+ }
+
+ if (disp) {
+ trace("--- Marshaling to stream\n");
+ ret = CoMarshalInterThreadInterfaceInStream(IID_IDispatch, disp, &info->dispatch);
+ disp->Release();
+ } else {
+ ret = S_FALSE;
+ }
+ }
+ break;
+ case PHPSE_ADD_NAMED_ITEM:
+ {
+ /* The Host uses this to add objects to the global namespace.
+ * Some objects are intended to have their child properties
+ * globally visible, so we add those to the global namespace too.
+ * */
+ struct php_active_script_add_named_item_info *info = (struct php_active_script_add_named_item_info *)lParam;
+
+ TWideString name(info->pstrName);
+ IDispatch *disp;
+
+ if (SUCCEEDED(CoGetInterfaceAndReleaseStream(info->marshal, IID_IDispatch, (void**)&disp)))
+ add_to_global_namespace(disp, info->dwFlags, name.ansi_string() TSRMLS_CC);
+
+ }
+ break;
+ case PHPSE_SET_SITE:
+ {
+ LPSTREAM stream = (LPSTREAM)lParam;
+
+ if (m_pass_eng) {
+ m_pass_eng->Release();
+ m_pass_eng = NULL;
+ }
+
+ if (stream)
+ CoGetInterfaceAndReleaseStream(stream, IID_IActiveScriptSite, (void**)&m_pass_eng);
+
+ trace("%08x: site (engine-side) is now %08x (base=%08x)\n", this, m_pass_eng, m_pass);
+
+ }
+ break;
+ case PHPSE_EXEC_PROC:
+ {
+ ScriptProcedureDispatch *disp = (ScriptProcedureDispatch *)lParam;
+ execute_code_fragment(disp->m_frag, NULL, NULL TSRMLS_CC);
+ }
+ break;
+ case PHPSE_PARSE_PROC:
+ {
+ /* This is the IActiveScriptParseProcedure implementation.
+ * IE uses this to request for an IDispatch that it will invoke in
+ * response to some event, and tells us the code that it wants to
+ * run.
+ * We compile the code and pass it back a dispatch object.
+ * The object will then serialize access to the engine thread and
+ * execute the opcodes */
+ struct php_active_script_parse_proc_info *info = (struct php_active_script_parse_proc_info*)lParam;
+ TWideString
+ code(info->pstrCode),
+ formal_params(info->pstrFormalParams),
+ procedure_name(info->pstrProcedureName),
+ item_name(info->pstrItemName),
+ delimiter(info->pstrDelimiter);
+
+ trace("%08x: ParseProc:\n state=%s\ncode=%s\n params=%s\nproc=%s\nitem=%s\n delim=%s\n line=%d\n",
+ this, scriptstate_to_string(m_scriptstate),
+ code.safe_ansi_string(), formal_params.ansi_string(), procedure_name.ansi_string(),
+ item_name.safe_ansi_string(), delimiter.safe_ansi_string(),
+ info->ulStartingLineNumber);
+
+
+ code_frag *frag = compile_code_fragment(
+ FRAG_PROCEDURE,
+ NULL,
+ info->pstrCode,
+ info->ulStartingLineNumber,
+ NULL,
+ this
+ TSRMLS_CC);
+
+ if (frag) {
+
+ frag->persistent = (info->dwFlags & SCRIPTTEXT_ISPERSISTENT);
+ zend_hash_next_index_insert(&m_frags, &frag, sizeof(code_frag*), NULL);
+
+ ScriptProcedureDispatch *disp = new ScriptProcedureDispatch;
+
+ disp->AddRef();
+ disp->m_frag = frag;
+ disp->m_procflags = info->dwFlags;
+ disp->m_engine = this;
+ frag->ptr = disp;
+
+ *info->ppdisp = disp;
+ /*
+ ret = CoMarshalInterThreadInterfaceInStream(IID_IDispatch, disp, &info->disp);
+ disp->Release();
+ */
+ } else {
+ ret = DISP_E_EXCEPTION;
+ }
+
+ }
+ break;
+
+ case PHPSE_PARSE_SCRIPT:
+ {
+ struct php_active_script_parse_info *info = (struct php_active_script_parse_info*)lParam;
+ int doexec;
+
+ TWideString
+ code(info->pstrCode),
+ item_name(info->pstrItemName),
+ delimiter(info->pstrDelimiter);
+
+ trace("%08x: ParseScriptText:\n state=%s\ncode=%s\n item=%s\n delim=%s\n line=%d\n",
+ this, scriptstate_to_string(m_scriptstate),
+ code.safe_ansi_string(), item_name.safe_ansi_string(), delimiter.safe_ansi_string(),
+ info->ulStartingLineNumber);
+
+ code_frag *frag = compile_code_fragment(
+ FRAG_MAIN,
+ info->dwFlags & SCRIPTTEXT_ISEXPRESSION ? FRAG_CREATE_FUNC : NULL,
+ info->pstrCode,
+ info->ulStartingLineNumber,
+ info->pexcepinfo,
+ this
+ TSRMLS_CC);
+
+ doexec = (info->dwFlags & SCRIPTTEXT_ISEXPRESSION) ||
+ m_scriptstate == SCRIPTSTATE_STARTED ||
+ m_scriptstate == SCRIPTSTATE_CONNECTED ||
+ m_scriptstate == SCRIPTSTATE_DISCONNECTED;
+
+ if (frag) {
+ frag->persistent = (info->dwFlags & SCRIPTTEXT_ISPERSISTENT);
+ ret = S_OK;
+
+ if (info->dwFlags & SCRIPTTEXT_ISEXPRESSION) {
+ if (m_scriptstate == SCRIPTSTATE_INITIALIZED) {
+ /* not allowed to execute code in this state */
+ ret = E_UNEXPECTED;
+ doexec = 0;
+ }
+ }
+
+ if (doexec) {
+ /* execute the code as an expression */
+ if (!execute_code_fragment(frag, info->pvarResult, info->pexcepinfo TSRMLS_CC))
+ ret = DISP_E_EXCEPTION;
+ }
+
+ zend_hash_next_index_insert(&m_frags, &frag, sizeof(code_frag*), NULL);
+ } else {
+ ret = DISP_E_EXCEPTION;
+ }
+
+ if (info->pvarResult) {
+ VariantInit(info->pvarResult);
+ }
+
+
+ }
+ break;
+ default:
+ trace("unhandled message type %08x\n", msg);
+ if (handled)
+ *handled = 0;
+ }
+ return ret;
+}
+
+/* The PHP/Zend state actually lives in this thread */
+void TPHPScriptingEngine::engine_thread_func(void)
+{
+ TSRMLS_FETCH();
+ int handled;
+ int terminated = 0;
+ MSG msg;
+
+ trace("%08x: engine thread started up!\n", this);
+
+ CoInitializeEx(0, COINIT_MULTITHREADED);
+
+ m_tsrm_hack = tsrm_ls;
+
+ while(!terminated) {
+ DWORD result = MsgWaitForMultipleObjects(0, NULL, FALSE, 4000, QS_ALLINPUT);
+
+ switch(result) {
+ case WAIT_OBJECT_0:
+ while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
+
+ if (msg.message == WM_QUIT) {
+ terminated = 1;
+ } else {
+
+ handled = 1;
+ m_sync_thread_ret = engine_thread_handler(msg.message, msg.wParam, msg.lParam, &handled TSRMLS_CC);
+ if (handled)
+ SetEvent(m_sync_thread_msg);
+ }
+
+ }
+ break;
+ case WAIT_TIMEOUT:
+ trace("thread wait timed out\n");
+ break;
+ default:
+ trace("some strange value\n");
+ }
+ }
+
+#if 0
+ while(GetMessage(&msg, NULL, 0, 0)) {
+
+ if (msg.message == WM_QUIT)
+ break;
+
+ handled = 1;
+ m_sync_thread_ret = engine_thread_handler(msg.message, msg.wParam, msg.lParam, &handled TSRMLS_CC);
+ if (handled)
+ SetEvent(m_sync_thread_msg);
+ }
+ trace("%08x: engine thread exiting!!!!!\n", this);
+#endif
+ m_enginethread = 0;
+
+ CoUninitialize();
+}
+
+/* Only call this in the context of the engine thread, or you'll be sorry.
+ *
+ * When SCRIPTITEM_GLOBALMEMBERS is set, we're only adding COM objects to the namespace.
+ * We could add *all* properties, but I don't like this idea; what if the value changes
+ * while the page is running? We'd be left with stale data.
+ * */
+void TPHPScriptingEngine::add_to_global_namespace(IDispatch *disp, DWORD flags, char *name TSRMLS_DC)
+{
+ zval *val;
+ ITypeInfo *typ;
+ int i;
+ unsigned int namelen;
+ FUNCDESC *func;
+ BSTR funcname;
+ TYPEATTR *attr;
+ DISPPARAMS dispparams;
+ VARIANT vres;
+ ITypeInfo *rettyp;
+ TYPEATTR *retattr;
+
+trace("Add %s to global namespace\n", name);
+
+ val = php_COM_object_from_dispatch(disp, NULL TSRMLS_CC);
+
+ if (val == NULL) {
+ disp->Release();
+ return;
+ }
+
+ ZEND_SET_SYMBOL(&EG(symbol_table), name, val);
+
+ if (flags & SCRIPTITEM_GLOBALMEMBERS == 0) {
+ disp->Release();
+ return;
+ }
+
+ /* Enumerate properties and add those too */
+ if (FAILED(disp->GetTypeInfo(0, 0, &typ))) {
+ disp->Release();
+ return;
+ }
+
+ if (SUCCEEDED(typ->GetTypeAttr(&attr))) {
+ for (i = 0; i < attr->cFuncs; i++) {
+ if (FAILED(typ->GetFuncDesc(i, &func)))
+ continue;
+
+ /* Look at it's type */
+ if (func->invkind == INVOKE_PROPERTYGET
+ && VT_PTR == func->elemdescFunc.tdesc.vt
+ && VT_USERDEFINED == func->elemdescFunc.tdesc.lptdesc->vt
+ && SUCCEEDED(typ->GetRefTypeInfo(func->elemdescFunc.tdesc.lptdesc->hreftype, &rettyp)))
+ {
+ if (SUCCEEDED(rettyp->GetTypeAttr(&retattr))) {
+ if (retattr->typekind == TKIND_DISPATCH) {
+ /* It's dispatchable */
+
+ /* get the value */
+ dispparams.cArgs = 0;
+ dispparams.cNamedArgs = 0;
+ VariantInit(&vres);
+
+ if (SUCCEEDED(disp->Invoke(func->memid, IID_NULL, 0, func->invkind,
+ &dispparams, &vres, NULL, NULL))) {
+
+ /* Get it's dispatch */
+ IDispatch *sub = NULL;
+
+ if (V_VT(&vres) == VT_UNKNOWN)
+ V_UNKNOWN(&vres)->QueryInterface(IID_IDispatch, (void**)&sub);
+ else if (V_VT(&vres) == VT_DISPATCH)
+ sub = V_DISPATCH(&vres);
+
+ if (sub) {
+ /* find out it's name */
+ typ->GetDocumentation(func->memid, &funcname, NULL, NULL, NULL);
+ name = php_OLECHAR_to_char(funcname, &namelen, CP_ACP TSRMLS_CC);
+
+ /* add to namespace */
+ zval *subval = php_COM_object_from_dispatch(sub, NULL TSRMLS_CC);
+ if (subval) {
+ ZEND_SET_SYMBOL(&EG(symbol_table), name, subval);
+ }
+
+ efree(name);
+ SysFreeString(funcname);
+ }
+ VariantClear(&vres);
+ }
+ }
+ rettyp->ReleaseTypeAttr(retattr);
+ }
+ rettyp->Release();
+ }
+ typ->ReleaseFuncDesc(func);
+ }
+ typ->ReleaseTypeAttr(attr);
+ }
+ disp->Release();
+}
+
+STDMETHODIMP_(DWORD) TPHPScriptingEngine::AddRef(void)
+{
+ return InterlockedIncrement(&m_refcount);
+}
+
+STDMETHODIMP_(DWORD) TPHPScriptingEngine::Release(void)
+{
+ DWORD ret = InterlockedDecrement(&m_refcount);
+ if (ret == 0) {
+ trace("%08x: Release: zero refcount, destroy the engine!\n", this);
+ delete this;
+ }
+ return ret;
+}
+
+STDMETHODIMP TPHPScriptingEngine::QueryInterface(REFIID iid, void **ppvObject)
+{
+ *ppvObject = NULL;
+
+ if (IsEqualGUID(IID_IActiveScript, iid)) {
+ *ppvObject = (IActiveScript*)this;
+ } else if (IsEqualGUID(IID_IActiveScriptParse32, iid)) {
+ *ppvObject = (IActiveScriptParse32*)this;
+ } else if (IsEqualGUID(IID_IActiveScriptParseProcedure32, iid)) {
+ *ppvObject = (IActiveScriptParseProcedure*)this;
+ } else if (IsEqualGUID(IID_IUnknown, iid)) {
+ *ppvObject = this;
+ } else {
+ LPOLESTR guidw;
+ StringFromCLSID(iid, &guidw);
+ {
+ TWideString guid(guidw);
+ trace("%08x: QueryInterface for unsupported %s\n", this, guid.ansi_string());
+ }
+ CoTaskMemFree(guidw);
+ }
+ if (*ppvObject) {
+ AddRef();
+ return S_OK;
+ }
+
+ return E_NOINTERFACE;
+}
+
+/* This is called by the host to set the scrite site.
+ * It also defines the base thread. */
+STDMETHODIMP TPHPScriptingEngine::SetScriptSite(IActiveScriptSite *pass)
+{
+ TSRMLS_FETCH();
+
+ tsrm_mutex_lock(m_mutex);
+
+ trace("%08x: -----> Base thread is %08x\n", this, tsrm_thread_id());
+
+ if (m_pass) {
+ m_pass->Release();
+ m_pass = NULL;
+ SEND_THREAD_MESSAGE(this, PHPSE_SET_SITE, 0, 0 TSRMLS_CC);
+ }
+
+ if (pass == NULL) {
+ trace("Closing down site; we should have no references to objects from the host\n"
+ " m_pass=%08x\n m_pass_eng=%08x\n What about named items??\n",
+ m_pass, m_pass_eng);
+ }
+
+ m_pass = pass;
+ if (m_pass) {
+ m_pass->AddRef();
+
+ LPSTREAM stream;
+ if (SUCCEEDED(CoMarshalInterThreadInterfaceInStream(IID_IActiveScriptSite, m_pass, &stream)))
+ SEND_THREAD_MESSAGE(this, PHPSE_SET_SITE, 0, (LPARAM)stream TSRMLS_CC);
+
+ if (m_scriptstate == SCRIPTSTATE_UNINITIALIZED)
+ SEND_THREAD_MESSAGE(this, PHPSE_STATE_CHANGE, 0, SCRIPTSTATE_INITIALIZED TSRMLS_CC);
+ }
+
+ tsrm_mutex_unlock(m_mutex);
+ return S_OK;
+}
+
+STDMETHODIMP TPHPScriptingEngine::GetScriptSite(REFIID riid, void **ppvObject)
+{
+ HRESULT ret = S_FALSE;
+
+ trace("%08x: GetScriptSite()\n", this);
+ tsrm_mutex_lock(m_mutex);
+
+
+ if (m_pass)
+ ret = m_pass->QueryInterface(riid, ppvObject);
+
+ tsrm_mutex_unlock(m_mutex);
+ return ret;
+}
+
+STDMETHODIMP TPHPScriptingEngine::SetScriptState(SCRIPTSTATE ss)
+{
+ TSRMLS_FETCH();
+ trace("%08x: SetScriptState(%s)\n", this, scriptstate_to_string(ss));
+ return SEND_THREAD_MESSAGE(this, PHPSE_STATE_CHANGE, 0, ss TSRMLS_CC);
+}
+
+STDMETHODIMP TPHPScriptingEngine::GetScriptState(SCRIPTSTATE *pssState)
+{
+ trace("%08x: GetScriptState(current=%s)\n", this, scriptstate_to_string(m_scriptstate));
+ tsrm_mutex_lock(m_mutex);
+ *pssState = m_scriptstate;
+ tsrm_mutex_unlock(m_mutex);
+ return S_OK;
+}
+
+STDMETHODIMP TPHPScriptingEngine::Close(void)
+{
+ TSRMLS_FETCH();
+
+ if (m_pass) {
+ m_pass->Release();
+ m_pass = NULL;
+ }
+ SEND_THREAD_MESSAGE(this, PHPSE_CLOSE, 0, 0 TSRMLS_CC);
+ return S_OK;
+}
+
+/* Add an item to global namespace.
+ * This is called in the context of the base thread (or perhaps some other thread).
+ * We want to be able to work with the object in the engine thread, so we marshal
+ * it into a stream and let the engine thread deal with it.
+ * This works quite nicely when PHP scripts call into the object; threading is
+ * handled correctly. */
+STDMETHODIMP TPHPScriptingEngine::AddNamedItem(LPCOLESTR pstrName, DWORD dwFlags)
+{
+ struct php_active_script_add_named_item_info info;
+ TSRMLS_FETCH();
+
+ info.pstrName = pstrName;
+ info.dwFlags = dwFlags;
+
+ m_pass->GetItemInfo(pstrName, SCRIPTINFO_IUNKNOWN, &info.punk, NULL);
+ if (SUCCEEDED(CoMarshalInterThreadInterfaceInStream(IID_IDispatch, info.punk, &info.marshal))) {
+ SEND_THREAD_MESSAGE(this, PHPSE_ADD_NAMED_ITEM, 0, (LPARAM)&info TSRMLS_CC);
+ }
+ info.punk->Release();
+
+ return S_OK;
+}
+
+/* Bind to a type library */
+STDMETHODIMP TPHPScriptingEngine::AddTypeLib(
+ /* [in] */ REFGUID rguidTypeLib,
+ /* [in] */ DWORD dwMajor,
+ /* [in] */ DWORD dwMinor,
+ /* [in] */ DWORD dwFlags)
+{
+ struct php_active_script_add_tlb_info info;
+ TSRMLS_FETCH();
+
+ info.rguidTypeLib = &rguidTypeLib;
+ info.dwMajor = dwMajor;
+ info.dwMinor = dwMinor;
+ info.dwFlags = dwFlags;
+
+ SEND_THREAD_MESSAGE(this, PHPSE_ADD_TYPELIB, 0, (LPARAM)&info TSRMLS_CC);
+
+ return S_OK;
+}
+
+/* Returns an object representing the PHP Scripting Engine.
+ * Optionally, a client can request a particular item directly.
+ * For the moment, we only do the bare minimum amount of work
+ * for the engine to work correctly; we can flesh out this part
+ * a little later. */
+STDMETHODIMP TPHPScriptingEngine::GetScriptDispatch(
+ /* [in] */ LPCOLESTR pstrItemName,
+ /* [out] */ IDispatch **ppdisp)
+{
+ TSRMLS_FETCH();
+ *ppdisp = NULL;
+ struct php_active_script_get_dispatch_info info;
+
+ info.pstrItemName = pstrItemName;
+ info.dispatch = NULL;
+
+ /* This hack is required because the host is likely to query us
+ * for a dispatch if we use any of it's objects from PHP script.
+ * Since the engine thread will be waiting for the return from
+ * a COM call, we need to deliberately poke a hole in thread
+ * safety so that it is possible to read the symbol table from
+ * outside the engine thread and give it a valid return value.
+ * This is "safe" only in this instance, since we are not modifying
+ * the engine state by looking up the dispatch (I hope).
+ * The scripting engine rules pretty much guarantee that this
+ * method is only called in the base thread. */
+
+ if (tsrm_thread_id() != m_enginethread) {
+ tsrm_ls = m_tsrm_hack;
+ trace("HEY: hacking thread safety!\n");
+ }
+
+ if (S_OK == engine_thread_handler(PHPSE_GET_DISPATCH, 0, (LPARAM)&info, NULL TSRMLS_CC)) {
+ CoGetInterfaceAndReleaseStream(info.dispatch, IID_IDispatch, (void**)ppdisp);
+ }
+
+ if (*ppdisp) {
+ return S_OK;
+ }
+ return S_FALSE;
+}
+
+STDMETHODIMP TPHPScriptingEngine::GetCurrentScriptThreadID(
+ /* [out] */ SCRIPTTHREADID *pstidThread)
+{
+// tsrm_mutex_lock(m_mutex);
+ trace("%08x: GetCurrentScriptThreadID()\n", this);
+ *pstidThread = m_enginethread;
+// tsrm_mutex_unlock(m_mutex);
+ return S_OK;
+}
+
+STDMETHODIMP TPHPScriptingEngine::GetScriptThreadID(
+ /* [in] */ DWORD dwWin32ThreadId,
+ /* [out] */ SCRIPTTHREADID *pstidThread)
+{
+// tsrm_mutex_lock(m_mutex);
+ trace("%08x: GetScriptThreadID()\n", this);
+ *pstidThread = dwWin32ThreadId;
+// tsrm_mutex_unlock(m_mutex);
+ return S_OK;
+}
+
+STDMETHODIMP TPHPScriptingEngine::GetScriptThreadState(
+ /* [in] */ SCRIPTTHREADID stidThread,
+ /* [out] */ SCRIPTTHREADSTATE *pstsState)
+{
+// tsrm_mutex_lock(m_mutex);
+
+ trace("%08x: GetScriptThreadState()\n", this);
+ *pstsState = SCRIPTTHREADSTATE_NOTINSCRIPT;
+
+ switch(stidThread) {
+ case SCRIPTTHREADID_BASE:
+ stidThread = m_basethread;
+ break;
+ case SCRIPTTHREADID_CURRENT:
+ stidThread = m_enginethread;
+ break;
+ };
+ if (stidThread == m_basethread) {
+ *pstsState = SCRIPTTHREADSTATE_NOTINSCRIPT;
+ } else if (stidThread == m_enginethread) {
+ *pstsState = SCRIPTTHREADSTATE_NOTINSCRIPT;
+ }
+// tsrm_mutex_unlock(m_mutex);
+ return S_OK;
+}
+
+STDMETHODIMP TPHPScriptingEngine::InterruptScriptThread(
+ /* [in] */ SCRIPTTHREADID stidThread,
+ /* [in] */ const EXCEPINFO *pexcepinfo,
+ /* [in] */ DWORD dwFlags)
+{
+ /* do not serialize this method, or call into the script site */
+ trace("%08x: InterruptScriptThread()\n", this);
+ return S_OK;
+}
+
+/* Clone is essential when running under Windows Script Host.
+ * It creates an engine to parse the code. Once it is parsed,
+ * the host clones other engines from the original and runs those.
+ * It is intended to be a fast method of running the same script
+ * multiple times in multiple threads. */
+STDMETHODIMP TPHPScriptingEngine::Clone(
+ /* [out] */ IActiveScript **ppscript)
+{
+ TPHPScriptingEngine *cloned = new TPHPScriptingEngine;
+ TSRMLS_FETCH();
+
+ trace("%08x: Clone()\n", this);
+
+ if (ppscript)
+ *ppscript = NULL;
+
+ if (cloned) {
+ cloned->InitNew();
+ SEND_THREAD_MESSAGE(cloned, PHPSE_CLONE, 0, (LPARAM)this TSRMLS_CC);
+ trace("%08x: Cloned OK, returning cloned object ptr %08x\n", this, cloned);
+ *ppscript = (IActiveScript*)cloned;
+ return S_OK;
+
+ }
+
+ return E_FAIL;
+}
+
+
+STDMETHODIMP TPHPScriptingEngine::InitNew( void)
+{
+ TSRMLS_FETCH();
+ SEND_THREAD_MESSAGE(this, PHPSE_INIT_NEW, 0, 0 TSRMLS_CC);
+ return S_OK;
+}
+
+STDMETHODIMP TPHPScriptingEngine::AddScriptlet(
+ /* [in] */ LPCOLESTR pstrDefaultName,
+ /* [in] */ LPCOLESTR pstrCode,
+ /* [in] */ LPCOLESTR pstrItemName,
+ /* [in] */ LPCOLESTR pstrSubItemName,
+ /* [in] */ LPCOLESTR pstrEventName,
+ /* [in] */ LPCOLESTR pstrDelimiter,
+ /* [in] */ DWORD dwSourceContextCookie,
+ /* [in] */ ULONG ulStartingLineNumber,
+ /* [in] */ DWORD dwFlags,
+ /* [out] */ BSTR *pbstrName,
+ /* [out] */ EXCEPINFO *pexcepinfo)
+{
+ struct php_active_script_add_scriptlet_info info;
+ TSRMLS_FETCH();
+
+ info.pstrDefaultName = pstrDefaultName;
+ info.pstrCode = pstrCode;
+ info.pstrItemName = pstrItemName;
+ info.pstrSubItemName = pstrSubItemName;
+ info.pstrEventName = pstrEventName;
+ info.pstrDelimiter = pstrDelimiter;
+ info.dwSourceContextCookie = dwSourceContextCookie;
+ info.ulStartingLineNumber = ulStartingLineNumber;
+ info.dwFlags = dwFlags;
+ info.pbstrName = pbstrName;
+ info.pexcepinfo = pexcepinfo;
+
+ return SEND_THREAD_MESSAGE(this, PHPSE_ADD_SCRIPTLET, 0, (LPARAM)&info TSRMLS_CC);
+}
+
+STDMETHODIMP TPHPScriptingEngine::ParseScriptText(
+ /* [in] */ LPCOLESTR pstrCode,
+ /* [in] */ LPCOLESTR pstrItemName,
+ /* [in] */ IUnknown *punkContext,
+ /* [in] */ LPCOLESTR pstrDelimiter,
+ /* [in] */ DWORD dwSourceContextCookie,
+ /* [in] */ ULONG ulStartingLineNumber,
+ /* [in] */ DWORD dwFlags,
+ /* [out] */ VARIANT *pvarResult,
+ /* [out] */ EXCEPINFO *pexcepinfo)
+{
+ struct php_active_script_parse_info info;
+ TSRMLS_FETCH();
+
+ info.pstrCode = pstrCode;
+ info.pstrItemName = pstrItemName;
+ info.punkContext = punkContext;
+ info.pstrDelimiter = pstrDelimiter;
+ info.dwSourceContextCookie = dwSourceContextCookie;
+ info.ulStartingLineNumber = ulStartingLineNumber;
+ info.dwFlags = dwFlags;
+ info.pvarResult = pvarResult;
+ info.pexcepinfo = pexcepinfo;
+
+ return SEND_THREAD_MESSAGE(this, PHPSE_PARSE_SCRIPT, 0, (LPARAM)&info TSRMLS_CC);
+}
+
+STDMETHODIMP TPHPScriptingEngine::ParseProcedureText(
+ /* [in] */ LPCOLESTR pstrCode,
+ /* [in] */ LPCOLESTR pstrFormalParams,
+ /* [in] */ LPCOLESTR pstrProcedureName,
+ /* [in] */ LPCOLESTR pstrItemName,
+ /* [in] */ IUnknown *punkContext,
+ /* [in] */ LPCOLESTR pstrDelimiter,
+ /* [in] */ DWORD dwSourceContextCookie,
+ /* [in] */ ULONG ulStartingLineNumber,
+ /* [in] */ DWORD dwFlags,
+ /* [out] */ IDispatch **ppdisp)
+{
+ struct php_active_script_parse_proc_info info;
+ HRESULT ret;
+ TSRMLS_FETCH();
+
+ info.pstrCode = pstrCode;
+ info.pstrFormalParams = pstrFormalParams;
+ info.pstrProcedureName = pstrProcedureName;
+ info.pstrItemName = pstrItemName;
+ info.punkContext = punkContext;
+ info.pstrDelimiter = pstrDelimiter;
+ info.dwSourceContextCookie = dwSourceContextCookie;
+ info.ulStartingLineNumber = ulStartingLineNumber;
+ info.dwFlags = dwFlags;
+ info.ppdisp = ppdisp;
+
+ ret = SEND_THREAD_MESSAGE(this, PHPSE_PARSE_PROC, 0, (LPARAM)&info TSRMLS_CC);
+
+ /*
+ if (ret == S_OK) {
+ ret = CoGetInterfaceAndReleaseStream(info.disp, IID_IDispatch, (void**)ppdisp);
+
+ }
+ */
+ trace("ParseProc: ret=%08x disp=%08x\n", ret, *ppdisp);
+ return ret;
+}
+
+extern "C"
+void activescript_error_func(int type, const char *error_msg, ...)
+{
+ TSRMLS_FETCH();
+
+ TPHPScriptingEngine *engine = (TPHPScriptingEngine*)SG(server_context);
+
+
+}
+
+class TActiveScriptError:
+ public IActiveScriptError
+{
+protected:
+ volatile LONG m_refcount;
+public:
+ /* IUnknown */
+ STDMETHODIMP QueryInterface(REFIID iid, void **ppvObject) {
+ *ppvObject = NULL;
+
+ if (IsEqualGUID(IID_IActiveScriptError, iid)) {
+ *ppvObject = (IActiveScriptError*)this;
+ } else if (IsEqualGUID(IID_IUnknown, iid)) {
+ *ppvObject = this;
+ }
+ if (*ppvObject) {
+ AddRef();
+ return S_OK;
+ }
+ return E_NOINTERFACE;
+ }
+
+ STDMETHODIMP_(DWORD) AddRef(void) {
+ return InterlockedIncrement(&m_refcount);
+ }
+
+ STDMETHODIMP_(DWORD) Release(void) {
+ DWORD ret = InterlockedDecrement(&m_refcount);
+ trace("Release: errobj refcount=%d\n", ret);
+ if (ret == 0)
+ delete this;
+ return ret;
+ }
+
+ HRESULT STDMETHODCALLTYPE GetExceptionInfo(
+ /* [out] */ EXCEPINFO *pexcepinfo)
+ {
+ memset(pexcepinfo, 0, sizeof(EXCEPINFO));
+ pexcepinfo->bstrDescription = SysAllocString(m_message);
+ pexcepinfo->bstrSource = SysAllocString(m_filename);
+ pexcepinfo->wCode = 1000;
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE GetSourcePosition(
+ /* [out] */ DWORD *pdwSourceContext,
+ /* [out] */ ULONG *pulLineNumber,
+ /* [out] */ LONG *plCharacterPosition)
+ {
+ *pdwSourceContext = 0;
+ *pulLineNumber = m_lineno;
+ *plCharacterPosition = 0;
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE GetSourceLineText(
+ /* [out] */ BSTR *pbstrSourceLine)
+ {
+ *pbstrSourceLine = NULL;
+ return E_FAIL;
+ }
+
+ BSTR m_filename, m_message;
+ UINT m_lineno;
+
+ TActiveScriptError(const char *filename, const uint lineno, const char *message)
+ {
+ m_refcount = 0; /* start with zero refcount because this object is passed
+ * directly to the script site; it will call addref */
+ m_filename = TWideString::bstr_from_ansi((char*)filename);
+ m_message = TWideString::bstr_from_ansi((char*)message);
+ m_lineno = lineno;
+ }
+
+ ~TActiveScriptError()
+ {
+ trace("%08x: cleaning up error object\n", this);
+ SysFreeString(m_filename);
+ SysFreeString(m_message);
+ }
+};
+
+extern "C"
+void activescript_error_handler(int type, const char *error_filename,
+ const uint error_lineno, const char *format, va_list args)
+{
+ TSRMLS_FETCH();
+ char *buf;
+ int buflen;
+ TPHPScriptingEngine *engine = (TPHPScriptingEngine*)SG(server_context);
+
+ buflen = vspprintf(&buf, PG(log_errors_max_len), format, args);
+ trace("%08x: Error: %s\n", engine, buf);
+
+ /* if it's a fatal error, report it using IActiveScriptError. */
+
+ switch(type) {
+ case E_ERROR:
+ case E_CORE_ERROR:
+ case E_COMPILE_ERROR:
+ case E_USER_ERROR:
+ case E_PARSE:
+ /* trigger an error in the host */
+ TActiveScriptError *eobj = new TActiveScriptError(error_filename, error_lineno, buf);
+ trace("raising error object!\n");
+ if (engine->m_pass_eng)
+ engine->m_pass_eng->OnScriptError(eobj);
+
+ /* now throw the exception to abort execution */
+ if (engine->m_err_trap)
+ longjmp(*engine->m_err_trap, 1);
+
+ break;
+ }
+ efree(buf);
+}
+