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

[email protected] 22 Apr 2005 05:10:59 -0000
Newsgroups perl.php.sandwich.dev
Message-ID <[email protected]>
Author: gschlossnagle
Date: Thu Apr 21 22:10:58 2005
New Revision: 1011

Added:
   PHP-Sandwich/trunk/PHP/Class.pm
   PHP-Sandwich/trunk/t/8.t   (contents, props changed)
Modified:
   PHP-Sandwich/trunk/PHP.xs
   PHP-Sandwich/trunk/phpinterp.c
   PHP-Sandwich/trunk/phpinterp.h
   PHP-Sandwich/trunk/typemap
Log:
PHP -> Perl object pasing



Modified: PHP-Sandwich/trunk/PHP.xs
==============================================================================
--- PHP-Sandwich/trunk/PHP.xs	(original)
+++ PHP-Sandwich/trunk/PHP.xs	Thu Apr 21 22:10:58 2005
@@ -21,10 +21,14 @@
 
 #endif
 
+typedef struct {
+  zval *val;
+  sandwich_per_interp *interp;
+} *PHP_Class;
+
 #define PHP_Interpreter sandwich_per_interp *
 #define PHP_Var_Scalar zval *
 
-
 static int zval_is_assoc(zval *zptr)
 {
   zval **entry;
@@ -45,12 +49,25 @@
 
 zval *SvZval(SV *sv)
 {
-  zval *retval;
+  zval *retval = NULL;
+  int type;
   MAKE_STD_ZVAL(retval);
+
+  /* derference sv as much as possible */
   while(SvROK(sv)) {
     sv = SvRV(sv);
   }
-  switch(SvTYPE(sv)) {
+  type = SvTYPE(sv);
+
+  /* this is a crazy hack that seems necessary since $obj->{a} = 1 
+   * set's A's type to SVt_PVLV.
+   */
+  if(type == SVt_PVLV) {
+    if(SvIOK(sv)) type = SVt_IV;
+    if(SvNOK(sv)) type = SVt_NV;
+    if(SvPOK(sv)) type = SVt_PV;
+  }
+  switch(type) {
     case SVt_IV:   /* int */
       ZVAL_LONG(retval, SvIV(sv));
       break;
@@ -95,18 +112,25 @@
       }
       break;
     case SVt_PVCV: /* code */
+      fprintf(stderr, "SVt_PVCV\n");
       break;
     case SVt_PVGV: /* glob */
+      fprintf(stderr, "SVt_PVGV\n");
       break;
     case SVt_PVMG: /* magic */
+      fprintf(stderr, "SVt_PVMG\n");
+      break;
+    case SVt_PVLV: 
+      fprintf(stderr, "SVt_PVLV\n");
       break;
     default:
+      fprintf(stderr, "SVt_??? default\n");
       break;
   }
   return retval;
 }
 
-SV *newSVzval(zval *zptr)
+SV *newSVzval(zval *zptr, PHP_Interpreter interp)
 {
   SV *retval;
   switch( zptr->type) {
@@ -145,16 +169,16 @@
                 /* something bad has happened here */ 
               }
               else {
-                hv_store((HV *) retval, sk, skl - 1, newSVzval(*entry), 0);
+                hv_store((HV *) retval, sk, skl - 1, newSVzval(*entry, interp), 0);
               }
               break;
             case HASH_KEY_IS_LONG:
               if(is_assoc) {
                 char buf[32];
                 snprintf(buf, sizeof(buf), "%d", nk);
-                hv_store((HV *) retval, buf, strlen(buf), newSVzval(*entry), 0);
+                hv_store((HV *) retval, buf, strlen(buf), newSVzval(*entry, interp), 0);
               } else {
-                av_store((AV *) retval, nk, newSVzval(*entry));
+                av_store((AV *) retval, nk, newSVzval(*entry, interp));
               }
               break;
           }
@@ -164,9 +188,41 @@
       }
       break;
     case IS_OBJECT:
