[svn:PHP-Sandwich] rev 980 - in PHP-Sandwich/trunk: . PHP stuff t

[email protected] 14 Apr 2005 03:31:34 -0000
Newsgroups perl.php.sandwich.dev
Message-ID <[email protected]>
Author: gschlossnagle
Date: Wed Apr 13 20:31:34 2005
New Revision: 980

Added:
   PHP-Sandwich/trunk/Makefile.PL
   PHP-Sandwich/trunk/PHP/
   PHP-Sandwich/trunk/PHP.pm
   PHP-Sandwich/trunk/PHP.xs
   PHP-Sandwich/trunk/PHP/Interpreter.pm
   PHP-Sandwich/trunk/phpinterp.c
   PHP-Sandwich/trunk/phpinterp.h
   PHP-Sandwich/trunk/stuff/
   PHP-Sandwich/trunk/stuff/TSRM.patch
   PHP-Sandwich/trunk/t/
   PHP-Sandwich/trunk/t/1.pl   (contents, props changed)
   PHP-Sandwich/trunk/t/2.pl   (contents, props changed)
   PHP-Sandwich/trunk/typemap
Modified:
   PHP-Sandwich/trunk/README
Log:
initial commit

Added: PHP-Sandwich/trunk/Makefile.PL
==============================================================================
--- (empty file)
+++ PHP-Sandwich/trunk/Makefile.PL	Wed Apr 13 20:31:34 2005
@@ -0,0 +1,26 @@
+use ExtUtils::MakeMaker 5.16 ;
+use Config ;
+use POSIX;
+
+#push @libs, `/usr/local/bin/php-config --libs`;
+push @libs, '-lphp5';
+
+use Data::Dumper;
+print Dumper(@libs);
+
+@lddlflags  = ($Config{lddlflags});
+push @lddlflags, `/usr/local/bin/php-config --ldflags`;
+
+@ofiles = ('PHP.o', 'phpinterp.o');
+
+WriteMakefile(
+        CC              => $ENV{CC},
+	CCFLAGS		=> '-g',
+	OBJECT		=> join(' ',@ofiles),
+        NAME            => 'PHP',
+        VERSION_FROM    => 'PHP.pm',
+        PMLIBDIRS       => ['PHP'],
+        LIBS            =>  join(' ',@libs),
+	LDDLFLAGS	=> join(' ',@lddlflags),
+        INC             => "-I/usr/local/include ".`/usr/local/bin/php-config --includes`
+        );

Added: PHP-Sandwich/trunk/PHP.pm
==============================================================================
--- (empty file)
+++ PHP-Sandwich/trunk/PHP.pm	Wed Apr 13 20:31:34 2005
@@ -0,0 +1,15 @@
+package PHP;
+
+require Exporter;
+require DynaLoader;
+
+use strict;
+use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $AUTOLOAD);
+$VERSION = "0.9";
+
+@ISA = qw(Exporter DynaLoader);
+
+bootstrap PHP;
+
+1;
+__END__

