[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, ¶ms, 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