[php] Generate PHP type declarations

We now automatically generate PHP type declarations for PHP >= 8.0.

The generated code still compiles with PHP 7.x but without type declarations.
This commit is contained in:
Olly Betts 2022-01-20 10:07:44 +13:00 committed by GitHub
parent 76d5a9ec27
commit 1f1349741f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 536 additions and 196 deletions

View File

@ -346,6 +346,79 @@ $c = bar(3.5); # Use default argument for 2nd parameter
</pre></div>
<p>
SWIG generates PHP type declarations for function parameters and return
types for PHP 8 and later (we don't try to support PHP 7's more limited type
declarations and the generated wrappers compiled for PHP 7 will not have any
type declarations).
</p>
<p>
You can control the generation of PHP type declarations using
the "php:type" %feature. This has three settings:
</p>
<ul>
<li> <p>If unset or set to "0" then no type declarations are generated, e.g.: <tt>%feature("php:type", "0");</tt>
</p></li>
<li> <p>If set to "1" then type declarations are generated for both parameters and return types, e.g.: <tt>%feature("php:type", "1");</tt>
</p></li>
<li> <p>The default setting is "compat", which is the same as "1" except no
return type declarations are generated for virtual methods for which
directors are enabled. This provides better compatibility for PHP
subclasses of wrapped virtual methods in existing SWIG-generated bindings, e.g.: <tt>%feature("php:type", "compat");</tt>
</p></li>
</ul>
<p>
If you have an existing PHP interface and are upgrading to SWIG &gt;= 4.1.0
then the default "compat" setting should work well.
</p>
<p>
If you're writing a new set of bindings and <b>only targetting PHP8 or newer</b>
then enabling type declarations everywhere probably makes sense. It will
only actually make a difference if you enable directors and are wrapping C++
classes with virtual methods, but doing it anyway means you won't forget to if
the code you are wrapping later evolves to have such classes and methods.
</p>
<p>
The type declaration information will make the generated source code and
compiler extension module larger, so you might want to turn off type
declarations if keeping these small is important to you. If you find you
need to turn off type declarations to fix a problem, please let us know
via our github issue tracker.
</p>
<p>
Note that being a SWIG feature this can be specified globally (like above) or
per class, per method, etc. See the <a
href="Customization.html#Customization_features">%feature directives</a>
section for full details of how to control at a fine-grained level.
</p>
<p>
The PHP type information is specified via a "phptype" attribute on "in" and
"out" typemaps, and these have been added for all the typemaps we supply for
PHP. We don't currently support this for "argout" templates, but probably
will in a future version.
</p>
<p>
If you have written custom SWIG typemaps for PHP and want to add PHP type
declarations, then the syntax is very like how you'd specify the type in
PHP code, e.g. <tt>%typemap(in, phptype="int|string|Foo")</tt> means the
typemap accepts a PHP int or string or an object of class Foo,
<tt>%typemap(in, phptype="?int")</tt> means a PHP int or NULL, etc.
As well as the standard PHP type declaration types, SWIG also understands the
special type "SWIGTYPE" as an entry in phptype, which means the PHP type
corresponding to the type that this typemap matched on - for a object this
will give you the PHP class for the object, and for a pointer to a non-class
type it will give you the name of the PHP class SWIG created for that
pointer type.
</p>
<!-- This isn't correct for 1.3.30 and needs rewriting to reflect reality
<p>
Because PHP is a dynamically typed language, the default typemaps

View File

