php: Wrap classes using only swig_object_wrapper

We no longer use PHP resources to wrap classes, and the proxy classes no
longer has a _cPtr property.
This commit is contained in:
Olly Betts 2021-04-04 07:45:20 +12:00
commit 40da8bcbb6
6 changed files with 83 additions and 203 deletions

View file

@ -1008,8 +1008,8 @@ that derives from both the class in question and a special
<tt>Swig::Director</tt> class. These new classes, referred to as director <tt>Swig::Director</tt> class. These new classes, referred to as director
classes, can be loosely thought of as the C++ equivalent of the PHP classes, can be loosely thought of as the C++ equivalent of the PHP
proxy classes. The director classes store a pointer to their underlying proxy classes. The director classes store a pointer to their underlying
PHP object. Indeed, this is quite similar to the "_cPtr" and "thisown" PHP object. Indeed, this is quite similar to <tt>struct swig_object_wrapper</tt>
members of the PHP proxy classes. which is used to implement the PHP proxy classes.
</p> </p>
<p> <p>
@ -1064,12 +1064,12 @@ infinite loop.
<p> <p>
One more point needs to be made about the relationship between director One more point needs to be made about the relationship between director
classes and proxy classes. When a proxy class instance is created in classes and proxy classes. When a proxy class instance is created in
PHP, SWIG creates an instance of the original C++ class and assigns it PHP, SWIG creates an instance of the original C++ class and stores it
to <tt>-&gt;_cPtr</tt>. This is exactly what happens without directors in the <tt>struct swig_object_wrapper</tt>. This is true whether or not
and is true even if directors are enabled for the particular class in directors are enabled for the particular class in question. However
question. When a class <i>derived</i> from a proxy class is created, when a class <i>derived</i> from a proxy class is created, SWIG instead
however, SWIG then creates an instance of the corresponding C++ director creates an instance of the corresponding C++ director class.
class. The reason for this difference is that user-defined subclasses The reason for this difference is that user-defined subclasses
may override or extend methods of the original class, so the director may override or extend methods of the original class, so the director
class is needed to route calls to these methods correctly. For class is needed to route calls to these methods correctly. For
unmodified proxy classes, all methods are ultimately implemented in C++ unmodified proxy classes, all methods are ultimately implemented in C++

View file

@ -8,7 +8,7 @@
%import "import_nomodule.h" %import "import_nomodule.h"
#if !defined(SWIGJAVA) && !defined(SWIGRUBY) && !defined(SWIGCSHARP) && !defined(SWIGD) && !defined(SWIGPYTHON_BUILTIN) #if !defined(SWIGJAVA) && !defined(SWIGRUBY) && !defined(SWIGCSHARP) && !defined(SWIGD) && !defined(SWIGPYTHON_BUILTIN) && !defined(SWIGPHP)
/** /**
* The proxy class does not have Bar derived from Foo, yet an instance of Bar * The proxy class does not have Bar derived from Foo, yet an instance of Bar
@ -16,8 +16,8 @@
* language modules). * language modules).
* *
* This violation of the type system is not possible in Java, C# and D due to * This violation of the type system is not possible in Java, C# and D due to
* static type checking. It's also not (currently) possible in Ruby, but this may * static type checking. It's also not (currently) possible in PHP or Ruby, but
* be fixable (needs more investigation). * this may be fixable (needs more investigation).
*/ */
%newobject create_Foo; %newobject create_Foo;

View file

@ -21,9 +21,7 @@ $b = new Bar();
$b->set($a); $b->set($a);
$c = $b->get(); $c = $b->get();
// FIXME: This doesn't work for checking that they wrap the same C++ object // FIXME: The python version checks that a.this == c.this, but we don't seem
// because the two objects have different PHP resources, and we can't easily // to have a way to check this with the PHP bindings we generate.
// look inside those resources to see which C++ objects they refer to.
//check::equal($a->_cPtr, $c->_cPtr, "_cPtr check failed");
check::done(); check::done();

View file

@ -9,6 +9,10 @@ check::classes(array('import_nomodule','Bar'));
// now new vars // now new vars
check::globals(array()); check::globals(array());
// SWIGPHP doesn't currently support the "violation of the type system" which
// is tested by this testcase.
exit(0);
$f = import_nomodule::create_Foo(); $f = import_nomodule::create_Foo();
import_nomodule::test1($f,42); import_nomodule::test1($f,42);
import_nomodule::delete_Foo($f); import_nomodule::delete_Foo($f);

