diff --git a/Doc/Manual/Php.html b/Doc/Manual/Php.html index 1752168b2..881b837e9 100644 --- a/Doc/Manual/Php.html +++ b/Doc/Manual/Php.html @@ -1008,8 +1008,8 @@ that derives from both the class in question and a special Swig::Director 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 struct swig_object_wrapper +which is used to implement the PHP proxy classes.

@@ -1064,12 +1064,12 @@ infinite loop.

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 ->_cPtr. This is exactly what happens without directors -and is true even if directors are enabled for the particular class in -question. When a class derived 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 struct swig_object_wrapper. This is true whether or not +directors are enabled for the particular class in question. However +when a class derived 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++ diff --git a/Examples/test-suite/import_nomodule.i b/Examples/test-suite/import_nomodule.i index 60ef7e0f6..48e119517 100644 --- a/Examples/test-suite/import_nomodule.i +++ b/Examples/test-suite/import_nomodule.i @@ -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; diff --git a/Examples/test-suite/php/director_unroll_runme.php b/Examples/test-suite/php/director_unroll_runme.php index a4453a8cd..862bd4665 100644 --- a/Examples/test-suite/php/director_unroll_runme.php +++ b/Examples/test-suite/php/director_unroll_runme.php @@ -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(); diff --git a/Examples/test-suite/php/import_nomodule_runme.php b/Examples/test-suite/php/import_nomodule_runme.php index 6e4f5a79a..2ebf7c9b2 100644 --- a/Examples/test-suite/php/import_nomodule_runme.php +++ b/Examples/test-suite/php/import_nomodule_runme.php @@ -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); diff --git a/Lib/php/phprun.swg b/Lib/php/phprun.swg index cbc1b4682..fd772267e 100644 --- a/Lib/php/phprun.swg +++ b/Lib/php/phprun.swg @@ -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); } } diff --git a/Source/Modules/php.cxx b/Source/Modules/php.cxx index eae2c5842..bc804cfc9 100644 --- a/Source/Modules/php.cxx +++ b/Source/Modules/php.cxx @@ -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 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); } }