Added: PHP-Sandwich/trunk/PHP.xs
==============================================================================
--- (empty file)
+++ PHP-Sandwich/trunk/PHP.xs	Wed Apr 13 20:31:34 2005
@@ -0,0 +1,273 @@
+/* vim: set sts=2 ts=2 expandtab bs=2 ai : */
+#include "EXTERN.h"
+#include "perl.h"
+#include "XSUB.h"
+
+#ifndef PERL_VERSION
+#include "patchlevel.h"
+#define PERL_REVISION   5
+#define PERL_VERSION    PATCHLEVEL
+#define PERL_SUBVERSION SUBVERSION
+#endif
+
+#include "phpinterp.h"
+
+#if PERL_REVISION == 5 && (PERL_VERSION < 4 || (PERL_VERSION == 4 && PERL_SUBVERSION <= 75 ))
+
+#    define PL_sv_undef         sv_undef
+#    define PL_na               na
+#    define PL_curcop           curcop
+#    define PL_compiling        compiling
+
+#endif
+
+#define PHP_Interpreter sandwich_per_interp *
+#define PHP_Var_Scalar zval *
+
+zval *SvZval(SV *sv)
+{
+  zval *retval;
+  MAKE_STD_ZVAL(retval);
+  switch(SvTYPE(sv)) {
+    case SVt_IV:   /* int */
+      ZVAL_LONG(retval, SvIV(sv));
+      break;
+    case SVt_NV:   /* double */
+      ZVAL_DOUBLE(retval, SvNV(sv));
+      break;
+    case SVt_PV:    /* string */
+      {
+        STRLEN strlen;
+        char *str = SvPV(sv, strlen);
+        ZVAL_STRINGL(retval, str, strlen, 1);
+        fprintf(stderr, "SvPV %s\n", str);
+      }
+      break;
+    case SVt_RV:    /* reference */
+      break;
+    case SVt_PVAV: /* indexed array */
+      break;
+    case SVt_PVHV: /* assoc. array */
+      break;
+    case SVt_PVCV: /* code */
+      break;
+    case SVt_PVGV: /* glob */
+      break;
+    case SVt_PVMG: /* magic */
+      break;
+    default:
+      break;
+  }
+  return retval;
+}
+
+void assign_to_zval(zval *zptr, SV *sv)
+{
+  zval_dtor(zptr);
+  switch(SvTYPE(sv)) {
+    case SVt_IV:   /* int */
+      ZVAL_LONG(zptr, SvIV(sv));
+      break;
+    case SVt_NV:   /* double */
+      ZVAL_DOUBLE(zptr, SvNV(sv));
+      break;
+    case SVt_PV:    /* string */
+      {
+        STRLEN strlen;
+        char *str = SvPV(sv, strlen);
+        ZVAL_STRINGL(zptr, str, strlen, 1);
+        fprintf(stderr, "SvPV %s\n", str);
+      }
+      break;
+    case SVt_RV:    /* reference */
+      break;
+    case SVt_PVAV: /* indexed array */
+      break;
+    case SVt_PVHV: /* assoc. array */
+      break;
+    case SVt_PVCV: /* code */
+      break;
+    case SVt_PVGV: /* glob */
+      break;
+    case SVt_PVMG: /* magic */
+      break;
+    default:
+      break;
+  }
+}
+
+SV *newSVzval(zval *zptr)
+{
+  switch( zptr->type) {
+    case IS_NULL:
+      fprintf(stderr, "IS_NULL\n");
+      return &PL_sv_undef;
+      break;
+    case IS_LONG:
+      fprintf(stderr, "IS_LONG\n");
+      return newSViv(Z_LVAL_P(zptr));
+      break;
+    case IS_DOUBLE:
+      fprintf(stderr, "IS_ODUBLE\n");
+      return newSVnv(Z_DVAL_P(zptr));
+      break;
+    case IS_BOOL:
+      fprintf(stderr, "IS_BOOL\n");
+      return Z_BVAL_P(zptr) ? &PL_sv_yes : &PL_sv_no;
+      break;
+    case IS_ARRAY:
+      /* FIXME: return by copy, not by reference */
+      fprintf(stderr, "IS_ARRAY\n");
+      return &PL_sv_undef;
+      break;
+    case IS_OBJECT:
+      /* FIXME: return by reference as a PHP::DataType::Object, not by copy */
+      fprintf(stderr, "IS_OBJECT\n");
+      return &PL_sv_undef;
+      break;
+    case IS_STRING:
+      fprintf(stderr, "IS_STRING\n");
+      return newSVpv(Z_STRVAL_P(zptr), Z_STRLEN_P(zptr));
+      break;
+    case IS_RESOURCE:
+      /* FIXME: return by reference as a PHP::DataType::Reference, not by copy */
+      fprintf(stderr, "IS_RESOURCE\n");
+      return &PL_sv_undef;
+      break;
+    case IS_CONSTANT:
+      fprintf(stderr, "IS_CONSTANT\n");
+      return newSVpv(Z_STRVAL_P(zptr), Z_STRLEN_P(zptr));
+      break;
+    case IS_CONSTANT_ARRAY:
+      fprintf(stderr, "IS_CONSTANT_ARRAY\n");
+      /* FIXME: return by copy, not by reference */
+      return &PL_sv_undef;
+      break;
+    default:
+      fprintf(stderr, "Unknown type in newSVzval()\n");
+      return &PL_sv_undef;
+      break;
+  }
+}
+
+MODULE = PHP PACKAGE = PHP::Interpreter PREFIX=SAND_
+
+REQUIRE:        1.9505
+PROTOTYPES:     DISABLE
+
+SV* SAND_new(classname)
+  char *classname;
+  CODE:
+    {
+      PHP_Interpreter interp;
+      if(sandwich_php_interpreter_create() == -1) {
+        fprintf(stderr, "Error creating Zend Runtime\n");
+        RETVAL = &PL_sv_undef;
+        return;
+      }
+      interp = sandwich_per_interp_setup();
+      RETVAL = newSV(0);
+      sv_setref_pv(RETVAL, classname, (void *)interp);
+    }
+  OUTPUT:
+    RETVAL
+
+void SAND_eval(interp, code)
+  PHP_Interpreter interp;
+  char *code;
+  CODE:
+    {
+      sandwich_eval(interp, code);
+    }
+
+void SAND_include(interp, file)
+  PHP_Interpreter interp;
+  char *file;
+  CODE:
+    {
+      sandwich_include(interp, file);
+    }
+
+SV *SAND_call(interp, method_name, ...)
+  PHP_Interpreter interp;
+  char *method_name;
+  CODE:
+    {
+      zval method;
+      zval **params = NULL;
+      int i;
+      int param_count = 0;
+      zval *retval;
+      INIT_ZVAL(method);
+      ZVAL_STRING(&method, method_name, 1);
+      if(items > 2) {
+        params = emalloc(sizeof(zval *) * (items - 2));
+        for(i = 2; i< items; i++) {
+          params[i - 2] = SvZval(ST(i));
+        }
+        param_count = items - 2;
+      }
+      retval = sandwich_call_function(interp, &method, params, param_count);
+      if(retval == NULL) {
+        fprintf(stderr, "an error occured calling %s\n", method_name);
+        RETVAL = &PL_sv_undef;
+      } else {
+        SV * c1 = sv_newmortal();
+        switch(retval->type) {
+          case IS_LONG:
+          case IS_BOOL:
+          case IS_DOUBLE:
+          case IS_STRING:
+            RETVAL = NEWSV(0,0);
+            sv_setref_pv(c1, "PHP::Var::Scalar", (void *) retval);
+            sv_magic(RETVAL, c1, PERL_MAGIC_tiedscalar, NULL, 0);
+            break;
+          default:
+            break;
+        }
+        fprintf(stderr, "worked!\n");
+      }
+      zval_dtor(&method);
+    }
+  OUTPUT:
+    RETVAL
+  
+void SAND_DESTROY(interp)
+  PHP_Interpreter interp;
+  CODE:
+    {
+      sandwich_per_interp_shutdown(interp);
+    }
+
+MODULE = PHP PACKAGE = PHP::Var::Scalar PREFIX=PHP_V_S_
+
+REQUIRE:        1.9505
+PROTOTYPES:     DISABLE
+
+SV* PHP_V_S_FETCH(zptr)
+  PHP_Var_Scalar zptr;
+  CODE:
+    {
+    fprintf(stderr, "PHP::Var::Scalar::FETCH\n");
+      RETVAL = newSVzval(zptr);
+    }
+  OUTPUT:
+    RETVAL
+  
+void PHP_V_S_STORE(zptr, new)
+  PHP_Var_Scalar zptr;
+  SV *new;
+  CODE:
+    {
+    fprintf(stderr, "PHP::Var::Scalar::STORE\n");
+      assign_to_zval(zptr, new);
+    }
+
+void PHP_V_S_DESTROY(zptr)
+  PHP_Var_Scalar zptr;
+  CODE:
+    {
+    fprintf(stderr, "PHP::Var::Scalar::DESTROY\n");
+      zval_dtor(zptr);
+    }
+