-      /* FIXME: return by reference as a PHP::DataType::Object, not by copy */
-      fprintf(stderr, "Unimplemented type: IS_OBJECT\n");
-      retval = &PL_sv_undef;
+        {
+          char *name;
+          zend_uint namelen;
+          char objectname[MAXPATHLEN];
+          HV *h1, *package;
+          SV * c1;
+          PHP_Class pclass;
+
+          c1 = sv_newmortal();
+          pclass = malloc(sizeof(*pclass));
+          /* FIXME: don't leak! */
+          pclass->val = zptr;
+          pclass->interp = interp;
+          sandwich_interp_inc_ref(interp);
+          if(get_class_name(zptr, &name, &namelen) < 0) {
+            name = "UNKNOWN";
+          }
+          snprintf(objectname, MAXPATHLEN, "PHP::Class::%s", name);
+          sv_setref_pv(c1, "PHP::Class", (void *) pclass);
+          h1 = (HV *)sv_2mortal((SV *)newHV());
+          hv_magic(h1, (GV*)c1, PERL_MAGIC_tied);
+          sv_magic((SV *)h1, c1, PERL_MAGIC_ext, NULL, -1);
+          retval = newRV((SV *)h1);
+          package = gv_stashpv(objectname, TRUE);
+          {
+            char objectisa[MAXPATHLEN];
+            package = gv_stashpv(objectname, 1);
+            snprintf(objectisa, MAXPATHLEN, "PHP::Class::%s::ISA", name);
+            //if(get_av(objectisa, FALSE)) {
+              av_push(get_av(objectisa, TRUE),
+                      newSVpv("PHP::Class", 0));
+            //}
+          }
+          retval = sv_bless(retval, package);
+        }
       break;
     case IS_STRING:
       retval = newSVpv(Z_STRVAL_P(zptr), Z_STRLEN_P(zptr));
@@ -192,6 +248,20 @@
   return retval;
 }
 
+static int get_class_name(zval *z, char **name, zend_uint *namelen)
+{
+  TSRMLS_FETCH();
+  if(Z_OBJ_HT_P(z)->get_class_name == NULL || Z_OBJ_HT_P(z)->get_class_name(z, name, namelen, 0 TSRMLS_CC) != SUCCESS)
+  {
+    zend_class_entry *ce;
+    ce = zend_get_class_entry(z TSRMLS_CC);
+    if(!ce) return -1;
+    *name = ce->name;
+    *namelen = ce->name_length;
+  }
+  return 0;
+}
+
 MODULE = PHP PACKAGE = PHP::Interpreter PREFIX=SAND_
 
 REQUIRE:        1.9505