View file

@ -77,64 +77,40 @@ typedef struct {
zend_object std; zend_object std;
} swig_object_wrapper; } swig_object_wrapper;
#define SWIG_Z_FETCH_OBJ_P(zv) php_fetch_object(Z_OBJ_P(zv))
static inline
swig_object_wrapper * php_fetch_object(zend_object *obj) {
return (swig_object_wrapper *)((char *)obj - XtOffsetOf(swig_object_wrapper, std));
}
#define SWIG_as_voidptr(a) const_cast< void * >(static_cast< const void * >(a)) #define SWIG_as_voidptr(a) const_cast< void * >(static_cast< const void * >(a))
static void static void
SWIG_SetPointerZval(zval *z, void *ptr, swig_type_info *type, int newobject) { SWIG_SetPointerZval(zval *z, void *ptr, swig_type_info *type, int newobject) {
/* // Return PHP NULL for a C/C++ NULL pointer.
* First test for Null pointers. Return those as PHP native NULL
*/
if (!ptr) { if (!ptr) {
ZVAL_NULL(z); ZVAL_NULL(z);
return; return;
} }
if (type->clientdata) { if (type->clientdata) {
swig_object_wrapper *value; if ((newobject & 2) == 0) {
if (! (*(int *)(type->clientdata))) int resource_type = *(int *)(type->clientdata);
if (resource_type == 0)
zend_error(E_ERROR, "Type: %s failed to register with zend", type->name); zend_error(E_ERROR, "Type: %s failed to register with zend", type->name);
value=(swig_object_wrapper *)emalloc(sizeof(swig_object_wrapper)); /* Register the pointer as a resource. */
swig_object_wrapper *value=(swig_object_wrapper *)emalloc(sizeof(swig_object_wrapper));
value->ptr = ptr; value->ptr = ptr;
value->newobject = (newobject & 1); value->newobject = (newobject & 1);
if ((newobject & 2) == 0) { ZVAL_RES(z, zend_register_resource(value, resource_type));
/* Just register the pointer as a resource. */
ZVAL_RES(z, zend_register_resource(value, *(int *)(type->clientdata)));
} else { } else {
/* /* This code path is currently only used by directorin typemaps. */
* Wrap the resource in an object, the resource will be accessible zend_class_entry *ce = (zend_class_entry*)(type->clientdata);
* via the "_cPtr" property. This code path is currently only used by zend_object *obj = ce->create_object(ce);
* directorin typemaps. swig_object_wrapper *value = php_fetch_object(obj);
*/ value->ptr = ptr;
zend_class_entry *ce = NULL; value->newobject = (newobject & 1);
const char *type_name = type->name+3; /* +3 so: _p_Foo -> Foo */ ZVAL_OBJ(z, obj);
size_t type_name_len;
const char * p;
/* Namespace__Foo -> Foo */
/* FIXME: ugly and goes wrong for classes with __ in their names. */
while ((p = strstr(type_name, "__")) != NULL) {
type_name = p + 2;
}
type_name_len = strlen(type_name);
if (SWIG_PREFIX_LEN > 0) {
zend_string * classname = zend_string_alloc(SWIG_PREFIX_LEN + type_name_len, 0);
memcpy(ZSTR_VAL(classname), SWIG_PREFIX, SWIG_PREFIX_LEN);
memcpy(ZSTR_VAL(classname) + SWIG_PREFIX_LEN, type_name, type_name_len);
ce = zend_lookup_class(classname);
zend_string_release(classname);
} else {
zend_string * classname = zend_string_init(type_name, type_name_len, 0);
ce = zend_lookup_class(classname);
zend_string_release(classname);
}
if (ce == NULL) {
/* class does not exist */
object_init(z);
} else {
object_init_ex(z, ce);
}
add_property_resource_ex(z, "_cPtr", sizeof("_cPtr") - 1, zend_register_resource(value, *(int *)(type->clientdata)));
} }
return; return;
} }
@ -197,30 +173,11 @@ SWIG_ConvertResourcePtr(zval *z, swig_type_info *ty, int flags) {
type_name=zend_rsrc_list_get_rsrc_type(Z_RES_P(z)); type_name=zend_rsrc_list_get_rsrc_type(Z_RES_P(z));
if (!type_name) {
if (Z_TYPE_P(z) == IS_OBJECT) {
#if PHP_MAJOR_VERSION < 8
HashTable * ht = Z_OBJ_HT_P(z)->get_properties(z);
#else
HashTable * ht = Z_OBJ_HT_P(z)->get_properties(Z_OBJ_P(z));
#endif
zval * _cPtr = zend_hash_str_find(ht, "_cPtr", sizeof("_cPtr") - 1);
type_name=zend_rsrc_list_get_rsrc_type(Z_RES_P(_cPtr));
}
}
return SWIG_ConvertResourceData(p, type_name, ty); return SWIG_ConvertResourceData(p, type_name, ty);
} }
#define SWIG_Z_FETCH_OBJ_P(zv) php_fetch_object(Z_OBJ_P(zv)) /* We allow passing of a RESOURCE wrapping a non-class pointer or an OBJECT
wrapping a pointer to an object. */
static inline
swig_object_wrapper * php_fetch_object(zend_object *obj) {
return (swig_object_wrapper *)((char *)obj - XtOffsetOf(swig_object_wrapper, std));
}
/* We allow passing of a RESOURCE pointing to the object or an OBJECT whose
_cPtr is a resource pointing to the object */
static int static int
SWIG_ConvertPtr(zval *z, void **ptr, swig_type_info *ty, int flags) { SWIG_ConvertPtr(zval *z, void **ptr, swig_type_info *ty, int flags) {
if (z == NULL) { if (z == NULL) {
@ -229,31 +186,10 @@ SWIG_ConvertPtr(zval *z, void **ptr, swig_type_info *ty, int flags) {
} }
switch (Z_TYPE_P(z)) { switch (Z_TYPE_P(z)) {
case IS_OBJECT: { case IS_OBJECT:
#if PHP_MAJOR_VERSION < 8
HashTable * ht = Z_OBJ_HT_P(z)->get_properties(z);
#else
HashTable * ht = Z_OBJ_HT_P(z)->get_properties(Z_OBJ_P(z));
#endif
if (ht) {
zval * _cPtr = zend_hash_str_find(ht, "_cPtr", sizeof("_cPtr") - 1);
if (_cPtr) {
if (Z_TYPE_P(_cPtr) == IS_NULL) {
/* FIXME - we need to check the type is compatible here! */ /* FIXME - we need to check the type is compatible here! */
*ptr = SWIG_Z_FETCH_OBJ_P(z)->ptr; *ptr = SWIG_Z_FETCH_OBJ_P(z)->ptr;
return (*ptr == NULL ? -1 : 0); return (*ptr == NULL ? -1 : 0);
}
if (Z_TYPE_P(_cPtr) == IS_INDIRECT) {
_cPtr = Z_INDIRECT_P(_cPtr);
}
if (Z_TYPE_P(_cPtr) == IS_RESOURCE) {
*ptr = SWIG_ConvertResourcePtr(_cPtr, ty, flags);
return (*ptr == NULL ? -1 : 0);
}
}
}
break;
}
case IS_RESOURCE: case IS_RESOURCE:
*ptr = SWIG_ConvertResourcePtr(z, ty, flags); *ptr = SWIG_ConvertResourcePtr(z, ty, flags);
return (*ptr == NULL ? -1 : 0); return (*ptr == NULL ? -1 : 0);
@ -265,34 +201,8 @@ SWIG_ConvertPtr(zval *z, void **ptr, swig_type_info *ty, int flags) {
return -1; return -1;
} }
static void
SWIG_pack_zval(zval *zv, void *ptr, int userNewObj) {
swig_object_wrapper *obj = SWIG_Z_FETCH_OBJ_P(zv);
obj->ptr = ptr;
obj->newobject = userNewObj;
}
static void
SWIG_generalize_object(zval *zval_obj, void *ptr, int userNewObj, swig_type_info *type) {
HashTable *ht = 0;
SWIG_pack_zval(zval_obj, ptr, userNewObj);
#if PHP_MAJOR_VERSION < 8
ht = Z_OBJ_HT_P(zval_obj)->get_properties(zval_obj);
#else
ht = Z_OBJ_HT_P(zval_obj)->get_properties(Z_OBJ_P(zval_obj));
#endif
if(ht) {
zval z;
ZVAL_NULL(&z);
zend_hash_str_add(ht, "_cPtr", sizeof("_cPtr") - 1, &z);
}
}
static void static void
SWIG_SetZval( zval *zv, int newFlow, int userNewObj, void *ptr, swig_type_info *type, zend_object *std) { SWIG_SetZval( zval *zv, int newFlow, int userNewObj, void *ptr, swig_type_info *type, zend_object *std) {
if (!ptr) { if (!ptr) {
ZVAL_NULL(zv); ZVAL_NULL(zv);
return; return;
@ -301,9 +211,10 @@ SWIG_SetZval( zval *zv, int newFlow, int userNewObj, void *ptr, swig_type_info *
if (newFlow) { if (newFlow) {
if (newFlow == 1) if (newFlow == 1)
ZVAL_OBJ(zv,std); ZVAL_OBJ(zv,std);
SWIG_generalize_object(zv, ptr, userNewObj, type); swig_object_wrapper *obj = SWIG_Z_FETCH_OBJ_P(zv);
} obj->ptr = ptr;
else { obj->newobject = userNewObj;
} else {
SWIG_SetPointerZval(zv, ptr, type, userNewObj); SWIG_SetPointerZval(zv, ptr, type, userNewObj);
} }
} }

View file

@ -32,7 +32,6 @@ static bool wrap_nonclass_global = true;
// before PHP added namespaces. // before PHP added namespaces.
static bool wrap_nonclass_fake_class = true; static bool wrap_nonclass_fake_class = true;
static String *NOTCLASS = NewString("Not a class");
static Node *classnode = 0; static Node *classnode = 0;
static String *module = 0; static String *module = 0;
static String *cap_module = 0; static String *cap_module = 0;
@ -98,7 +97,7 @@ static String *fake_class_name() {
*/ */
static Hash *arginfo_used; static Hash *arginfo_used;
/* Variables for using PHP classes */ /* Track non-class pointer types that get wrapped as resources */
static Hash *zend_types = 0; static Hash *zend_types = 0;
static int shadow = 1; static int shadow = 1;
@ -139,6 +138,7 @@ static void print_creation_free_wrapper(Node *n) {
Printf(s, " if(!object)\n\t return;\n\n"); Printf(s, " if(!object)\n\t return;\n\n");
Printf(s, " obj = php_fetch_object(object);\n\n"); Printf(s, " obj = php_fetch_object(object);\n\n");
// expand %delete typemap?
if (Getattr(n, "destructor") != NULL) { if (Getattr(n, "destructor") != NULL) {
Printf(s, " if(obj->newobject)\n"); Printf(s, " if(obj->newobject)\n");
Printf(s, " SWIG_remove((%s *)obj->ptr);\n", Getattr(n, "classtype")); Printf(s, " SWIG_remove((%s *)obj->ptr);\n", Getattr(n, "classtype"));
@ -158,59 +158,32 @@ static void print_creation_free_wrapper(Node *n) {
} }
static void SwigPHP_emit_resource_registrations() { static void SwigPHP_emit_resource_registrations() {
Iterator ki;
bool emitted_default_dtor = false;
if (!zend_types) if (!zend_types)
return; return;
ki = First(zend_types); Iterator ki = First(zend_types);
if (ki.key) if (!ki.key)
Printf(s_oinit, "\n /* Register resource destructors for pointer types */\n"); return;
while (ki.key) {
DOH *key = ki.key;
Node *class_node = ki.item;
String *human_name = key;
String *rsrc_dtor_name = NULL;
// write out body
if (class_node != NOTCLASS) {
String *destructor = Getattr(class_node, "destructor");
human_name = Getattr(class_node, "sym:name");
if (!human_name)
human_name = Getattr(class_node, "name");
// Do we have a known destructor for this type?
if (destructor) {
rsrc_dtor_name = NewStringf("_wrap_destroy%s", key);
// Write out custom destructor function
Printf(s_wrappers, "static ZEND_RSRC_DTOR_FUNC(%s) {\n", rsrc_dtor_name);
Printf(s_wrappers, " %s(res, SWIGTYPE%s->name);\n", destructor, key);
Printf(s_wrappers, "}\n");
}
}
if (!rsrc_dtor_name) {
rsrc_dtor_name = NewString("_swig_default_rsrc_destroy");
if (!emitted_default_dtor) {
// Write out custom destructor function // Write out custom destructor function
const char *rsrc_dtor_name = "_swig_default_rsrc_destroy";
Printf(s_wrappers, "static ZEND_RSRC_DTOR_FUNC(%s) {\n", rsrc_dtor_name); Printf(s_wrappers, "static ZEND_RSRC_DTOR_FUNC(%s) {\n", rsrc_dtor_name);
Printf(s_wrappers, " efree(res->ptr);\n"); Printf(s_wrappers, " efree(res->ptr);\n");
Printf(s_wrappers, "}\n"); Printf(s_wrappers, "}\n");
emitted_default_dtor = true;
} Printf(s_oinit, "\n /* Register resource destructors for non-class pointer types */\n");
} while (ki.key) {
String *type = ki.key;
// declare le_swig<mangled> to store php registration // declare le_swig<mangled> to store php registration
Printf(s_vdecl, "static int le_swig%s=0; /* handle for %s */\n", key, human_name); Printf(s_vdecl, "static int le_swig%s=0; /* handle for %s */\n", type, type);
// register with php // register with php
Printf(s_oinit, " le_swig%s=zend_register_list_destructors_ex" Printf(s_oinit, " le_swig%s=zend_register_list_destructors_ex"
"(%s, NULL, SWIGTYPE%s->name, module_number);\n", key, rsrc_dtor_name, key); "(%s, NULL, SWIGTYPE%s->name, module_number);\n", type, rsrc_dtor_name, type);
// store php type in class struct // store php type in class struct
Printf(s_oinit, " SWIG_TypeClientData(SWIGTYPE%s,&le_swig%s);\n", key, key); Printf(s_oinit, " SWIG_TypeClientData(SWIGTYPE%s,&le_swig%s);\n", type, type);
Delete(rsrc_dtor_name);
ki = Next(ki); ki = Next(ki);
} }
@ -1859,7 +1832,9 @@ public:
Printf(s_oinit, " SWIGTYPE_%s_ce->create_object = %s_object_new;\n", class_name, class_name); Printf(s_oinit, " SWIGTYPE_%s_ce->create_object = %s_object_new;\n", class_name, class_name);
Printf(s_oinit, " memcpy(&%s_object_handlers,zend_get_std_object_handlers(), sizeof(zend_object_handlers));\n", class_name); Printf(s_oinit, " memcpy(&%s_object_handlers,zend_get_std_object_handlers(), sizeof(zend_object_handlers));\n", class_name);
Printf(s_oinit, " %s_object_handlers.clone_obj = NULL;\n}\n\n", class_name); Printf(s_oinit, " %s_object_handlers.clone_obj = NULL;\n", class_name);
Printf(s_oinit, " SWIG_TypeClientData(SWIGTYPE_p%s,SWIGTYPE_%s_ce);\n", SwigType_manglestr(Getattr(n, "classtypeobj")), class_name);
Printf(s_oinit, "}\n\n");
classnode = n; classnode = n;
Language::classHandler(n); Language::classHandler(n);
@ -2453,29 +2428,21 @@ public:
static PHP *maininstance = 0; static PHP *maininstance = 0;
// We use this function to be able to write out zend_register_list_destructor_ex // Collect non-class pointer types from the type table so we can set up PHP
// lines for most things in the type table // resource types for them later.
//
// NOTE: it's a function NOT A PHP::METHOD // NOTE: it's a function NOT A PHP::METHOD
extern "C" { extern "C" {
static void typetrace(const SwigType *ty, String *mangled, String *clientdata) { static void typetrace(const SwigType *ty, String *mangled, String *clientdata) {
Node *class_node; if (maininstance->classLookup(ty) == NULL) {
// a non-class pointer
if (!zend_types) { if (!zend_types) {
zend_types = NewHash(); zend_types = NewHash();
} }
// we want to know if the type which reduced to this has a constructor Setattr(zend_types, mangled, mangled);
if ((class_node = maininstance->classLookup(ty))) {
if (!Getattr(zend_types, mangled)) {
// OK it may have been set before by a different SwigType but it would
// have had the same underlying class node I think
// - it is certainly required not to have different originating class
// nodes for the same SwigType
Setattr(zend_types, mangled, class_node);
}
} else { // a non-class pointer
Setattr(zend_types, mangled, NOTCLASS);
} }
if (r_prevtracefunc) if (r_prevtracefunc)
(*r_prevtracefunc) (ty, mangled, (String *) clientdata); (*r_prevtracefunc) (ty, mangled, clientdata);
} }
} }