Added: PHP-Sandwich/trunk/PHP/Interpreter.pm
==============================================================================
--- (empty file)
+++ PHP-Sandwich/trunk/PHP/Interpreter.pm	Wed Apr 13 20:31:34 2005
@@ -0,0 +1,19 @@
+package PHP::Interpreter;
+
+use AutoLoader;
+
+use strict;
+use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $AUTOLOAD);
+
+@ISA = qw(AutoLoader);
+
+sub AUTOLOAD {
+  my $self = shift;
+  my $sub = $AUTOLOAD;
+  $sub =~ s/.*:://;
+  return $self->call($sub, @_);
+}
+
+1;
+
+__END__

Modified: PHP-Sandwich/trunk/README
==============================================================================
--- PHP-Sandwich/trunk/README	(original)
+++ PHP-Sandwich/trunk/README	Wed Apr 13 20:31:34 2005
@@ -6,4 +6,11 @@
 variables loaded into Perl. So PHP executed from Perl can use any Perl
 modules.
 
+BUILD INSTRUCTIONS
+==================
+
+1) Apply stuff/TSRM.patch to a PHP 5.x source tree
+2) Build/install PHP with --enable-maintainer-zts and --enable-embed 
+3) Build/Install PHP sources HERE.  See non-tests in t/ for examples.
+
 