@@ -203,7 +273,7 @@
     {
       PHP_Interpreter interp;
       if(sandwich_php_interpreter_create() == -1) {
-        fprintf(stderr, "Error creating Zend Runtime\n");
+        croak("Error creating Zend Runtime\n");
         RETVAL = &PL_sv_undef;
         return;
       }
@@ -273,7 +343,6 @@
         fprintf(stderr, "an error occured calling %s\n", method_name);
         RETVAL = &PL_sv_undef;
       } else {
-        SV * c1 = sv_newmortal();
         switch(retval->type) {
           case IS_NULL:
             RETVAL = &PL_sv_undef;
@@ -282,17 +351,55 @@
           case IS_BOOL:
           case IS_DOUBLE:
           case IS_STRING:
-            RETVAL = newSVzval(retval);
+            RETVAL = newSVzval(retval, interp);
             /*
 
-            RETVAL = NEWSV(0,0);
+            SV * c1 = sv_newmortal();
+            RETVAL = newSV(0);
             sv_setref_pv(c1, "PHP::Var::Scalar", (void *) retval);
             sv_magic(RETVAL, c1, PERL_MAGIC_tiedscalar, NULL, 0);
 
             */
             break;
           case IS_ARRAY:
-            RETVAL = newSVzval(retval);
+            RETVAL = newSVzval(retval, interp);
+            break;
+          case IS_OBJECT: 
+            {
+              char *name;
+              zend_uint namelen;
+              char objectname[MAXPATHLEN];
+              HV *h1, *package;
+              SV * c1;
+              PHP_Class pclass;
+
+              c1 = sv_newmortal();
+              pclass = malloc(sizeof(*pclass));
+              /* FIXME: don't leak! */
+              pclass->val = retval;
+              pclass->interp = interp;
+              sandwich_interp_inc_ref(interp);
+              if(get_class_name(retval, &name, &namelen) < 0) {
+                name = "UNKNOWN";
+              } 
+              snprintf(objectname, MAXPATHLEN, "PHP::Class::%s", name);
+              sv_setref_pv(c1, "PHP::Class", (void *) pclass);
+              h1 = (HV *)sv_2mortal((SV *)newHV());
+              hv_magic(h1, (GV*)c1, PERL_MAGIC_tied);
+              sv_magic((SV *)h1, c1, PERL_MAGIC_ext, NULL, -1);
+              RETVAL = newRV((SV *)h1);
+              package = gv_stashpv(objectname, TRUE);
+              {
+                char objectisa[MAXPATHLEN];
+                package = gv_stashpv(objectname, 1);
+                snprintf(objectisa, MAXPATHLEN, "PHP::Class::%s::ISA", name);
+                //if(get_av(objectisa, FALSE)) {
+                  av_push(get_av(objectisa, TRUE),
+                          newSVpv("PHP::Class", 0));
+                //}
+              }
+              RETVAL = sv_bless(RETVAL, package);
+            }
             break;
           default:
             fprintf(stderr, "unsupported return type in PHP_Interpreter_call\n");
@@ -311,39 +418,360 @@
   PHP_Interpreter interp;
   CODE:
     {
-      sandwich_per_interp_shutdown(interp);
+      sandwich_interp_dec_ref(interp);
     }
 
-MODULE = PHP PACKAGE = PHP::Var::Scalar PREFIX=PHP_V_S_
+MODULE = PHP PACKAGE = PHP::Class PREFIX=PHP_V_C_
 
 REQUIRE:        1.9505
 PROTOTYPES:     DISABLE
 
-SV* PHP_V_S_FETCH(zptr)
-  PHP_Var_Scalar zptr;
+SV* PHP_V_C_FETCH(pclass, key)
+  PHP_Class pclass;
+  char *key;
   CODE:
     {
-    fprintf(stderr, "PHP::Var::Scalar::FETCH\n");
-      RETVAL = newSVzval(zptr);
+      zend_class_entry *ce;
+      zval *retval;
+      void *old_ctx;
+
+      old_ctx = tsrm_set_interpreter_context(pclass->interp->ctx);
+      {
+        TSRMLS_FETCH();
+
+        ce = zend_get_class_entry(pclass->val TSRMLS_CC);
+        retval = zend_read_property(ce, pclass->val, key, strlen(key), 0 TSRMLS_CC);
+        RETVAL = newSVzval(retval, pclass->interp);
+      }
+      tsrm_set_interpreter_context(old_ctx);
     }
   OUTPUT:
     RETVAL
-  
-void PHP_V_S_STORE(zptr, new)
-  PHP_Var_Scalar zptr;
-  SV *new;
+
+SV* PHP_V_C_STORE(pclass, key, value)
+  PHP_Class pclass;
+  char *key;
+  SV *value;
+  CODE:
+    {
+      zend_class_entry *ce;
+      zval *retval;
+      void *old_ctx;
+
+      old_ctx = tsrm_set_interpreter_context(pclass->interp->ctx);
+      {
+        zval *tostore;
+        TSRMLS_FETCH();
+
+        ce = zend_get_class_entry(pclass->val TSRMLS_CC);
+        tostore =  SvZval(value);
+        if(tostore) zend_update_property(ce, pclass->val, key, strlen(key), tostore TSRMLS_CC);
+        else croak("problem converting perl type to SV");
+        RETVAL = newSVsv(value);
+      }
+      tsrm_set_interpreter_context(old_ctx);
+    }
+  OUTPUT:
+    RETVAL
+
+SV* PHP_V_C_FIRSTKEY(pclass)
+  PHP_Class pclass;
+  CODE:
+    {
+      char *key;
+      ulong index;
+      char buf[32];
+      HashTable *properties;
+      void *old_ctx;
+
+      old_ctx = tsrm_set_interpreter_context(pclass->interp->ctx);
+      {
+        TSRMLS_FETCH();
+        properties = Z_OBJPROP_P(pclass->val);
+        zend_hash_internal_pointer_reset(properties);
+        switch(zend_hash_get_current_key(properties, &key, &index, 0))
+        {
+          case HASH_KEY_IS_STRING:
+            RETVAL = newSVpv(key, 0);
+            break;
+          case HASH_KEY_IS_LONG:
+            RETVAL = newSViv(index);
+            break;
+          case HASH_KEY_NON_EXISTANT:
+          default:
+            RETVAL = &PL_sv_undef;
+            break;
+        }
+      }
+      tsrm_set_interpreter_context(old_ctx);
+    }
+  OUTPUT:
+    RETVAL
+   
+SV* PHP_V_C_NEXTKEY(pclass, lastkey)
+  PHP_Class pclass;
+  char *lastkey;
   CODE:
     {
-      zval *newz = SvZval(new);
-    fprintf(stderr, "PHP::Var::Scalar::STORE\n");
-      ZVAL_ZVAL(zptr, newz, 1, 1);
+      char *key;
+      ulong index;
+      char buf[32];
+      HashTable *properties;
+      void *old_ctx;
+
+      old_ctx = tsrm_set_interpreter_context(pclass->interp->ctx);
+      {
+        TSRMLS_FETCH();
+        properties = Z_OBJPROP_P(pclass->val);
+        zend_hash_move_forward(properties);
+        switch(zend_hash_get_current_key(properties, &key, &index, 0))
+        {
+          case HASH_KEY_IS_STRING:
+            RETVAL = newSVpv(key, 0);
+            break;
+          case HASH_KEY_IS_LONG:
+            RETVAL = newSViv(index);
+            break;
+          case HASH_KEY_NON_EXISTANT:
+          default:
+            RETVAL = &PL_sv_undef;
+            break;
+        }
+      }
+      tsrm_set_interpreter_context(old_ctx);
     }
+  OUTPUT:
+    RETVAL
 
-void PHP_V_S_DESTROY(zptr)
-  PHP_Var_Scalar zptr;
+SV* PHP_V_C_EXISTS(pclass, skey)
+  PHP_Class pclass;
+  SV *skey;
   CODE:
     {
-    fprintf(stderr, "PHP::Var::Scalar::DESTROY\n");
-      zval_dtor(zptr);
+      char *key;
+      uint keylen;
+      HashTable *properties;
+      void *old_ctx;
+
+      old_ctx = tsrm_set_interpreter_context(pclass->interp->ctx);
+      {
+        TSRMLS_FETCH();
+        properties = Z_OBJPROP_P(pclass->val);
+        key = SvPV(skey, keylen);
+        if(zend_hash_exists(properties, key, keylen + 1)) {
+          RETVAL = &PL_sv_yes;
+        } else {
+          RETVAL = &PL_sv_no;
+        }
+      }
+      tsrm_set_interpreter_context(old_ctx);
+    }
+  OUTPUT:
+    RETVAL
+ 
+SV *PHP_V_C_DELETE(pclass, skey)
+  PHP_Class pclass;
+  SV *skey;
+  CODE:
+    {
+      char *key;
+      uint keylen;
+      HashTable *properties;
+      zval zkey;
+      void *old_ctx;
+
+      old_ctx = tsrm_set_interpreter_context(pclass->interp->ctx);
+      {
+        TSRMLS_FETCH();
+        properties = Z_OBJPROP_P(pclass->val);
+        key = SvPV(skey, keylen);
+        INIT_ZVAL(zkey);
+        ZVAL_STRINGL(&zkey, key, keylen, 1);
+        /*
+        if(Z_OBJ_HT_P(pclass->val)->unset_property(pclass->val, &zkey TSRMLS_CC)) {
+          RETVAL = &PL_sv_yes;
+        } else {
+          RETVAL = &PL_sv_no;
+        }
+        */
+        Z_OBJ_HT_P(pclass->val)->unset_property(pclass->val, &zkey TSRMLS_CC);
+        RETVAL = &PL_sv_yes;
+        zval_dtor(&zkey);
+      }
+      tsrm_set_interpreter_context(old_ctx);
+    }
+  OUTPUT:
+    RETVAL
+
+SV *PHP_V_C__AUTOLOAD(self, method_name, ...)
+  SV *self;
+  char *method_name;
+  CODE:
+    {
+      PHP_Class pclass;
+      void *old_ctx;
+      MAGIC *mg;
+      zval method;
+      zval ***params = NULL;
+      zval calling_obj;
+      int i;
+      int param_count = 0;
+      zval *retval;
+      PHP_Interpreter interp;
+      char *croakstr = NULL;
+      
+      mg = mg_find((SvRV(self)), PERL_MAGIC_ext);
+      pclass = (PHP_Class) SvIV(SvRV(mg->mg_obj));
+
+      interp = pclass->interp;
+
+      old_ctx = tsrm_set_interpreter_context(interp->ctx);
+      {
+        zend_fcall_info fci;
+        TSRMLS_FETCH();
+        //INIT_ZVAL(calling_obj);
+        //array_init(&calling_obj);
+  
+        INIT_ZVAL(method);
+        ZVAL_STRING(&method, method_name, 1);
+       
+        
+        //PZVAL_IS_REF(pclass->val) = 1;
+        //zval_add_ref(&pclass->val);
+        //add_index_zval(&calling_obj, 0, pclass->val);
+        //add_index_zval(&calling_obj, 1, &method);
+       
+        //if(!zend_is_callable(&calling_obj, 0, NULL)) {
+        //  croakstr = "Function not callable";
+        //  goto cleanup;
+        //}
+        if(items > 2) {
+          params = emalloc(sizeof(zval **) * (items - 2));
+          for(i = 2; i< items; i++) {
+            zval *arg = SvZval(ST(i));
+            params[i - 2] = &arg;
+          }
+          param_count = items - 2;
+        }
+        fci.param_count = param_count;
+        fci.params = params;
+        fci.size = sizeof(fci);
+        fci.function_table = EG(function_table);
+        fci.function_name = &method;
+        fci.no_separation = 0;
+        fci.object_pp = &pclass->val;
+        fci.retval_ptr_ptr = &retval;
+/* 
+        if(call_user_function_ex(EG(function_table), NULL, &calling_obj, &retval, param_count, &params, 0, NULL TSRMLS_CC) != SUCCESS) {
+          fprintf(stderr, "an error occured calling %s\n", method_name);
+          RETVAL = &PL_sv_undef;
+        } 
+*/
+        if(zend_call_function(&fci, NULL TSRMLS_CC) == FAILURE) {
+          croakstr = "an error occured while calling your method";
+        }
+        else {
+          switch(retval->type) {
+            case IS_NULL:
+              RETVAL = &PL_sv_undef;
+              break;
+            case IS_LONG:
+            case IS_BOOL:
+            case IS_DOUBLE:
+            case IS_STRING:
+              RETVAL = newSVzval(retval, pclass->interp);
+              /*
+  
+              SV * c1 = sv_newmortal();
+              RETVAL = newSV(0);
+              sv_setref_pv(c1, "PHP::Var::Scalar", (void *) retval);
+              sv_magic(RETVAL, c1, PERL_MAGIC_tiedscalar, NULL, 0);
+  
+              */
+              break;
+            case IS_ARRAY:
+              RETVAL = newSVzval(retval, pclass->interp);
+              break;
+            case IS_OBJECT: 
+              {
+                char *name;
+                zend_uint namelen;
+                char objectname[MAXPATHLEN];
+                HV *h1, *package;
+                SV * c1;
+                PHP_Class pclass;
+  
+                c1 = sv_newmortal();
+                pclass = malloc(sizeof(*pclass));
+                /* FIXME: don't leak! */
+                pclass->val = retval;
+                pclass->interp = interp;
+                if(get_class_name(retval, &name, &namelen) < 0) {
+                  name = "UNKNOWN";
+                } 
+                snprintf(objectname, MAXPATHLEN, "PHP::Class::%s", name);
+                sv_setref_pv(c1, "PHP::Class", (void *) pclass);
+                h1 = (HV *)sv_2mortal((SV *)newHV());
+                hv_magic(h1, (GV*)c1, PERL_MAGIC_tied);
+                RETVAL = newRV((SV *)h1);
+                package = gv_stashpv(objectname, TRUE);
+                {
+                  char objectisa[MAXPATHLEN];
+                  package = gv_stashpv(objectname, 1);
+                  snprintf(objectisa, MAXPATHLEN, "PHP::Class::%s::ISA", name);
+                  //if(get_av(objectisa, FALSE)) {
+                    av_push(get_av(objectisa, TRUE),
+                            newSVpv("PHP::Class", 0));
+                  //}
+                }
+                sv_setref_pv(RETVAL, objectname, (void *) pclass);
+                //RETVAL = sv_bless(RETVAL, package);
+              }
+              break;
+            default:
+              fprintf(stderr, "unsupported return type in PHP_Interpreter_call\n");
+              RETVAL = &PL_sv_undef;
+              break;
+          }
+        }
+  cleanup:
+        /* FIXME: this dtor must not destroy pclass->val */
+        //zval_dtor(&calling_obj);
+        if(fci.param_count > 0) {
+          int i;
+          for (i=0; i < fci.param_count; i++) {
+              zval_ptr_dtor(fci.params[i]);
+          }
+          efree(fci.params);
+        }
+        zval_dtor(&method);
+        tsrm_set_interpreter_context(old_ctx);
+      }
+      if(croakstr) croak(croakstr);
+    }
+  OUTPUT:
+    RETVAL
+
+void PHP_V_C_DESTROY(in)
+  SV *in;
+  CODE:
+    {
+      PHP_Class pclass;
+      void *old_ctx;
+      MAGIC *mg;
+      
+      mg = mg_find((SvRV(in)), PERL_MAGIC_ext);
+      if(!mg) return;
+      pclass = (PHP_Class) SvIV(SvRV(mg->mg_obj));
+      if(!pclass) return;
+      old_ctx = tsrm_set_interpreter_context(pclass->interp->ctx);
+      {
+        TSRMLS_FETCH();
+        zval_ptr_dtor(&pclass->val);
+      }
+      tsrm_set_interpreter_context(old_ctx);
+      /* potentially shutdown ZE */
+      sandwich_interp_dec_ref(pclass->interp);
+      free(pclass);
     }
 

Added: PHP-Sandwich/trunk/PHP/Class.pm
==============================================================================
--- (empty file)
+++ PHP-Sandwich/trunk/PHP/Class.pm	Thu Apr 21 22:10:58 2005
@@ -0,0 +1,16 @@
+package PHP::Class;
+
+use strict;
+
+use vars qw/$AUTOLOAD @ISA/;
+
+sub AUTOLOAD {
+  my $self = shift;
+  my $sub = $AUTOLOAD;
+  (my $method = $sub) =~ s/.*:://;
+  $self->_AUTOLOAD($method, @_);
+}
+
+1;
+
+__END__

Modified: PHP-Sandwich/trunk/phpinterp.c
==============================================================================
--- PHP-Sandwich/trunk/phpinterp.c	(original)
+++ PHP-Sandwich/trunk/phpinterp.c	Thu Apr 21 22:10:58 2005
@@ -129,9 +129,11 @@
       tsrm_set_interpreter_context(old_ctx);
     }
   }
