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

View file

@ -8,7 +8,7 @@
%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
@ -16,8 +16,8 @@
* language modules).
*
* 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
* be fixable (needs more investigation).
* static type checking. It's also not (currently) possible in PHP or Ruby, but
* this may be fixable (needs more investigation).
*/
%newobject create_Foo;

View file

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

View file

@ -9,6 +9,10 @@ check::classes(array('import_nomodule','Bar'));
// now new vars
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();
import_nomodule::test1($f,42);
import_nomodule::delete_Foo($f);

View file

@ -77,64 +77,40 @@ typedef struct {
zend_object std;
} 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))
static void
SWIG_SetPointerZval(zval *z, void *ptr, swig_type_info *type, int newobject) {
/*
* First test for Null pointers. Return those as PHP native NULL
*/
if (!ptr ) {
// Return PHP NULL for a C/C++ NULL pointer.
if (!ptr) {
ZVAL_NULL(z);
return;
}
if (type->clientdata) {
swig_object_wrapper *value;
if (! (*(int *)(type->clientdata)))
zend_error(E_ERROR, "Type: %s failed to register with zend",type->name);
value=(swig_object_wrapper *)emalloc(sizeof(swig_object_wrapper));
value->ptr=ptr;
value->newobject=(newobject & 1);
if ((newobject & 2) == 0) {
/* Just register the pointer as a resource. */
ZVAL_RES(z, zend_register_resource(value, *(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);
/* Register the pointer as a resource. */
swig_object_wrapper *value=(swig_object_wrapper *)emalloc(sizeof(swig_object_wrapper));
value->ptr = ptr;
value->newobject = (newobject & 1);
ZVAL_RES(z, zend_register_resource(value, resource_type));
} else {
/*
* Wrap the resource in an object, the resource will be accessible
* via the "_cPtr" property. This code path is currently only used by
* directorin typemaps.
*/
zend_class_entry *ce = NULL;
const char *type_name = type->name+3; /* +3 so: _p_Foo -> Foo */
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)));
/* This code path is currently only used by directorin typemaps. */
zend_class_entry *ce = (zend_class_entry*)(type->clientdata);
zend_object *obj = ce->create_object(ce);
swig_object_wrapper *value = php_fetch_object(obj);
value->ptr = ptr;
value->newobject = (newobject & 1);
ZVAL_OBJ(z, obj);
}
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));
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);
}
#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));
}
/* We allow passing of a RESOURCE pointing to the object or an OBJECT whose
_cPtr is a resource pointing to the object */
/* We allow passing of a RESOURCE wrapping a non-class pointer or an OBJECT
wrapping a pointer to an object. */
static int
SWIG_ConvertPtr(zval *z, void **ptr, swig_type_info *ty, int flags) {
if (z == NULL) {
@ -229,31 +186,10 @@ SWIG_ConvertPtr(zval *z, void **ptr, swig_type_info *ty, int flags) {
}
switch (Z_TYPE_P(z)) {
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! */
*ptr = SWIG_Z_FETCH_OBJ_P(z)->ptr;
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_OBJECT:
/* FIXME - we need to check the type is compatible here! */
*ptr = SWIG_Z_FETCH_OBJ_P(z)->ptr;
return (*ptr == NULL ? -1 : 0);
case IS_RESOURCE:
*ptr = SWIG_ConvertResourcePtr(z, ty, flags);
return (*ptr == NULL ? -1 : 0);
@ -265,34 +201,8 @@ SWIG_ConvertPtr(zval *z, void **ptr, swig_type_info *ty, int flags) {
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
SWIG_SetZval( zval *zv, int newFlow, int userNewObj, void *ptr, swig_type_info *type, zend_object *std) {
if (!ptr) {
ZVAL_NULL(zv);
return;
@ -301,9 +211,10 @@ SWIG_SetZval( zval *zv, int newFlow, int userNewObj, void *ptr, swig_type_info *
if (newFlow) {
if (newFlow == 1)
ZVAL_OBJ(zv,std);
SWIG_generalize_object(zv, ptr, userNewObj, type);
}
else {
swig_object_wrapper *obj = SWIG_Z_FETCH_OBJ_P(zv);
obj->ptr = ptr;
obj->newobject = userNewObj;
} else {
SWIG_SetPointerZval(zv, ptr, type, userNewObj);
}
}

View file

@ -32,7 +32,6 @@ static bool wrap_nonclass_global = true;
// before PHP added namespaces.
static bool wrap_nonclass_fake_class = true;
static String *NOTCLASS = NewString("Not a class");
static Node *classnode = 0;
static String *module = 0;
static String *cap_module = 0;
@ -98,7 +97,7 @@ static String *fake_class_name() {
*/
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 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, " obj = php_fetch_object(object);\n\n");
// expand %delete typemap?
if (Getattr(n, "destructor") != NULL) {
Printf(s, " if(obj->newobject)\n");
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() {
Iterator ki;
bool emitted_default_dtor = false;
if (!zend_types)
return;
ki = First(zend_types);
if (ki.key)
Printf(s_oinit, "\n /* Register resource destructors for pointer types */\n");
Iterator ki = First(zend_types);
if (!ki.key)
return;
// 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, " efree(res->ptr);\n");
Printf(s_wrappers, "}\n");
Printf(s_oinit, "\n /* Register resource destructors for non-class pointer types */\n");
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
Printf(s_wrappers, "static ZEND_RSRC_DTOR_FUNC(%s) {\n", rsrc_dtor_name);
Printf(s_wrappers, " efree(res->ptr);\n");
Printf(s_wrappers, "}\n");
emitted_default_dtor = true;
}
}
String *type = ki.key;
// 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
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
Printf(s_oinit, " SWIG_TypeClientData(SWIGTYPE%s,&le_swig%s);\n", key, key);
Delete(rsrc_dtor_name);
Printf(s_oinit, " SWIG_TypeClientData(SWIGTYPE%s,&le_swig%s);\n", type, type);
ki = Next(ki);
}
@ -1827,9 +1800,9 @@ public:
}
if (baseClassExtend && (exceptionClassFlag || is_class_wrapped(baseClassExtend))) {
Printf(s_oinit, " SWIGTYPE_%s_ce = zend_register_internal_class_ex(&SWIGTYPE_%s_internal_ce, SWIGTYPE_%s_ce);\n", class_name , class_name, baseClassExtend);
Printf(s_oinit, " SWIGTYPE_%s_ce = zend_register_internal_class_ex(&SWIGTYPE_%s_internal_ce, SWIGTYPE_%s_ce);\n", class_name, class_name, baseClassExtend);
} else {
Printf(s_oinit, " SWIGTYPE_%s_ce = zend_register_internal_class(&SWIGTYPE_%s_internal_ce);\n", class_name , class_name);
Printf(s_oinit, " SWIGTYPE_%s_ce = zend_register_internal_class(&SWIGTYPE_%s_internal_ce);\n", class_name, class_name);
}
{
@ -1847,7 +1820,7 @@ public:
String *interface = Getitem(interface_list, Iterator-1);
String *interface_ce = NewStringEmpty();
Printf(interface_ce, "php_%s_interface_ce_%d" , class_name , Iterator);
Printf(s_oinit, " zend_class_entry *%s = zend_lookup_class(zend_string_init(\"%s\", sizeof(\"%s\") - 1, 0));\n", interface_ce , interface, interface);
Printf(s_oinit, " zend_class_entry *%s = zend_lookup_class(zend_string_init(\"%s\", sizeof(\"%s\") - 1, 0));\n", interface_ce, interface, interface);
Append(append_interface, interface_ce);
Append(append_interface, " ");
}
@ -1859,7 +1832,9 @@ public:
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, " %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;
Language::classHandler(n);
@ -2453,29 +2428,21 @@ public:
static PHP *maininstance = 0;
// We use this function to be able to write out zend_register_list_destructor_ex
// lines for most things in the type table
// Collect non-class pointer types from the type table so we can set up PHP
// resource types for them later.
//
// NOTE: it's a function NOT A PHP::METHOD
extern "C" {
static void typetrace(const SwigType *ty, String *mangled, String *clientdata) {
Node *class_node;
if (!zend_types) {
zend_types = NewHash();
}
// we want to know if the type which reduced to this has a constructor
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);
if (maininstance->classLookup(ty) == NULL) {
// a non-class pointer
if (!zend_types) {
zend_types = NewHash();
}
} else { // a non-class pointer
Setattr(zend_types, mangled, NOTCLASS);
Setattr(zend_types, mangled, mangled);
}
if (r_prevtracefunc)
(*r_prevtracefunc) (ty, mangled, (String *) clientdata);
(*r_prevtracefunc) (ty, mangled, clientdata);
}
}