Added: PHP-Sandwich/trunk/phpinterp.c
==============================================================================
--- (empty file)
+++ PHP-Sandwich/trunk/phpinterp.c	Wed Apr 13 20:31:34 2005
@@ -0,0 +1,221 @@
+#include "phpinterp.h"
+
+static pthread_key_t sandwich_per_thread_info_key;
+
+/* {{{ sandwich SAPI Details */
+
+static int sandwich_sapi_ub_write(const char *str, uint str_length TSRMLS_DC)
+{
+  // FIXME - call out to Perl's selected fh
+  fwrite(str, 1, str_length, stdout);
+  return str_length;
+}
+
+static void sandwich_sapi_reg_server_vars(zval *track_vars_array TSRMLS_DC)
+{
+  // FIXME - register $php here?
+}
+
+static char *sandwich_sapi_read_cookies(TSRMLS_D)
+{
+  return NULL;
+}
+
+static int sandwich_sapi_header_handler(sapi_header_struct *sapi_header,
+  sapi_headers_struct *sapi_headers TSRMLS_DC)
+{
+  // FIXME - call out to perl
+  return SAPI_HEADER_ADD;
+}
+
+static int sandwich_sapi_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC)
+{
+  // FIXME set headers back through perl
+  return SAPI_HEADER_SENT_SUCCESSFULLY;
+}
+
+static void sandwich_sapi_log_message(char *message)
+{
+  // allow this to be overridden nicely?
+  Perl_croak("%s\n", message);
+}
+
+
+
+static sapi_module_struct sandwich_sapi = {
+  "sandwich",
+  "Ham and Cheese",
+  NULL, /* startup */
+  NULL, /* shutdown */
+  NULL, /* activate */
+  NULL, /* deactivate */
+  sandwich_sapi_ub_write,
+  NULL, /* flush */
+  NULL, /* get uid */
+  NULL, /* getenv */
+  zend_error,
+  sandwich_sapi_header_handler,
+  sandwich_sapi_send_headers,
+  NULL, /* send header */
+  NULL, /* read POST */
+  sandwich_sapi_read_cookies,
+  sandwich_sapi_reg_server_vars,
+  sandwich_sapi_log_message,
+  STANDARD_SAPI_MODULE_PROPERTIES
+};
+
+static int ze_started = 0;
+
+int sandwich_php_interpreter_create()
+{
+  zend_compiler_globals *compiler_globals;
+  zend_executor_globals *executor_globals;
+  php_core_globals *core_globals;
+  sapi_globals_struct *sapi_globals;
+  void ***tsrm_ls;
+
+  if(ze_started) {
+    return 0;
+  }
+  pthread_key_create(&sandwich_per_thread_info_key, NULL);
+
+  if (!tsrm_startup(128, 32, TSRM_ERROR_LEVEL_CORE, "/opt/ecelerity/var/log/TSRM.log")) {
+    Perl_croak("Failed to init PHP TSRM!\n");
+    return -1;
+  }
+  
+  compiler_globals = ts_resource(compiler_globals_id);
+  executor_globals = ts_resource(executor_globals_id);
+  core_globals = ts_resource(core_globals_id);
+  sapi_globals = ts_resource(sapi_globals_id);
+  tsrm_ls = ts_resource(0);
+
+  sandwich_sapi.php_ini_path_override = "/opt/ecelerity/3rdParty/lib/php.ini";
+  sapi_startup(&sandwich_sapi);
+  ze_started = 1;
+  /* if (FAILURE == php_module_startup(&sandwich_sapi, &ec_php_ext_module, 1)) { */
+  if (FAILURE == php_module_startup(&sandwich_sapi, NULL, 0)) {
+    fprintf(stderr, "Failed to initialize PHP\n");
+    return -1;
+  }
+  return 0;
+}
+
+sandwich_per_interp *sandwich_per_interp_setup() 
+{
+  /* if (!conf->eval_ok) return NULL; */
+  sandwich_per_interp *info;
+  void *old_ctx = NULL;
+
+  info = calloc(sizeof(*info), 1);
+
+  if (!info->ctx) {
+    info->ctx = tsrm_new_interpreter_context();
+    old_ctx = tsrm_set_interpreter_context(info->ctx);
+    {
+      TSRMLS_FETCH();
+      
+      zend_alter_ini_entry("register_argc_argv", 19, "0", 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);
+  
+      SG(headers_sent) = 1;
+      SG(request_info).no_headers = 1;
+      SG(server_context) = info;
+      SG(options) = SAPI_OPTION_NO_CHDIR;
+      php_request_startup(TSRMLS_C);
+      PG(during_request_startup) = 0;
+      tsrm_set_interpreter_context(old_ctx);
+    }
+  }
+  return info;
+}
+
+void sandwich_per_interp_shutdown(sandwich_per_interp *interp)
+{
+  void *old_ctx = NULL;
+  old_ctx = tsrm_set_interpreter_context(interp->ctx);
+  {
+    TSRMLS_FETCH();
+    php_request_shutdown(NULL);
+    tsrm_free_interpreter_context(interp->ctx);
+    free(interp);
+  }
+  tsrm_set_interpreter_context(old_ctx);
+}
+
+void sandwich_eval(sandwich_per_interp *interp, char *code)
+{
+  void *old_ctx = NULL;
+  old_ctx = tsrm_set_interpreter_context(interp->ctx);
+  {
+    TSRMLS_FETCH();
+    zend_try {
+      if (FAILURE == zend_eval_string_ex(code, NULL, "PHP::eval", 1 TSRMLS_CC)) {
+        Perl_croak("Failed to eval your PHP code\n");
+        goto cleanup;
+      }
+    } zend_catch {
+      Perl_croak("Failed to eval your PHP code\n");
+      goto cleanup;
+    } zend_end_try() {
+    }
+  }
+cleanup:
+  tsrm_set_interpreter_context(old_ctx);
+  return;
+}
+
+void sandwich_include(sandwich_per_interp *interp, char *file)
+{
+  void *old_ctx = NULL;
+  old_ctx = tsrm_set_interpreter_context(interp->ctx);
+  {
+    zend_file_handle file_handle;
+    TSRMLS_FETCH();
+    zend_try {
+        SG(request_info).path_translated = file;
+        file_handle.type = ZEND_HANDLE_FILENAME;
+        file_handle.handle.fd = 0;
+        file_handle.filename = SG(request_info).path_translated;
+        file_handle.opened_path = NULL;
+        file_handle.free_filename = 0;
+fprintf(stderr, "executing %s\n", file_handle.filename);
+        php_execute_script(&file_handle TSRMLS_CC);
+    } zend_catch {
+      Perl_croak("Failed to eval your PHP code\n");
+      goto cleanup;
+    } zend_end_try() {
+    }
+  }
+cleanup:
+  tsrm_set_interpreter_context(old_ctx);
+  return;
+}
+
+zval *sandwich_call_function(sandwich_per_interp *interp, zval *method, zval **params, zend_uint param_count)
+{
+  void *old_ctx = NULL;
+  old_ctx = tsrm_set_interpreter_context(interp->ctx);
+  {
+    TSRMLS_FETCH();
+    zend_try {
+      zval *retval;
+      MAKE_STD_ZVAL(retval);
+      if(call_user_function(EG(function_table), NULL, method, retval, param_count, params TSRMLS_CC) == FAILURE) {
+        return NULL;
+      }
+      return retval;
+    } zend_catch {
+      Perl_croak("Failed to eval your PHP code\n");
+      goto cleanup;
+    } zend_end_try() {
+    }
+  }
+cleanup:
+  tsrm_set_interpreter_context(old_ctx);
+  return;
+}
+
+/* vim: set ts=2 sts=2 ai bs=2 expandtab : */