+  info->ref = 1;
   return info;
 }
 
+
 void sandwich_per_interp_shutdown(sandwich_per_interp *interp)
 {
   void *old_ctx = NULL;

Modified: PHP-Sandwich/trunk/phpinterp.h
==============================================================================
--- PHP-Sandwich/trunk/phpinterp.h	(original)
+++ PHP-Sandwich/trunk/phpinterp.h	Thu Apr 21 22:10:58 2005
@@ -14,6 +14,7 @@
 
 typedef struct {
   void *ctx;
+  int ref;
 } sandwich_per_interp;
 
 int sandwich_php_interpreter_create();
@@ -28,4 +29,5 @@
 /* NOTE: caller must tsrm_set_interpreter_context and reset the old one */
 zval *sandwich_call_function(sandwich_per_interp *interp, zval *method, zval **params, zend_uint param_count);
 
-
+#define sandwich_interp_inc_ref(interp)  interp->ref++
+#define sandwich_interp_dec_ref(interp)  if(--interp->ref == 0) sandwich_per_interp_shutdown(interp)

Added: PHP-Sandwich/trunk/t/8.t
==============================================================================
--- (empty file)
+++ PHP-Sandwich/trunk/t/8.t	Thu Apr 21 22:10:58 2005
@@ -0,0 +1,29 @@
+#!/opt/ecelerity/3rdParty/bin/perl 
+use strict;
+use Test::More tests => 9;
+use Data::Dumper;
+
+BEGIN {
+    diag '3 ways of calling methods.';
+    use_ok 'PHP' or die;
+    use_ok 'PHP::Interpreter' or die;
+    use_ok 'PHP::Class' or die;
+}
+
+ok my $p = new PHP::Interpreter(), "Create new PHP interpreter";
+$p->eval(q/
+class Foo { 
+  var $a = 123; 
+  function bar() { return 'george'; }
+}; 
+function foo() { return $foo = new Foo();}
+
+/);
+my $arg = $p->foo();
+
+is ref($arg), "PHP::Class::Foo", "class membership correct";
+is $arg->{a}, 123, "attribute access";
+$arg->{a} = 456;
+is $arg->{a}, 456, "attribute writing";
+is $arg->bar(), 'george', 'function calls';
+is $PHP::Class::Foo::ISA[0], 'PHP::Class', 'class inherits correctly';

Modified: PHP-Sandwich/trunk/typemap
==============================================================================
--- PHP-Sandwich/trunk/typemap	(original)
+++ PHP-Sandwich/trunk/typemap	Thu Apr 21 22:10:58 2005
@@ -1,5 +1,6 @@
 PHP_Interpreter T_PTROBJ_SPECIAL
 PHP_Var_Scalar T_PTROBJ_SPECIAL
+PHP_Class T_PTROBJ_SPECIAL
 
 INPUT
 T_PTROBJ_SPECIAL