diff options
author | Olly Betts <olly@survex.com> | 2022-01-20 14:42:02 +1300 |
---|---|---|
committer | Olly Betts <olly@survex.com> | 2022-01-20 14:42:02 +1300 |
commit | c417250b4ef8c945b602722642ab3b7c82acb7ca (patch) | |
tree | 67853e698564fac861677d5903a720a9a282518d | |
parent | 0cfc750d44cb9b4efe40b7bdaec44b930c7f4a9d (diff) | |
download | swig-c417250b4ef8c945b602722642ab3b7c82acb7ca.tar.gz |
[php] Allow testing if an object is SWIG-wrapped
Since the switch to wrapping classes using PHP's C API, we now
internally need to be able to tell if a PHP object is of or derived
from a class that is wrapped by SWIG so we know if we can offset the
zend_object pointer to get to the swig_object_wrapper. If we try to
do this to an object which isn't wrapped by SWIG then we invoke C/C++
undefined behaviour (and typically get a segmentation fault).
This check is implemented by having a SWIG\wrapped empty interface which
we make all SWIG-wrapped classes implement simply so we can test for it
to detect such classes.
Fixes #2125
-rw-r--r-- | Examples/test-suite/php/overload_simple_runme.php | 7 | ||||
-rw-r--r-- | Lib/php/phprun.swg | 19 | ||||
-rw-r--r-- | Source/Modules/php.cxx | 13 |
3 files changed, 35 insertions, 4 deletions
diff --git a/Examples/test-suite/php/overload_simple_runme.php b/Examples/test-suite/php/overload_simple_runme.php index 3cba1d35b..949de9d98 100644 --- a/Examples/test-suite/php/overload_simple_runme.php +++ b/Examples/test-suite/php/overload_simple_runme.php @@ -187,8 +187,9 @@ check("' 11 '", 'char *'); # Check TypeError is thrown when the wrong type is passed. check("array()", NULL); -# FIXME: These need fixing -#check("function(){}", NULL); -#check("new stdClass()", NULL); +check("function(){}", NULL); +check("new stdClass()", NULL); +class NotASwigWrappedClass { }; +check("new NotASwigWrappedClass()", NULL); check::done(); diff --git a/Lib/php/phprun.swg b/Lib/php/phprun.swg index a24b2551c..97d17d9ca 100644 --- a/Lib/php/phprun.swg +++ b/Lib/php/phprun.swg @@ -93,6 +93,12 @@ static int default_error_code = E_ERROR; #define SWIG_GetModule(clientdata) SWIG_Php_GetModule() #define SWIG_SetModule(clientdata, pointer) SWIG_Php_SetModule(pointer, *(int*)clientdata) +static zend_class_entry SWIG_Php_swig_wrapped_interface_ce; + +#if PHP_MAJOR_VERSION == 7 +# define zend_class_implements_interface(C, I) instanceof_function_ex(C, I, 1) +#endif + /* used to wrap returned objects in so we know whether they are newobject and need freeing, or not */ typedef struct { @@ -156,7 +162,18 @@ SWIG_ConvertPtrAndOwn(zval *z, void **ptr, swig_type_info *ty, int flags, swig_o switch (Z_TYPE_P(z)) { case IS_OBJECT: { - swig_object_wrapper *value = SWIG_Z_FETCH_OBJ_P(z); + zend_object *obj = Z_OBJ_P(z); + swig_object_wrapper *value; + if (ty && ty->clientdata == (void*)obj->ce) { + // Object is exactly the class asked for - this handles common cases cheaply, + // and in particular the PHP classes we use to wrap a pointer to a non-class. + } else if (!zend_class_implements_interface(obj->ce, &SWIG_Php_swig_wrapped_interface_ce)) { + // Not an object we've wrapped. + return -1; + } + + /* convert and cast value->ptr from value->type to ptr as ty. */ + value = swig_php_fetch_object(obj); if (!ty) { /* They don't care about the target type, so just pass on the pointer! */ *ptr = value->ptr; diff --git a/Source/Modules/php.cxx b/Source/Modules/php.cxx index 1887bba3c..533ea2fc6 100644 --- a/Source/Modules/php.cxx +++ b/Source/Modules/php.cxx @@ -87,6 +87,15 @@ static String *fake_class_name() { return result; } +static String *swig_wrapped_interface_ce() { + static String *result = NULL; + if (!result) { + result = NewStringf("SWIG_Php_swig_wrapped_interface_ce"); + Printf(s_oinit, " INIT_CLASS_ENTRY(%s, \"SWIG\\\\wrapped\", NULL);\n", result); + } + return result; +} + /* To reduce code size (generated and compiled) we only want to emit each * different arginfo once, so we need to track which have been used. */ @@ -163,12 +172,14 @@ static void SwigPHP_emit_pointer_type_registrations() { while (ki.key) { String *type = ki.key; + String *swig_wrapped = swig_wrapped_interface_ce(); Printf(s_creation, "/* class entry for pointer to %s */\n", type); Printf(s_creation, "static zend_class_entry *SWIG_Php_ce_%s;\n\n", type); Printf(s_oinit, " INIT_CLASS_ENTRY(internal_ce, \"%s\\\\%s\", NULL);\n", "SWIG", type); Printf(s_oinit, " SWIG_Php_ce_%s = zend_register_internal_class(&internal_ce);\n", type); Printf(s_oinit, " SWIG_Php_ce_%s->create_object = swig_ptr_object_new;\n", type); + Printv(s_oinit, " zend_do_implement_interface(SWIG_Php_ce_", type, ", &", swig_wrapped, ");\n", NIL); Printf(s_oinit, " SWIG_TypeClientData(SWIGTYPE%s,SWIG_Php_ce_%s);\n", type, type); Printf(s_oinit, "\n"); @@ -1633,6 +1644,8 @@ public: if (Getattr(n, "abstracts") && !GetFlag(n, "feature:notabstract")) { Printf(s_oinit, " SWIG_Php_ce_%s->ce_flags |= ZEND_ACC_EXPLICIT_ABSTRACT_CLASS;\n", class_name); } + String *swig_wrapped = swig_wrapped_interface_ce(); + Printv(s_oinit, " zend_do_implement_interface(SWIG_Php_ce_", class_name, ", &", swig_wrapped, ");\n", NIL); { Node *node = NewHash(); |