Added: PHP-Sandwich/trunk/phpinterp.h
==============================================================================
--- (empty file)
+++ PHP-Sandwich/trunk/phpinterp.h	Wed Apr 13 20:31:34 2005
@@ -0,0 +1,29 @@
+#undef module_name
+#undef API_EXPORT
+#include <main/php_config.h>
+#include <main/php.h>
+#include <main/php_ini.h>
+#include <main/php_main.h>
+#include <zend.h>
+#include <zend_API.h>
+#include <zend_compile.h>
+#include <zend_ini.h>
+#include <SAPI.h>
+#include <TSRM.h>
+
+
+typedef struct {
+  void *ctx;
+} sandwich_per_interp;
+
+int sandwich_php_interpreter_create();
+
+sandwich_per_interp *sandwich_per_interp_setup();
+
+void sandwhich_per_interp_shutdown(sandwich_per_interp *interp);
+
+void sandwich_eval(sandwich_per_interp *interp, char *code);
+void sandwich_include(sandwich_per_interp *interp, char *file);
+zval *sandwich_call_function(sandwich_per_interp *interp, zval *method, zval **params, zend_uint param_count);
+
+

Added: PHP-Sandwich/trunk/stuff/TSRM.patch
==============================================================================
--- (empty file)
+++ PHP-Sandwich/trunk/stuff/TSRM.patch	Wed Apr 13 20:31:34 2005
@@ -0,0 +1,124 @@
+Index: TSRM.c
+===================================================================
+RCS file: /repository/TSRM/TSRM.c,v
+retrieving revision 1.64
+diff -u -p -r1.64 TSRM.c
+--- TSRM.c	20 Mar 2005 09:03:40 -0000	1.64
++++ TSRM.c	8 Apr 2005 16:01:17 -0000
+@@ -379,6 +379,97 @@ TSRM_API void *ts_resource_ex(ts_rsrc_id
+ #endif
+ }
+
++/* frees and interpreter context.  You are responsible for making sure that
++ * it is not linked into the TSRM hash, and not marked as the current interpreter */
++void tsrm_free_interpreter_context(void *context)
++{
++	tsrm_tls_entry *next, *thread_resources = (tsrm_tls_entry*)context;
++	int i;
++
++	while (thread_resources) {
++		next = thread_resources->next;
++
++		for (i=0; i<thread_resources->count; i++) {
++			if (resource_types_table[i].dtor) {
++				resource_types_table[i].dtor(thread_resources->storage[i], &thread_resources->storage);
++			}
++		}
++		for (i=0; i<thread_resources->count; i++) {
++			free(thread_resources->storage[i]);
++		}
++		free(thread_resources->storage);
++		free(thread_resources);
++		thread_resources = next;
++	}
++	free(context);
++}
++
++void *tsrm_set_interpreter_context(void *new_ctx)
++{
++	tsrm_tls_entry *current;
++
++#if defined(PTHREADS)
++	current = pthread_getspecific(tls_key);
++#elif defined(TSRM_ST)
++	current = st_thread_getspecific(tls_key);
++#elif defined(TSRM_WIN32)
++	current = TlsGetValue(tls_key);
++#elif defined(BETHREADS)
++	current = (tsrm_tls_entry*)tls_get(tls_key);
++#else
++#warning tsrm_set_interpreter_context is probably broken on this platform
++	current = NULL;
++#endif
++
++	/* TODO: unlink current from the global linked list, and replace it
++	 * it with the new context, protected by mutex where appropriate */
++
++	/* Set thread local storage to this new thread resources structure */
++#if defined(PTHREADS)
++	pthread_setspecific(tls_key, new_ctx);
++#elif defined(TSRM_ST)
++	st_thread_setspecific(tls_key, new_ctx);
++#elif defined(TSRM_WIN32)
++	TlsSetValue(tls_key, new_ctx);
++#elif defined(BETHREADS)
++	tls_set(tls_key, new_ctx);
++#endif
++
++	/* return old context, so caller can restore it when they're done */
++	return current;
++}
++
++
++/* allocates a new interpreter context */
++void *tsrm_new_interpreter_context(void)
++{
++	tsrm_tls_entry *new_ctx, *current;
++	THREAD_T thread_id;
++
++	thread_id = tsrm_thread_id();
++	tsrm_mutex_lock(tsmm_mutex);
++
++#if defined(PTHREADS)
++	current = pthread_getspecific(tls_key);
++#elif defined(TSRM_ST)
++	current = st_thread_getspecific(tls_key);
++#elif defined(TSRM_WIN32)
++	current = TlsGetValue(tls_key);
++#elif defined(BETHREADS)
++	current = (tsrm_tls_entry*)tls_get(tls_key);
++#else
++#warning tsrm_new_interpreter_context is probably broken on this platform
++	current = NULL;
++#endif
++
++	new_ctx = malloc(sizeof(*new_ctx));
++	allocate_new_resource(&new_ctx, thread_id);
++	
++	/* switch back to the context that was in use prior to our creation
++	 * of the new one */
++	return tsrm_set_interpreter_context(current);
++}
++
+
+ /* frees all resources allocated for the current thread */
+ void ts_free_thread(void)
+Index: TSRM.h
+===================================================================
+RCS file: /repository/TSRM/TSRM.h,v
+retrieving revision 1.48
+diff -u -p -r1.48 TSRM.h
+--- TSRM.h	17 Mar 2005 08:15:22 -0000	1.48
++++ TSRM.h	8 Apr 2005 16:01:17 -0000
+@@ -132,6 +132,12 @@ TSRM_API int tsrm_mutex_unlock(MUTEX_T m
+ TSRM_API void *tsrm_set_new_thread_begin_handler(tsrm_thread_begin_func_t new_thread_begin_handler);
+ TSRM_API void *tsrm_set_new_thread_end_handler(tsrm_thread_end_func_t new_thread_end_handler);
+
++/* these 3 APIs should only be used by people that fully understand the threading model
++ * used by PHP/Zend and the select SAPI. */
++TSRM_API void *tsrm_new_interpreter_context(void);
++TSRM_API void *tsrm_set_interpreter_context(void *new_ctx);
++TSRM_API void tsrm_free_interpreter_context(void *context);
++
+ #define TSRM_SHUFFLE_RSRC_ID(rsrc_id)		((rsrc_id)+1)
+ #define TSRM_UNSHUFFLE_RSRC_ID(rsrc_id)		((rsrc_id)-1)

Added: PHP-Sandwich/trunk/t/1.pl
==============================================================================
--- (empty file)
+++ PHP-Sandwich/trunk/t/1.pl	Wed Apr 13 20:31:34 2005
@@ -0,0 +1,10 @@
+#!/opt/ecelerity/3rdParty/bin/perl 
+use PHP;
+$p = new PHP::Interpreter();
+$p2 = new PHP::Interpreter();
+$p->eval('$name = "george"; echo "hello $name";');
+$p->eval('echo "hello $name";');
+
+$p2->eval('echo "hello $name";');
+
+$p->include("/home/george/a.php");

Added: PHP-Sandwich/trunk/t/2.pl
==============================================================================
--- (empty file)
+++ PHP-Sandwich/trunk/t/2.pl	Wed Apr 13 20:31:34 2005
@@ -0,0 +1,13 @@
+#!/opt/ecelerity/3rdParty/bin/perl 
+use PHP;
+use PHP::Interpreter;
+
+$p = new PHP::Interpreter();
+$p->eval('function hello($a) { return "hello $a\n"; }');
+$arg = $p->hello('george');
+use Data::Dumper;
+
+print "REF: ".ref($arg);
+print Dumper $arg;
+
+

Added: PHP-Sandwich/trunk/typemap
==============================================================================
--- (empty file)
+++ PHP-Sandwich/trunk/typemap	Wed Apr 13 20:31:34 2005
@@ -0,0 +1,17 @@
+PHP_Interpreter T_PTROBJ_SPECIAL
+PHP_Var_Scalar T_PTROBJ_SPECIAL
+
+INPUT
+T_PTROBJ_SPECIAL
+    if (sv_derived_from($arg, \"${(my $ntt=$ntype)=~s/_/::/g;\$ntt}\")) {
+        IV tmp = SvIV((SV*)SvRV($arg));
+        $var = ($type) tmp;
+    }
+    else
+        croak(\"$var is not of type ${(my $ntt=$ntype)=~s/_/::/g;\$ntt}\")
+
+OUTPUT
+T_PTROBJ_SPECIAL
+    sv_setref_pv($arg, \"${(my $ntt=$ntype)=~s/_/::/g;\$ntt}\",
+                 (void*)$var);
+