@ -16,7 +16,8 @@
struct Geometry {
enum GeomType{
POINT,
CIRCLE
CIRCLE,
SHAPELESS
};
virtual ~Geometry() {}

View File

@ -20,11 +20,14 @@ $tr=copy_intp(4);
check::equal(4,inctr($tr),"4==incr($tr)");
check::equal(5,intp_value($tr),"5==$tr");
# Check the voidhandle call, first with null
# Check the voidhandle call, first with NULL and then with the SWIG\p_void we
# get from the first call.
$handle=NULL;
voidhandle($handle);
check::equal(get_class($handle),"SWIG\\_p_void",'$handle is not _p_void');
$handledata=handle($handle);
check::equal($handledata,"Here it is","\$handledata != \"Here it is\"");
for ($i=0; $i != 1; $i++) {
voidhandle($handle);
check::equal(get_class($handle),"SWIG\\_p_void",'$handle is not _p_void');
$handledata=handle($handle);
check::equal($handledata,"Here it is","\$handledata != \"Here it is\"");
}
check::done();

View File

@ -17,4 +17,7 @@ $point = Geometry::create(Geometry::POINT);
$w = $point->width();
check::equal($w, 1.0, "w failed");
$point = Geometry::create(Geometry::SHAPELESS);
check::equal($point, NULL, "NULL failed");
check::done();

View File

@ -100,7 +100,7 @@ if (!dcast) {
}%enddef
%define %factory(Method,Types...)
%typemap(out) Method {
%typemap(out, phptype="?SWIGTYPE") Method {
int dcast = 0;
%formacro(%_factory_dispatch, Types)
if (!dcast) {

View File

@ -4,6 +4,11 @@
* PHP configuration file
* ----------------------------------------------------------------------------- */
// Default to generating PHP type declarations (for PHP >= 8) except for
// cases which are liable to cause compatibility issues with existing
// bindings.
%feature("php:type", "compat");
%runtime "swigrun.swg" // Common C API type-checking code
%runtime "swigerrors.swg" // SWIG errors
%runtime "phprun.swg" // PHP runtime functions
@ -34,56 +39,56 @@
%include <utils.i>
%pass_by_val(bool,CONVERT_BOOL_IN);
%pass_by_val(bool, "bool", CONVERT_BOOL_IN);
%pass_by_val(size_t, CONVERT_INT_IN);
%pass_by_val(size_t, "int", CONVERT_INT_IN);
%pass_by_val(enum SWIGTYPE, CONVERT_INT_IN);
%pass_by_val(enum SWIGTYPE, "int", CONVERT_INT_IN);
%pass_by_val(signed int, CONVERT_INT_IN);
%pass_by_val(int,CONVERT_INT_IN);
%pass_by_val(unsigned int,CONVERT_INT_IN);
%pass_by_val(signed int, "int", CONVERT_INT_IN);
%pass_by_val(int,"int", CONVERT_INT_IN);
%pass_by_val(unsigned int,"int", CONVERT_INT_IN);
%pass_by_val(signed short, CONVERT_INT_IN);
%pass_by_val(short,CONVERT_INT_IN);
%pass_by_val(unsigned short, CONVERT_INT_IN);
%pass_by_val(signed short, "int", CONVERT_INT_IN);
%pass_by_val(short,"int", CONVERT_INT_IN);
%pass_by_val(unsigned short, "int", CONVERT_INT_IN);
%pass_by_val(signed long, CONVERT_INT_IN);
%pass_by_val(long, CONVERT_INT_IN);
%pass_by_val(unsigned long, CONVERT_INT_IN);
%pass_by_val(signed long, "int", CONVERT_INT_IN);
%pass_by_val(long, "int", CONVERT_INT_IN);
%pass_by_val(unsigned long, "int", CONVERT_INT_IN);
%pass_by_val(signed long long, CONVERT_LONG_LONG_IN);
%pass_by_val(long long, CONVERT_LONG_LONG_IN);
%pass_by_val(unsigned long long, CONVERT_UNSIGNED_LONG_LONG_IN);
%pass_by_val(signed long long, "int|string", CONVERT_LONG_LONG_IN);
%pass_by_val(long long, "int|string", CONVERT_LONG_LONG_IN);
%pass_by_val(unsigned long long, "int|string", CONVERT_UNSIGNED_LONG_LONG_IN);
%pass_by_val(signed char, CONVERT_INT_IN);
%pass_by_val(char, CONVERT_CHAR_IN);
%pass_by_val(unsigned char, CONVERT_INT_IN);
%pass_by_val(signed char, "int", CONVERT_INT_IN);
%pass_by_val(char, "string", CONVERT_CHAR_IN);
%pass_by_val(unsigned char, "int", CONVERT_INT_IN);
%pass_by_val(float, CONVERT_FLOAT_IN);
%pass_by_val(float, "float", CONVERT_FLOAT_IN);
%pass_by_val(double, CONVERT_FLOAT_IN);
%pass_by_val(double, "float", CONVERT_FLOAT_IN);
%pass_by_val(char *, CONVERT_STRING_IN);
%pass_by_val(char *, "string", CONVERT_STRING_IN);
%typemap(in) char *& = const char *&;
%typemap(directorout) char *& = const char *&;
// char array can be in/out, though the passed string may not be big enough...
// so we have to size it
%typemap(in) char[ANY]
%typemap(in, phptype="string") char[ANY]
%{
convert_to_string(&$input);
$1 = ($1_ltype) Z_STRVAL($input);
%}
%typemap(in) (char *STRING, int LENGTH), (char *STRING, size_t LENGTH) %{
%typemap(in, phptype="string") (char *STRING, int LENGTH), (char *STRING, size_t LENGTH) %{
convert_to_string(&$input);
$1 = ($1_ltype) Z_STRVAL($input);
$2 = ($2_ltype) Z_STRLEN($input);
%}
/* Object passed by value. Convert to a pointer */
%typemap(in) SWIGTYPE ($&1_ltype tmp)
%typemap(in, phptype="SWIGTYPE") SWIGTYPE ($&1_ltype tmp)
%{
if (SWIG_ConvertPtr(&$input, (void **) &tmp, $&1_descriptor, 0) < 0 || tmp == NULL) {
zend_type_error("Expected $&1_descriptor for argument $argnum of $symname");
@ -101,7 +106,7 @@
$result = *tmp;
%}
%typemap(in) SWIGTYPE *,
%typemap(in, phptype="?SWIGTYPE") SWIGTYPE *,
SWIGTYPE []
%{
if (SWIG_ConvertPtr(&$input, (void **) &$1, $1_descriptor, 0) < 0) {
@ -120,7 +125,7 @@
swig_acquire_ownership_obj((void*)$result, own);
%}
%typemap(in) SWIGTYPE &,
%typemap(in, phptype="SWIGTYPE") SWIGTYPE &,
SWIGTYPE &&
%{
if (SWIG_ConvertPtr(&$input, (void **) &$1, $1_descriptor, 0) < 0 || $1 == NULL) {
@ -139,7 +144,7 @@
$result = tmp;
%}
%typemap(in) SWIGTYPE *const& ($*ltype temp)
%typemap(in, phptype="?SWIGTYPE") SWIGTYPE *const& ($*ltype temp)
%{
if (SWIG_ConvertPtr(&$input, (void **) &temp, $*1_descriptor, 0) < 0) {
zend_type_error("Expected $*1_descriptor for argument $argnum of $symname");
@ -148,7 +153,7 @@
$1 = ($1_ltype)&temp;
%}
%typemap(in) SWIGTYPE *DISOWN
%typemap(in, phptype="?SWIGTYPE") SWIGTYPE *DISOWN
%{
if (SWIG_ConvertPtr(&$input, (void **) &$1, $1_descriptor, SWIG_POINTER_DISOWN) < 0) {
zend_type_error("Expected $1_descriptor for argument $argnum of $symname");
@ -161,7 +166,7 @@
SWIGTYPE &,
SWIGTYPE &&;
%typemap(in) void *
%typemap(in, phptype="?SWIGTYPE") void *
%{
if (SWIG_ConvertPtr(&$input, (void **) &$1, 0, 0) < 0) {
/* Allow NULL from php for void* */
@ -176,7 +181,7 @@
/* Special case when void* is passed by reference so it can be made to point
to opaque api structs */
%typemap(in, byref=1) void ** ($*1_ltype ptr, int force),
%typemap(in, phptype="?SWIG\\_p_void", byref=1) void ** ($*1_ltype ptr, int force),
void *& ($*1_ltype ptr, int force)
{
/* If they pass NULL by reference, make it into a void*
@ -211,7 +216,8 @@
/* Typemap for output values */
%typemap(out) int,
%typemap(out, phptype="int")
int,
unsigned int,
short,
unsigned short,
@ -224,12 +230,12 @@
RETVAL_LONG($1);
%}
%typemap(out) enum SWIGTYPE
%typemap(out, phptype="int") enum SWIGTYPE
%{
RETVAL_LONG((long)$1);
%}
%typemap(out) long long
%typemap(out, phptype="int|string") long long
%{
if ((long long)LONG_MIN <= $1 && $1 <= (long long)LONG_MAX) {
RETVAL_LONG((long)($1));
@ -239,7 +245,7 @@
RETVAL_STRING(temp);
}
%}
%typemap(out) unsigned long long
%typemap(out, phptype="int|string") unsigned long long
%{
if ($1 <= (unsigned long long)LONG_MAX) {
RETVAL_LONG((long)($1));
@ -250,7 +256,8 @@
}
%}
%typemap(out) const int &,
%typemap(out, phptype="int")
const int &,
const unsigned int &,
const short &,
const unsigned short &,
@ -264,17 +271,17 @@
RETVAL_LONG(*$1);
%}
%typemap(out) const enum SWIGTYPE &
%typemap(out, phptype="int") const enum SWIGTYPE &
%{
RETVAL_LONG((long)*$1);
%}
%typemap(out) const enum SWIGTYPE &&
%typemap(out, phptype="int") const enum SWIGTYPE &&
%{
RETVAL_LONG((long)*$1);
%}
%typemap(out) const long long &
%typemap(out, phptype="int|string") const long long &
%{
if ((long long)LONG_MIN <= *$1 && *$1 <= (long long)LONG_MAX) {
RETVAL_LONG((long)(*$1));
@ -284,7 +291,7 @@
RETVAL_STRING(temp);
}
%}
%typemap(out) const unsigned long long &
%typemap(out, phptype="int|string") const unsigned long long &
%{
if (*$1 <= (unsigned long long)LONG_MAX) {
RETVAL_LONG((long)(*$1));
@ -323,12 +330,12 @@
}
%}
%typemap(out) bool
%typemap(out, phptype="bool") bool
%{
RETVAL_BOOL(($1) ? 1 : 0);
%}
%typemap(out) const bool &
%typemap(out, phptype="bool") const bool &
%{
RETVAL_BOOL((*$1) ? 1 : 0);
%}
@ -338,13 +345,13 @@
ZVAL_BOOL($input, ($1) ? 1 : 0);
%}
%typemap(out) float,
%typemap(out, phptype="float") float,
double
%{
RETVAL_DOUBLE($1);
%}
%typemap(out) const float &,
%typemap(out, phptype="float") const float &,
const double &
%{
RETVAL_DOUBLE(*$1);
@ -356,18 +363,22 @@
ZVAL_DOUBLE($input, $1);
%}
%typemap(out) char
%typemap(out, phptype="string") char
%{
RETVAL_STRINGL(&$1, 1);
%}
%typemap(out) const char &
%typemap(out, phptype="string") const char &
%{
RETVAL_STRINGL(&*$1, 1);
%}
%typemap(out) char *,
char []
%typemap(out, phptype="string") char []
%{
RETVAL_STRING((const char *)$1);
%}
%typemap(out, phptype="?string") char *
%{
if (!$1) {
RETVAL_NULL();
@ -376,7 +387,7 @@
}
%}
%typemap(out) char *&
%typemap(out, phptype="?string") char *&
%{
if (!*$1) {
RETVAL_NULL();
@ -385,7 +396,12 @@
}
%}
%typemap(out) SWIGTYPE *,
%typemap(out, phptype="?SWIGTYPE") SWIGTYPE *
%{
SWIG_SetPointerZval($result, (void *)$1, $1_descriptor, $owner);
%}
%typemap(out, phptype="SWIGTYPE")
SWIGTYPE [],
SWIGTYPE &,
SWIGTYPE &&
@ -393,7 +409,7 @@
SWIG_SetPointerZval($result, (void *)$1, $1_descriptor, $owner);
%}
%typemap(out) SWIGTYPE *const&
%typemap(out, phptype="?SWIGTYPE") SWIGTYPE *const&
%{
SWIG_SetPointerZval($result, (void *)*$1, $*1_descriptor, $owner);
%}
@ -406,27 +422,32 @@
SWIG_SetPointerZval($input, (void *)&$1, $1_descriptor, $owner);
%}
%typemap(out) SWIGTYPE (CLASS::*)
%typemap(out, phptype="SWIGTYPE") SWIGTYPE (CLASS::*)
{
void * p = emalloc(sizeof($1));
memcpy(p, &$1, sizeof($1));
SWIG_SetPointerZval($result, (void *)p, $&1_descriptor, 1);
}
%typemap(in) SWIGTYPE (CLASS::*)
%typemap(in, phptype="SWIGTYPE") SWIGTYPE (CLASS::*)
{
void * p = SWIG_Z_FETCH_OBJ_P(&$input)->ptr;
memcpy(&$1, p, sizeof($1));
}
%typemap(out) SWIGTYPE *DYNAMIC,
SWIGTYPE &DYNAMIC
%typemap(out, phptype="?SWIGTYPE") SWIGTYPE *DYNAMIC
{
swig_type_info *ty = SWIG_TypeDynamicCast($1_descriptor, (void **) &$1);
SWIG_SetPointerZval($result, (void *)$1, ty, $owner);
}
%typemap(out) SWIGTYPE
%typemap(out, phptype="SWIGTYPE") SWIGTYPE &DYNAMIC
{
swig_type_info *ty = SWIG_TypeDynamicCast($1_descriptor, (void **) &$1);
SWIG_SetPointerZval($result, (void *)$1, ty, $owner);
}
%typemap(out, phptype="SWIGTYPE") SWIGTYPE
{
#ifdef __cplusplus
$&1_ltype resultobj = new $1_ltype((const $1_ltype &) $1);
@ -442,9 +463,9 @@
SWIG_SetPointerZval($input, SWIG_as_voidptr(new $1_ltype((const $1_ltype &)$1)), $&1_descriptor, 1);
%}
%typemap(out) void "";
%typemap(out, phptype="void") void "";
%typemap(out) char [ANY]
%typemap(out, phptype="string") char [ANY]
{
size_t len = 0;
while (len < $1_dim0 && $1[len]) ++len;

View File

@ -1,5 +1,5 @@
%define %pass_by_ref( TYPE, CONVERT_IN, CONVERT_OUT )
%typemap(in, byref=1) TYPE *REF ($*1_ltype tmp),
%define %pass_by_ref( TYPE, PHP_TYPE, CONVERT_IN, CONVERT_OUT )
%typemap(in,byref=1,phptype=PHP_TYPE) TYPE *REF ($*1_ltype tmp),
TYPE &REF ($*1_ltype tmp)
%{
if (Z_ISREF($input)) {
@ -18,25 +18,25 @@
%}
%enddef
%pass_by_ref( size_t, CONVERT_INT_IN, ZVAL_LONG );
%pass_by_ref( size_t, "int", CONVERT_INT_IN, ZVAL_LONG );
%pass_by_ref( signed int, CONVERT_INT_IN, ZVAL_LONG );
%pass_by_ref( int, CONVERT_INT_IN, ZVAL_LONG );
%pass_by_ref( unsigned int, CONVERT_INT_IN, ZVAL_LONG );
%pass_by_ref( signed int, "int", CONVERT_INT_IN, ZVAL_LONG );
%pass_by_ref( int, "int", CONVERT_INT_IN, ZVAL_LONG );
%pass_by_ref( unsigned int, "int", CONVERT_INT_IN, ZVAL_LONG );
%pass_by_ref( signed short, CONVERT_INT_IN, ZVAL_LONG );
%pass_by_ref( short, CONVERT_INT_IN, ZVAL_LONG );
%pass_by_ref( unsigned short, CONVERT_INT_IN, ZVAL_LONG );
%pass_by_ref( signed short, "int", CONVERT_INT_IN, ZVAL_LONG );
%pass_by_ref( short, "int", CONVERT_INT_IN, ZVAL_LONG );
%pass_by_ref( unsigned short, "int", CONVERT_INT_IN, ZVAL_LONG );
%pass_by_ref( signed long, CONVERT_INT_IN, ZVAL_LONG );
%pass_by_ref( long, CONVERT_INT_IN, ZVAL_LONG );
%pass_by_ref( unsigned long, CONVERT_INT_IN, ZVAL_LONG );
%pass_by_ref( signed long, "int", CONVERT_INT_IN, ZVAL_LONG );
%pass_by_ref( long, "int", CONVERT_INT_IN, ZVAL_LONG );
%pass_by_ref( unsigned long, "int", CONVERT_INT_IN, ZVAL_LONG );
%pass_by_ref( signed char, CONVERT_INT_IN, ZVAL_LONG );
%pass_by_ref( char, CONVERT_CHAR_IN, ZVAL_STRING );
%pass_by_ref( unsigned char, CONVERT_INT_IN, ZVAL_LONG );
%pass_by_ref( signed char, "int", CONVERT_INT_IN, ZVAL_LONG );
%pass_by_ref( char, "string", CONVERT_CHAR_IN, ZVAL_STRING );
%pass_by_ref( unsigned char, "int", CONVERT_INT_IN, ZVAL_LONG );
%pass_by_ref( float, CONVERT_FLOAT_IN, ZVAL_DOUBLE );
%pass_by_ref( double, CONVERT_FLOAT_IN, ZVAL_DOUBLE );
%pass_by_ref( float, "float", CONVERT_FLOAT_IN, ZVAL_DOUBLE );
%pass_by_ref( double, "float", CONVERT_FLOAT_IN, ZVAL_DOUBLE );
%pass_by_ref( char *, CONVERT_CHAR_IN, ZVAL_STRING );
%pass_by_ref( char *, "string", CONVERT_CHAR_IN, ZVAL_STRING );

View File

@ -20,6 +20,27 @@ extern "C" {
#include "zend_exceptions.h"
#include "zend_inheritance.h"
#if PHP_MAJOR_VERSION == 7
/* These macros were new in PHP 8.0. For PHP 7.x we define them to give the
* same result except without any type declarations. PHP 7.x supports type
* declarations, but not for the return type, and alternate types aren't
* supported, so we don't try to support these.
*/
# define ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(name, byref, num_req, classes, types) \
ZEND_BEGIN_ARG_INFO_EX(name, 0, byref, num_req)
# define ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(name, byref, num_req, types) \
ZEND_BEGIN_ARG_INFO_EX(name, 0, byref, num_req)
/* NB We can just ignore `default` here we currently always pass NULL for it
* (this mechnism for specifying default parameter values was new in PHP 8.0
* so it's not useful while we still want to support PHP7 too).
*/
# define ZEND_ARG_OBJ_TYPE_MASK(byref, name, classes, types, default) \
ZEND_ARG_INFO(byref, name)
# define ZEND_ARG_TYPE_MASK(byref, name, types, default) \
ZEND_ARG_INFO(byref, name)
#endif
#include <stdlib.h> /* for abort(), used in generated code. */
#define SWIG_BOOL_CONSTANT(N, V) REGISTER_BOOL_CONSTANT(#N, V, CONST_CS | CONST_PERSISTENT)

View File

@ -27,7 +27,7 @@ namespace std {
$1 = (Z_TYPE($input) == IS_STRING) ? 1 : 0;
%}
%typemap(in) string %{
%typemap(in, phptype="string") string %{
convert_to_string(&$input);
$1.assign(Z_STRVAL($input), Z_STRLEN($input));
%}
@ -37,7 +37,7 @@ namespace std {
$result.assign(Z_STRVAL_P($input), Z_STRLEN_P($input));
%}
%typemap(out) string %{
%typemap(out, phptype="string") string %{
ZVAL_STRINGL($result, $1.data(), $1.size());
%}
@ -45,7 +45,7 @@ namespace std {
ZVAL_STRINGL($input, $1.data(), $1.size());
%}
%typemap(out) const string & %{
%typemap(out, phptype="string") const string & %{
ZVAL_STRINGL($result, $1->data(), $1->size());
%}
@ -54,7 +54,7 @@ namespace std {
return;
%}
%typemap(in) const string & ($*1_ltype temp) %{
%typemap(in, phptype="string") const string & ($*1_ltype temp) %{
convert_to_string(&$input);
temp.assign(Z_STRVAL($input), Z_STRLEN($input));
$1 = &temp;
@ -62,7 +62,7 @@ namespace std {
/* These next two handle a function which takes a non-const reference to
* a std::string and modifies the string. */
%typemap(in,byref=1) string & ($*1_ltype temp) %{
%typemap(in,byref=1, phptype="string") string & ($*1_ltype temp) %{
{
zval * p = Z_ISREF($input) ? Z_REFVAL($input) : &$input;
convert_to_string(p);

View File

@ -25,7 +25,7 @@
* ----------------------------------------------------------------------------- */
%define BOOL_TYPEMAP(TYPE)
%typemap(in) TYPE *INPUT(TYPE temp), TYPE &INPUT(TYPE temp)
%typemap(in, phptype="bool") TYPE *INPUT(TYPE temp), TYPE &INPUT(TYPE temp)
%{
convert_to_boolean(&$input);
temp = (Z_TYPE($input) == IS_TRUE);
@ -39,7 +39,7 @@
ZVAL_BOOL(&o, temp$argnum);
t_output_helper($result, &o);
}
%typemap(in) TYPE *REFERENCE (TYPE lvalue), TYPE &REFERENCE (TYPE lvalue)
%typemap(in, phptype="float") TYPE *REFERENCE (TYPE lvalue), TYPE &REFERENCE (TYPE lvalue)
%{
convert_to_boolean($input);
lvalue = (Z_TYPE_P($input) == IS_TRUE);
@ -52,7 +52,7 @@
%enddef
%define DOUBLE_TYPEMAP(TYPE)
%typemap(in) TYPE *INPUT(TYPE temp), TYPE &INPUT(TYPE temp)
%typemap(in, phptype="float") TYPE *INPUT(TYPE temp), TYPE &INPUT(TYPE temp)
%{
temp = (TYPE) zval_get_double(&$input);
$1 = &temp;
@ -65,7 +65,7 @@
ZVAL_DOUBLE(&o, temp$argnum);
t_output_helper($result, &o);
}
%typemap(in) TYPE *REFERENCE (TYPE dvalue), TYPE &REFERENCE (TYPE dvalue)
%typemap(in, phptype="float") TYPE *REFERENCE (TYPE dvalue), TYPE &REFERENCE (TYPE dvalue)
%{
dvalue = (TYPE) zval_get_double(&$input);
$1 = &dvalue;
@ -77,7 +77,7 @@
%enddef
%define INT_TYPEMAP(TYPE)
%typemap(in) TYPE *INPUT(TYPE temp), TYPE &INPUT(TYPE temp)
%typemap(in, phptype="int") TYPE *INPUT(TYPE temp), TYPE &INPUT(TYPE temp)
%{
temp = (TYPE) zval_get_long(&$input);
$1 = &temp;
@ -90,7 +90,7 @@
ZVAL_LONG(&o, temp$argnum);
t_output_helper($result, &o);
}
%typemap(in) TYPE *REFERENCE (TYPE lvalue), TYPE &REFERENCE (TYPE lvalue)
%typemap(in, phptype="int") TYPE *REFERENCE (TYPE lvalue), TYPE &REFERENCE (TYPE lvalue)
%{
lvalue = (TYPE) zval_get_long(&$input);
$1 = &lvalue;
@ -128,7 +128,7 @@ INT_TYPEMAP(long long);
}
t_output_helper($result, &o);
}
%typemap(in) TYPE *REFERENCE (long long lvalue)
%typemap(in, phptype="int|string") TYPE *REFERENCE (long long lvalue)
%{
CONVERT_LONG_LONG_IN(lvalue, long long, $input)
$1 = &lvalue;
@ -167,7 +167,7 @@ INT_TYPEMAP(unsigned long long);
}
t_output_helper($result, &o);
}
%typemap(in) TYPE *REFERENCE (unsigned long long lvalue)
%typemap(in, phptype="int|string") TYPE *REFERENCE (unsigned long long lvalue)
%{
CONVERT_UNSIGNED_LONG_LONG_IN(lvalue, unsigned long long, $input)
$1 = &lvalue;
@ -253,7 +253,7 @@ INT_TYPEMAP(unsigned long long);
%typemap(argout) unsigned long long &INOUT = unsigned long long *OUTPUT;
%typemap(argout) signed char &INOUT = signed char *OUTPUT;
%typemap(in) char INPUT[ANY] ( char temp[$1_dim0] )
%typemap(in, phptype="string") char INPUT[ANY] ( char temp[$1_dim0] )
%{
convert_to_string(&$input);
strncpy(temp, Z_STRVAL($input), $1_dim0);
@ -268,7 +268,7 @@ INT_TYPEMAP(unsigned long long);
t_output_helper($result, &o);
}
%typemap(in,numinputs=0) void **OUTPUT (int force),
%typemap(in,numinputs=0,phptype="?SWIGTYPE") void **OUTPUT (int force),
void *&OUTPUT (int force)
%{
/* If they pass NULL by reference, make it into a void*

View File

@ -63,12 +63,12 @@
}
%enddef
%define %pass_by_val( TYPE, CONVERT_IN )
%typemap(in) TYPE
%define %pass_by_val( TYPE, PHP_TYPE, CONVERT_IN )
%typemap(in, phptype=PHP_TYPE) TYPE
%{
CONVERT_IN($1,$1_ltype,$input);
%}
%typemap(in) const TYPE & ($*1_ltype temp)
%typemap(in, phptype=PHP_TYPE) const TYPE & ($*1_ltype temp)
%{
CONVERT_IN(temp,$*1_ltype,$input);
$1 = &temp;

View File

@ -176,7 +176,95 @@ static void SwigPHP_emit_pointer_type_registrations() {
}
}
// Class encapsulating the machinery to add PHP type declarations.
class PHPTypes {
Hash *phptypes;
// List with an entry for each parameter and one for the return type.
//
// We assemble the types in here before emitting them so for an overloaded
// function we combine the type declarations from each overloaded form.
List *merged_types;
// List with an entry for each parameter which is passed "byref" in any
// overloaded form. We use this to pass such parameters by reference in
// the dispatch function. If NULL, no parameters are passed by reference.
List *byref;
public:
PHPTypes() : phptypes(NewHash()), merged_types(NULL), byref(NULL) {
Setattr(phptypes, "array", "MAY_BE_ARRAY");
Setattr(phptypes, "bool", "MAY_BE_BOOL");
Setattr(phptypes, "callable", "MAY_BE_CALLABLE");
Setattr(phptypes, "float", "MAY_BE_DOUBLE");
Setattr(phptypes, "int", "MAY_BE_LONG");
Setattr(phptypes, "iterable", "MAY_BE_ITERABLE");
Setattr(phptypes, "mixed", "MAY_BE_MIXED");
Setattr(phptypes, "null", "MAY_BE_NULL");
Setattr(phptypes, "object", "MAY_BE_OBJECT");
Setattr(phptypes, "resource", "MAY_BE_RESOURCE");
Setattr(phptypes, "string", "MAY_BE_STRING");
Setattr(phptypes, "void", "MAY_BE_VOID");
}
void reset() {
Delete(merged_types);
merged_types = NewList();
Delete(byref);
byref = NULL;
}
// key is 0 for return type, or >= 1 for parameters numbered from 1
void process_phptype(Node *n, int key, const String_or_char *attribute_name);
String *get_phptype(int key, String *classtypes) {
Clear(classtypes);
DOH *types = Getitem(merged_types, key);
String *result = NewStringEmpty();
if (types != None) {
SortList(types, NULL);
String *prev = NULL;
for (Iterator i = First(types); i.item; i = Next(i)) {
if (prev && Equal(prev, i.item)) {
// Skip duplicates when merging.
continue;
}
String *c = Getattr(phptypes, i.item);
if (c) {
if (Len(result) > 0) Append(result, "|");
Append(result, c);
} else {
if (Len(classtypes) > 0) Append(classtypes, "|");
Append(classtypes, i.item);
}
prev = i.item;
}
}
// Make the mask 0 if there are only class names specified.
if (Len(result) == 0) {
Append(result, "0");
}
return result;
}
void set_byref(int key) {
if (!byref) {
byref = NewList();
}
while (Len(byref) <= key) {
Append(byref, None);
}
Setitem(byref, key, ""); // Just needs to be something != None.
}
int get_byref(int key) const {
return byref && key < Len(byref) && Getitem(byref, key) != None;
}
};
class PHP : public Language {
PHPTypes phptypes;
public:
PHP() {
director_language = 1;
@ -572,124 +660,165 @@ public:
}
/* Just need to append function names to function table to register with PHP. */
void create_command(String *cname, String *fname, Node *n, bool overload, String *modes = NULL) {
void create_command(String *cname, String *fname, Node *n, bool dispatch, String *modes = NULL) {
// This is for the single main zend_function_entry record
bool has_this = false;
ParmList *l = Getattr(n, "parms");
if (cname && Cmp(Getattr(n, "storage"), "friend") != 0) {
Printf(f_h, "PHP_METHOD(%s%s,%s);\n", prefix, cname, fname);
has_this = (wrapperType != staticmemberfn) &&
(wrapperType != staticmembervar) &&
(Cmp(fname, "__construct") != 0);
if (wrapperType != staticmemberfn &&
wrapperType != staticmembervar &&
!Equal(fname, "__construct")) {
// Skip the first entry in the parameter list which is the this pointer.
l = Getattr(l, "tmap:in:next");
// FIXME: does this throw the phptype key value off?
}
} else {
if (overload) {
if (dispatch) {
Printf(f_h, "ZEND_NAMED_FUNCTION(%s);\n", fname);
} else {
Printf(f_h, "PHP_FUNCTION(%s);\n", fname);
}
}
int num_required = emit_num_required(l);
// We want to only emit each different arginfo once, as that reduces the
// size of both the generated source code and the compiled extension
// module. The parameters at this level are just named arg1, arg2, etc
// so we generate an arginfo name with the number of parameters and a
// bitmap value saying which (if any) are passed by reference.
ParmList *l = Getattr(n, "parms");
unsigned long bitmap = 0, bit = 1;
bool overflowed = false;
bool skip_this = has_this;
for (Parm *p = l; p; p = Getattr(p, "tmap:in:next")) {
if (skip_this) {
skip_this = false;
continue;
// so the arginfo will be the same for any function with the same number
// of parameters and (if present) PHP type declarations for parameters and
// return type.
//
// We generate the arginfo we want (taking care to normalise, e.g. the
// lists of types are unique and in sorted order), then use the
// arginfo_used Hash to see if we've already generated it.
// Don't add a return type declaration for a constructor (because there
// is no return type as far as PHP is concerned).
String *out_phptype = NULL;
String *out_phpclasses = NewStringEmpty();
if (!Equal(fname, "__construct")) {
String *php_type_flag = GetFlagAttr(n, "feature:php:type");
if (Equal(php_type_flag, "1") ||
(php_type_flag && !Getattr(n, "directorNode"))) {
// We provide a simple way to generate PHP return type declarations
// except for directed methods. The point of directors is to allow
// subclassing in the target language, and if the wrapped method has
// a return type declaration then an overriding method in user code
// needs to have a compatible declaration.
//
// The upshot of this is that enabling return type declarations for
// existing bindings would break compatibility with user code written
// for an older version. For parameters however the situation is
// different because if the parent class declares types for parameters
// a subclass overriding the function will be compatible whether it
// declares them or not.
//
// directorNode being present seems to indicate if this method or one
// it inherits from is directed, which is what we care about here.
// Using (!is_member_director(n)) would get it wrong for testcase
// director_frob.
out_phptype = phptypes.get_phptype(0, out_phpclasses);
}
}
// ### in arginfo_code will be replaced with the id once that is known.
String *arginfo_code = NewStringEmpty();
if (out_phptype) {
if (Len(out_phpclasses)) {
Replace(out_phpclasses, "\\", "\\\\", DOH_REPLACE_ANY);
Printf(arginfo_code, "ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(swig_arginfo_###, 0, %d, %s, %s)\n", num_required, out_phpclasses, out_phptype);
} else {
Printf(arginfo_code, "ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(swig_arginfo_###, 0, %d, %s)\n", num_required, out_phptype);
}
} else {
Printf(arginfo_code, "ZEND_BEGIN_ARG_INFO_EX(swig_arginfo_###, 0, 0, %d)\n", num_required);
}
int param_count = 0;
for (Parm *p = l; p; p = Getattr(p, "tmap:in:next")) {
String *tmap_in_numinputs = Getattr(p, "tmap:in:numinputs");
// tmap:in:numinputs is unset for varargs, which we don't count here.
if (!tmap_in_numinputs || Equal(tmap_in_numinputs, "0")) {
/* Ignored parameter */
continue;
}
if (GetFlag(p, "tmap:in:byref")) {
bitmap |= bit;
if (bit == 0) overflowed = true;
}
bit <<= 1;
}
int num_arguments = emit_num_arguments(l);
int num_required = emit_num_required(l);
if (has_this) {
--num_arguments;
--num_required;
}
String *arginfo_code;
if (overflowed) {
// We overflowed the bitmap so just generate a unique name - this only
// happens for a function with more parameters than bits in a long
// where a high numbered parameter is passed by reference, so should be
// rare in practice.
static int overflowed_counter = 0;
arginfo_code = NewStringf("z%d", ++overflowed_counter);
} else if (bitmap == 0) {
// No parameters passed by reference.
if (num_required == num_arguments) {
arginfo_code = NewStringf("%d", num_arguments);
} else {
arginfo_code = NewStringf("%d_%d", num_required, num_arguments);
}
} else {
if (num_required == num_arguments) {
arginfo_code = NewStringf("%d_r%lx", num_arguments, bitmap);
} else {
arginfo_code = NewStringf("%d_%d_r%lx", num_required, num_arguments, bitmap);
}
}
if (!GetFlag(arginfo_used, arginfo_code)) {
// Not had this one before so emit it.
SetFlag(arginfo_used, arginfo_code);
Printf(s_arginfo, "ZEND_BEGIN_ARG_INFO_EX(swig_arginfo_%s, 0, 0, %d)\n", arginfo_code, num_required);
bool skip_this = has_this;
int param_count = 0;
for (Parm *p = l; p; p = Getattr(p, "tmap:in:next")) {
if (skip_this) {
skip_this = false;
continue;
}
String *tmap_in_numinputs = Getattr(p, "tmap:in:numinputs");
// tmap:in:numinputs is unset for varargs, which we don't count here.
if (!tmap_in_numinputs || Equal(tmap_in_numinputs, "0")) {
/* Ignored parameter */
continue;
}
Printf(s_arginfo, " ZEND_ARG_INFO(%d,arg%d)\n", GetFlag(p, "tmap:in:byref"), ++param_count);
++param_count;
String *phpclasses = NewStringEmpty();
String *phptype = NULL;
if (GetFlag(n, "feature:php:type")) {
phptypes.get_phptype(param_count, phpclasses);
}
int byref;
if (!dispatch) {
byref = GetFlag(p, "tmap:in:byref");
if (byref) phptypes.set_byref(param_count);
} else {
// If any overload takes a particular parameter by reference then the
// dispatch function also needs to take that parameter by reference.
byref = phptypes.get_byref(param_count);
}
// FIXME: Should we be doing byref for return value as well?
if (phptype) {
if (Len(phpclasses)) {
// We need to double any backslashes (which are PHP namespace
// separators) in the PHP class names as they get turned into
// C strings by the ZEND_ARG_OBJ_TYPE_MASK macro.
Replace(phpclasses, "\\", "\\\\", DOH_REPLACE_ANY);
Printf(arginfo_code, " ZEND_ARG_OBJ_TYPE_MASK(%d,arg%d,%s,%s,NULL)\n", byref, param_count, phpclasses, phptype);
} else {
Printf(arginfo_code, " ZEND_ARG_TYPE_MASK(%d,arg%d,%s,NULL)\n", byref, param_count, phptype);
}
} else {
Printf(arginfo_code, " ZEND_ARG_INFO(%d,arg%d)\n", byref, param_count);
}
Printf(s_arginfo, "ZEND_END_ARG_INFO()\n");
}
Printf(arginfo_code, "ZEND_END_ARG_INFO()\n");
String *arginfo_id_new = Getattr(n, "sym:name");
String *arginfo_id = Getattr(arginfo_used, arginfo_code);
if (arginfo_id) {
Printf(s_arginfo, "#define swig_arginfo_%s swig_arginfo_%s\n", arginfo_id_new, arginfo_id);
} else {
// Not had this arginfo before.
Setattr(arginfo_used, arginfo_code, arginfo_id_new);
arginfo_code = Copy(arginfo_code);
Replace(arginfo_code, "###", arginfo_id_new, DOH_REPLACE_FIRST);
Append(s_arginfo, arginfo_code);
}
Delete(arginfo_code);
arginfo_code = NULL;
String *s = cs_entry;
if (!s) s = s_entry;
if (cname && Cmp(Getattr(n, "storage"), "friend") != 0) {
Printf(all_cs_entry, " PHP_ME(%s%s,%s,swig_arginfo_%s,%s)\n", prefix, cname, fname, arginfo_code, modes);
Printf(all_cs_entry, " PHP_ME(%s%s,%s,swig_arginfo_%s,%s)\n", prefix, cname, fname, arginfo_id_new, modes);
} else {
if (overload) {
if (dispatch) {
if (wrap_nonclass_global) {
Printf(s, " ZEND_NAMED_FE(%(lower)s,%s,swig_arginfo_%s)\n", Getattr(n, "sym:name"), fname, arginfo_code);
Printf(s, " ZEND_NAMED_FE(%(lower)s,%s,swig_arginfo_%s)\n", Getattr(n, "sym:name"), fname, arginfo_id_new);
}
if (wrap_nonclass_fake_class) {
(void)fake_class_name();
Printf(fake_cs_entry, " ZEND_NAMED_ME(%(lower)s,%s,swig_arginfo_%s,ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)\n", Getattr(n, "sym:name"), fname, arginfo_code);
Printf(fake_cs_entry, " ZEND_NAMED_ME(%(lower)s,%s,swig_arginfo_%s,ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)\n", Getattr(n, "sym:name"), fname, arginfo_id_new);
}
} else {
if (wrap_nonclass_global) {
Printf(s, " PHP_FE(%s,swig_arginfo_%s)\n", fname, arginfo_code);
Printf(s, " PHP_FE(%s,swig_arginfo_%s)\n", fname, arginfo_id_new);
}
if (wrap_nonclass_fake_class) {
String *fake_class = fake_class_name();
Printf(fake_cs_entry, " PHP_ME(%s,%s,swig_arginfo_%s,ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)\n", fake_class, fname, arginfo_code);
Printf(fake_cs_entry, " PHP_ME(%s,%s,swig_arginfo_%s,ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)\n", fake_class, fname, arginfo_id_new);
}
}
}
Delete(arginfo_code);
}
/* ------------------------------------------------------------
@ -784,27 +913,29 @@ public:
base_class = NULL;
}
// Ensure arginfo_1 and arginfo_2 exist.
if (!GetFlag(arginfo_used, "1")) {
SetFlag(arginfo_used, "1");
static bool generated_magic_arginfo = false;
if (!generated_magic_arginfo) {
// Create arginfo entries for __get, __set and __isset.
Append(s_arginfo,
"ZEND_BEGIN_ARG_INFO_EX(swig_arginfo_1, 0, 0, 1)\n"
" ZEND_ARG_INFO(0,arg1)\n"
"ZEND_BEGIN_ARG_INFO_EX(swig_magic_arginfo_get, 0, 0, 1)\n"
" ZEND_ARG_TYPE_MASK(0,arg1,MAY_BE_STRING,NULL)\n"
"ZEND_END_ARG_INFO()\n");
}
if (!GetFlag(arginfo_used, "2")) {
SetFlag(arginfo_used, "2");
Append(s_arginfo,
"ZEND_BEGIN_ARG_INFO_EX(swig_arginfo_2, 0, 0, 2)\n"
" ZEND_ARG_INFO(0,arg1)\n"
"ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(swig_magic_arginfo_set, 0, 1, MAY_BE_VOID)\n"
" ZEND_ARG_TYPE_MASK(0,arg1,MAY_BE_STRING,NULL)\n"
" ZEND_ARG_INFO(0,arg2)\n"
"ZEND_END_ARG_INFO()\n");
Append(s_arginfo,
"ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(swig_magic_arginfo_isset, 0, 1, MAY_BE_BOOL)\n"
" ZEND_ARG_TYPE_MASK(0,arg1,MAY_BE_STRING,NULL)\n"
"ZEND_END_ARG_INFO()\n");
generated_magic_arginfo = true;
}
Wrapper *f = NewWrapper();
Printf(f_h, "PHP_METHOD(%s%s,__set);\n", prefix, class_name);
Printf(all_cs_entry, " PHP_ME(%s%s,__set,swig_arginfo_2,ZEND_ACC_PUBLIC)\n", prefix, class_name);
Printf(all_cs_entry, " PHP_ME(%s%s,__set,swig_magic_arginfo_set,ZEND_ACC_PUBLIC)\n", prefix, class_name);
Printf(f->code, "PHP_METHOD(%s%s,__set) {\n", prefix, class_name);
Printf(f->code, " swig_object_wrapper *arg = SWIG_Z_FETCH_OBJ_P(ZEND_THIS);\n");
@ -842,7 +973,7 @@ public:
Printf(f_h, "PHP_METHOD(%s%s,__get);\n", prefix, class_name);
Printf(all_cs_entry, " PHP_ME(%s%s,__get,swig_arginfo_1,ZEND_ACC_PUBLIC)\n", prefix, class_name);
Printf(all_cs_entry, " PHP_ME(%s%s,__get,swig_magic_arginfo_get,ZEND_ACC_PUBLIC)\n", prefix, class_name);
Printf(f->code, "PHP_METHOD(%s%s,__get) {\n",prefix, class_name);
Printf(f->code, " swig_object_wrapper *arg = SWIG_Z_FETCH_OBJ_P(ZEND_THIS);\n");
@ -875,7 +1006,7 @@ public:
Printf(f_h, "PHP_METHOD(%s%s,__isset);\n", prefix, class_name);
Printf(all_cs_entry, " PHP_ME(%s%s,__isset,swig_arginfo_1,ZEND_ACC_PUBLIC)\n", prefix, class_name);
Printf(all_cs_entry, " PHP_ME(%s%s,__isset,swig_magic_arginfo_isset,ZEND_ACC_PUBLIC)\n", prefix, class_name);
Printf(f->code, "PHP_METHOD(%s%s,__isset) {\n",prefix, class_name);
Printf(f->code, " swig_object_wrapper *arg = SWIG_Z_FETCH_OBJ_P(ZEND_THIS);\n");
@ -994,6 +1125,12 @@ public:
return SWIG_ERROR;
}
if (!Getattr(n, "sym:previousSibling")) {
// First function of an overloaded group or a function which isn't part
// of a group so reset the phptype information.
phptypes.reset();
}
if (constructor) {
wname = NewString("__construct");
} else if (wrapperType == membervar) {
@ -1077,10 +1214,6 @@ public:
/* Attach standard typemaps */
emit_attach_parmmaps(l, f);
// Not issued for overloaded functions.
if (!overloaded && !static_getter) {
create_command(class_name, wname, n, false, modes);
}
if (wrapperType == memberfn || wrapperType == membervar) {
// Assign "this" to arg1 and remove first entry from ParmList l.
@ -1164,6 +1297,8 @@ public:
continue;
}
phptypes.process_phptype(p, i + 1, "tmap:in:phptype");
String *source = NewStringf("args[%d]", i);
Replaceall(tm, "$input", source);
Setattr(p, "emit:input", source);
@ -1247,6 +1382,8 @@ public:
}
emit_return_variable(n, d, f);
phptypes.process_phptype(n, 0, "tmap:out:phptype");
if (outarg) {
Printv(f->code, outarg, NIL);
}
@ -1290,10 +1427,15 @@ public:
Wrapper_print(f, s_wrappers);
DelWrapper(f);
f = NULL;
wname = NULL;
if (overloaded && !Getattr(n, "sym:nextSibling")) {
dispatchFunction(n, constructor);
if (overloaded) {
if (!Getattr(n, "sym:nextSibling")) {
dispatchFunction(n, constructor);
}
} else {
if (!static_getter) {
create_command(class_name, wname, n, false, modes);
}
}
return SWIG_OK;
@ -2154,6 +2296,82 @@ public:
static PHP *maininstance = 0;
void PHPTypes::process_phptype(Node *n, int key, const String_or_char *attribute_name) {
while (Len(merged_types) <= key) {
Append(merged_types, NewList());
}
String *phptype = Getattr(n, attribute_name);
if (!phptype || Len(phptype) == 0) {
// There's no type declaration, so any merged version has no type declaration.
//
// Use a DOH None object as a marker to indicate there's no type
// declaration for this parameter/return value (you can't store NULL as a
// value in a DOH List).
Setitem(merged_types, key, None);
return;
}
DOH *merge_list = Getitem(merged_types, key);
if (merge_list == None) return;
List *types = Split(phptype, '|', -1);
String *first_type = Getitem(types, 0);
if (Char(first_type)[0] == '?') {
if (Len(types) > 1) {
Printf(stderr, "warning: Invalid phptype: '%s' (can't use ? and | together)\n", phptype);
}
// Treat `?foo` just like `foo|null`.
Append(types, "null");
Setitem(types, 0, NewString(Char(first_type) + 1));
}
SortList(types, NULL);
String *prev = NULL;
for (Iterator i = First(types); i.item; i = Next(i)) {
if (prev && Equal(prev, i.item)) {
Printf(stderr, "warning: Invalid phptype: '%s' (duplicate entry for '%s')\n", phptype, i.item);
continue;
}
if (key > 0 && Equal(i.item, "void")) {
// Reject void for parameter type.
Printf(stderr, "warning: Invalid phptype: '%s' ('%s' can't be used as a parameter phptype)\n", phptype, i.item);
continue;
}
if (Equal(i.item, "SWIGTYPE")) {
String *type = Getattr(n, "type");
Node *class_node = maininstance->classLookup(type);
if (class_node) {
// FIXME: Prefix classname with a backslash to prevent collisions
// with built-in types? Or are non of those valid anyway and so will
// have been renamed at this point?
Append(merge_list, Getattr(class_node, "sym:name"));
} else {
// SWIG wraps a pointer to a non-object type as an object in a PHP
// class named based on the SWIG-mangled C/C++ type.
//
// FIXME: We should check this is actually a known pointer to
// non-object type so we complain about `phptype="SWIGTYPE"` being
// used for PHP types like `int` or `string` (currently this only
// fails at runtime and the error isn't very helpful). We could
// check the condition
//
// zend_types && Getattr(zend_types, SwigType_manglestr(type))
//
// except that zend_types may not have been fully filled in when
// we are called.
Append(merge_list, NewStringf("SWIG\\%s", SwigType_manglestr(type)));
}
} else {
Append(merge_list, i.item);
}
prev = i.item;
}
}
// Collect non-class pointer types from the type table so we can set up PHP
// classes for them later.
//