diff options
53 files changed, 2838 insertions, 66 deletions
diff --git a/CHANGES.current b/CHANGES.current index 89057e97d..ece581863 100644 --- a/CHANGES.current +++ b/CHANGES.current @@ -1,6 +1,9 @@ Version 1.3.40 (in progress) ============================ +2009-07-21: vmiklos + [PHP] Director support added. + 2009-07-15: olly [Perl] Don't specify Perl prototype "()" for a constructor with a different name to the class, as such constructors can still take @@ -17,6 +20,10 @@ Version 1.3.40 (in progress) of the manual. Based on patch from SF#2810380 by Christian Gollwitzer. +2009-07-02: vmiklos + [PHP] Added factory.i for PHP, see the li_factory testcase + for more info on how to use it. + 2009-07-02: wsfulton Fix -Wallkw option as reported by Solomon Gibbs. @@ -24,6 +31,14 @@ Version 1.3.40 (in progress) Fix syntax error when a nested struct contains a comment containing a * followed eventually by a /. Regression from 1.3.37, reported by Solomon Gibbs. +2009-07-01: vmiklos + [PHP] Unknown properties are no longer ignored in proxy + classes. + +2009-07-01: vmiklos + [PHP] Fixed %newobject behaviour, previously any method + marked with %newobject was handled as a constructor. + 2009-06-30: olly [Ruby] Undefine close and connect macros defined by Ruby API headers as we don't need them and they can clash with C++ methods @@ -66,6 +81,12 @@ Version 1.3.40 (in progress) *** POTENTIAL INCOMPATIBILITY *** +2009-05-20: vmiklos + [PHP] Add the 'thisown' member to classes. The usage of it + is the same as the Python thisown one: it's 1 by default and + you can set it to 0 if you want to prevent freeing it. (For + example to prevent a double free.) + 2009-05-14: bhy [Python] Fix the wrong pointer value returned by SwigPyObject_repr(). diff --git a/Doc/Manual/Php.html b/Doc/Manual/Php.html index ab2c73f6c..7bf158918 100644 --- a/Doc/Manual/Php.html +++ b/Doc/Manual/Php.html @@ -32,6 +32,16 @@ </ul> <li><a href="#Php_nn2_7">PHP Pragmas, Startup and Shutdown code</a> </ul> +<li><a href="#Php_nn3">Cross language polymorphism</a> +<ul> +<li><a href="#Php_nn3_1">Enabling directors</a> +<li><a href="#Php_nn3_2">Director classes</a> +<li><a href="#Php_nn3_3">Ownership and object destruction</a> +<li><a href="#Php_nn3_4">Exception unrolling</a> +<li><a href="#Php_nn3_5">Overhead and code bloat</a> +<li><a href="#Php_nn3_6">Typemaps</a> +<li><a href="#Php_nn3_7">Miscellaneous</a> +</ul> </ul> </div> <!-- INDEX --> @@ -866,5 +876,381 @@ The <tt>%rinit</tt> and <tt>%rshutdown</tt> statements insert code into the request init and shutdown code respectively. </p> +<H2><a name="Php_nn3"></a>29.3 Cross language polymorphism</H2> + + +<p> +Proxy classes provide a more natural, object-oriented way to access +extension classes. As described above, each proxy instance has an +associated C++ instance, and method calls to the proxy are passed to the +C++ instance transparently via C wrapper functions. +</p> + +<p> +This arrangement is asymmetric in the sense that no corresponding +mechanism exists to pass method calls down the inheritance chain from +C++ to PHP. In particular, if a C++ class has been extended in PHP +(by extending the proxy class), these extensions will not be visible +from C++ code. Virtual method calls from C++ are thus not able access +the lowest implementation in the inheritance chain. +</p> + +<p> +Changes have been made to SWIG 1.3.18 to address this problem and make +the relationship between C++ classes and proxy classes more symmetric. +To achieve this goal, new classes called directors are introduced at the +bottom of the C++ inheritance chain. Support for generating PHP classes +has been added in SWIG 1.3.40. The job of the directors is to route +method calls correctly, either to C++ implementations higher in the +inheritance chain or to PHP implementations lower in the inheritance +chain. The upshot is that C++ classes can be extended in PHP and from +C++ these extensions look exactly like native C++ classes. Neither C++ +code nor PHP code needs to know where a particular method is +implemented: the combination of proxy classes, director classes, and C +wrapper functions takes care of all the cross-language method routing +transparently. +</p> + +<H3><a name="Php_nn3_1"></a>29.3.1 Enabling directors</H3> + + +<p> +The director feature is disabled by default. To use directors you +must make two changes to the interface file. First, add the "directors" +option to the %module directive, like this: +</p> + +<div class="code"> +<pre> +%module(directors="1") modulename +</pre> +</div> + +<p> +Without this option no director code will be generated. Second, you +must use the %feature("director") directive to tell SWIG which classes +and methods should get directors. The %feature directive can be applied +globally, to specific classes, and to specific methods, like this: +</p> + +<div class="code"> +<pre> +// generate directors for all classes that have virtual methods +%feature("director"); + +// generate directors for all virtual methods in class Foo +%feature("director") Foo; + +// generate a director for just Foo::bar() +%feature("director") Foo::bar; +</pre> +</div> + +<p> +You can use the %feature("nodirector") directive to turn off +directors for specific classes or methods. So for example, +</p> + +<div class="code"> +<pre> +%feature("director") Foo; +%feature("nodirector") Foo::bar; +</pre> +</div> + +<p> +will generate directors for all virtual methods of class Foo except +bar(). +</p> + +<p> +Directors can also be generated implicitly through inheritance. +In the following, class Bar will get a director class that handles +the methods one() and two() (but not three()): +</p> + +<div class="code"> +<pre> +%feature("director") Foo; +class Foo { +public: + Foo(int foo); + virtual void one(); + virtual void two(); +}; + +class Bar: public Foo { +public: + virtual void three(); +}; +</pre> +</div> + +<p> +then at the PHP side you can define +</p> + +<div class="targetlang"> +<pre> +require("mymodule.php"); + +class MyFoo extends Foo { + function one() { + print "one from php\n"; + } +} +</pre> +</div> + + +<H3><a name="Php_nn3_2"></a>29.3.2 Director classes</H3> + + + + + +<p> +For each class that has directors enabled, SWIG generates a new class +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. +</p> + +<p> +For simplicity let's ignore the <tt>Swig::Director</tt> class and refer to the +original C++ class as the director's base class. By default, a director +class extends all virtual methods in the inheritance chain of its base +class (see the preceding section for how to modify this behavior). +Thus all virtual method calls, whether they originate in C++ or in +PHP via proxy classes, eventually end up in at the implementation in the +director class. The job of the director methods is to route these method +calls to the appropriate place in the inheritance chain. By "appropriate +place" we mean the method that would have been called if the C++ base +class and its extensions in PHP were seamlessly integrated. That +seamless integration is exactly what the director classes provide, +transparently skipping over all the messy extension API glue that binds +the two languages together. +</p> + +<p> +In reality, the "appropriate place" is one of only two possibilities: +C++ or PHP. Once this decision is made, the rest is fairly easy. If the +correct implementation is in C++, then the lowest implementation of the +method in the C++ inheritance chain is called explicitly. If the correct +implementation is in PHP, the Zend API is used to call the method of the +underlying PHP object (after which the usual virtual method resolution +in PHP automatically finds the right implementation). +</p> + +<p> +Now how does the director decide which language should handle the method call? +The basic rule is to handle the method in PHP, unless there's a good +reason not to. The reason for this is simple: PHP has the most +"extended" implementation of the method. This assertion is guaranteed, +since at a minimum the PHP proxy class implements the method. If the +method in question has been extended by a class derived from the proxy +class, that extended implementation will execute exactly as it should. +If not, the proxy class will route the method call into a C wrapper +function, expecting that the method will be resolved in C++. The wrapper +will call the virtual method of the C++ instance, and since the director +extends this the call will end up right back in the director method. Now +comes the "good reason not to" part. If the director method were to blindly +call the PHP method again, it would get stuck in an infinite loop. We avoid this +situation by adding special code to the C wrapper function that tells +the director method to not do this. The C wrapper function compares the +called and the declaring class name of the given method. If these are +not the same, then the C wrapper function tells the director to resolve +the method by calling up the C++ inheritance chain, preventing an +infinite loop. +</p> + +<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>->_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 +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++ +so there is no need for the extra overhead involved with routing the +calls through PHP. +</p> + +<H3><a name="Php_nn3_3"></a>29.3.3 Ownership and object destruction</H3> + + +<p> +Memory management issues are slightly more complicated with directors +than for proxy classes alone. PHP instances hold a pointer to the +associated C++ director object, and the director in turn holds a pointer +back to the PHP object. By default, proxy classes own their C++ director +object and take care of deleting it when they are garbage collected. +</p> + +<p> +This relationship can be reversed by calling the special +<tt>->thisown</tt> property of the proxy class. After setting this +property to <tt>0</tt>, the director class no longer destroys the PHP +object. Assuming no outstanding references to the PHP object remain, +the PHP object will be destroyed at the same time. This is a good thing, +since directors and proxies refer to each other and so must be created +and destroyed together. Destroying one without destroying the other will +likely cause your program to segfault. +</p> + +<p> +Here is an example: +</p> + +<div class="code"> +<pre> +class Foo { +public: + ... +}; +class FooContainer { +public: + void addFoo(Foo *); + ... +}; +</pre> +</div> + +<br> + +<div class="targetlang"> +<pre> +$c = new FooContainer(); +$a = new Foo(); +$a->thisown = 0; +$c->addFoo($a); +</pre> +</div> + +<p> +In this example, we are assuming that FooContainer will take care of +deleting all the Foo pointers it contains at some point. +</p> + +<H3><a name="Php_nn3_4"></a>29.3.4 Exception unrolling</H3> + + +<p> +With directors routing method calls to PHP, and proxies routing them +to C++, the handling of exceptions is an important concern. By default, the +directors ignore exceptions that occur during method calls that are +resolved in PHP. To handle such exceptions correctly, it is necessary +to temporarily translate them into C++ exceptions. This can be done with +the %feature("director:except") directive. The following code should +suffice in most cases: +</p> + +<div class="code"> +<pre> +%feature("director:except") { + if ($error == FAILURE) { + throw Swig::DirectorMethodException(); + } +} +</pre> +</div> + +<p> +This code will check the PHP error state after each method call from a +director into PHP, and throw a C++ exception if an error occurred. This +exception can be caught in C++ to implement an error handler. +Currently no information about the PHP error is stored in the +Swig::DirectorMethodException object, but this will likely change in the +future. +</p> + +<p> +It may be the case that a method call originates in PHP, travels up to +C++ through a proxy class, and then back into PHP via a director method. +If an exception occurs in PHP at this point, it would be nice for that +exception to find its way back to the original caller. This can be done +by combining a normal %exception directive with the +<tt>director:except</tt> handler shown above. Here is an example of a +suitable exception handler: +</p> + +<div class="code"> +<pre> +%exception { + try { $action } + catch (Swig::DirectorException &e) { SWIG_fail; } +} +</pre> +</div> + +<p> +The class Swig::DirectorException used in this example is actually a +base class of Swig::DirectorMethodException, so it will trap this +exception. Because the PHP error state is still set when +Swig::DirectorMethodException is thrown, PHP will register the exception +as soon as the C wrapper function returns. +</p> + +<H3><a name="Php_nn3_5"></a>29.3.5 Overhead and code bloat</H3> + + +<p> +Enabling directors for a class will generate a new director method for +every virtual method in the class' inheritance chain. This alone can +generate a lot of code bloat for large hierarchies. Method arguments +that require complex conversions to and from target language types can +result in large director methods. For this reason it is recommended that +you selectively enable directors only for specific classes that are +likely to be extended in PHP and used in C++. +</p> + +<p> +Compared to classes that do not use directors, the call routing in the +director methods does add some overhead. In particular, at least one +dynamic cast and one extra function call occurs per method call from +PHP. Relative to the speed of PHP execution this is probably completely +negligible. For worst case routing, a method call that ultimately +resolves in C++ may take one extra detour through PHP in order to ensure +that the method does not have an extended PHP implementation. This could +result in a noticeable overhead in some cases. +</p> + +<p> +Although directors make it natural to mix native C++ objects with PHP +objects (as director objects) via a common base class pointer, one +should be aware of the obvious fact that method calls to PHP objects +will be much slower than calls to C++ objects. This situation can be +optimized by selectively enabling director methods (using the %feature +directive) for only those methods that are likely to be extended in PHP. +</p> + +<H3><a name="Php_nn3_6"></a>29.3.6 Typemaps</H3> + + +<p> +Typemaps for input and output of most of the basic types from director +classes have been written. These are roughly the reverse of the usual +input and output typemaps used by the wrapper code. The typemap +operation names are 'directorin', 'directorout', and 'directorargout'. +The director code does not currently use any of the other kinds of +typemaps. It is not clear at this point which kinds are appropriate and +need to be supported. +</p> + + +<H3><a name="Php_nn3_7"></a>29.3.7 Miscellaneous</H3> + + +<p> Director typemaps for STL classes are mostly in place, and hence you +should be able to use std::string, etc., as you would any other type. +</p> + </body> </html> diff --git a/Examples/php/callback/Makefile b/Examples/php/callback/Makefile new file mode 100644 index 000000000..42597202b --- /dev/null +++ b/Examples/php/callback/Makefile @@ -0,0 +1,22 @@ +TOP = ../.. +SWIG = $(TOP)/../preinst-swig +CXXSRCS = example.cxx +TARGET = example +INTERFACE = example.i +LIBS = -lm +SWIGOPT = + +all:: + $(MAKE) -f $(TOP)/Makefile $(SWIGLIB) CXXSRCS='$(CXXSRCS)' SWIG='$(SWIG)' \ + SWIGOPT='$(SWIGOPT)' TARGET='$(TARGET)' INTERFACE='$(INTERFACE)' php_cpp + +static:: + $(MAKE) -f $(TOP)/Makefile $(SWIGLIB) CXXSRCS='$(CXXSRCS)' SWIG='$(SWIG)' \ + SWIGOPT='$(SWIGOPT)' TARGET='myphp' INTERFACE='$(INTERFACE)' php_cpp_static + +clean:: + $(MAKE) -f $(TOP)/Makefile php_clean + rm -f $(TARGET).php + +check: all + $(MAKE) -f $(TOP)/Makefile php_run diff --git a/Examples/php/callback/example.cxx b/Examples/php/callback/example.cxx new file mode 100644 index 000000000..450d75608 --- /dev/null +++ b/Examples/php/callback/example.cxx @@ -0,0 +1,4 @@ +/* File : example.cxx */ + +#include "example.h" + diff --git a/Examples/php/callback/example.h b/Examples/php/callback/example.h new file mode 100644 index 000000000..2a0194999 --- /dev/null +++ b/Examples/php/callback/example.h @@ -0,0 +1,22 @@ +/* File : example.h */ + +#include <iostream> + +class Callback { +public: + virtual ~Callback() { std::cout << "Callback::~Callback()" << std:: endl; } + virtual void run() { std::cout << "Callback::run()" << std::endl; } +}; + + +class Caller { +private: + Callback *_callback; +public: + Caller(): _callback(0) {} + ~Caller() { delCallback(); } + void delCallback() { delete _callback; _callback = 0; } + void setCallback(Callback *cb) { delCallback(); _callback = cb; } + void call() { if (_callback) _callback->run(); } +}; + diff --git a/Examples/php/callback/example.i b/Examples/php/callback/example.i new file mode 100644 index 000000000..90beda01a --- /dev/null +++ b/Examples/php/callback/example.i @@ -0,0 +1,13 @@ +/* File : example.i */ +%module(directors="1") example +%{ +#include "example.h" +%} + +%include "std_string.i" + +/* turn on director wrapping Callback */ +%feature("director") Callback; + +%include "example.h" + diff --git a/Examples/php/callback/index.html b/Examples/php/callback/index.html new file mode 100644 index 000000000..2aa720e24 --- /dev/null +++ b/Examples/php/callback/index.html @@ -0,0 +1,19 @@ +<html> +<head> +<title>SWIG:Examples:php:callback</title> +</head> + +<body bgcolor="#ffffff"> + + +<tt>SWIG/Examples/php/callback/</tt> +<hr> + +<H2>Implementing C++ callbacks in PHP</H2> + +<p> +This example illustrates how to use directors to implement C++ callbacks in PHP. + +<hr> +</body> +</html> diff --git a/Examples/php/callback/runme.php b/Examples/php/callback/runme.php new file mode 100644 index 000000000..2be71994f --- /dev/null +++ b/Examples/php/callback/runme.php @@ -0,0 +1,47 @@ +<?php + +# This file illustrates the cross language polymorphism using directors. + +require("example.php"); + +# Class, which overwrites Callback::run(). + +class PhpCallback extends Callback { + function run() { + print "PhpCallback.run()\n"; + } +}; + +# Create an Caller instance + +$caller = new Caller(); + +# Add a simple C++ callback (caller owns the callback, so +# we disown it first by clearing the .thisown flag). + +print "Adding and calling a normal C++ callback\n"; +print "----------------------------------------\n"; + +$callback = new Callback(); +$callback->thisown = 0; +$caller->setCallback($callback); +$caller->call(); +$caller->delCallback(); + +print "\n"; +print "Adding and calling a PHP callback\n"; +print "------------------------------------\n"; + +# Add a PHP callback. + +$callback = new PhpCallback(); +$callback->thisown = 0; +$caller->setCallback($callback); +$caller->call(); +$caller->delCallback(); + +# All done. + +print "php exit\n"; + +?> diff --git a/Examples/php/extend/Makefile b/Examples/php/extend/Makefile new file mode 100644 index 000000000..42597202b --- /dev/null +++ b/Examples/php/extend/Makefile @@ -0,0 +1,22 @@ +TOP = ../.. +SWIG = $(TOP)/../preinst-swig +CXXSRCS = example.cxx +TARGET = example +INTERFACE = example.i +LIBS = -lm +SWIGOPT = + +all:: + $(MAKE) -f $(TOP)/Makefile $(SWIGLIB) CXXSRCS='$(CXXSRCS)' SWIG='$(SWIG)' \ + SWIGOPT='$(SWIGOPT)' TARGET='$(TARGET)' INTERFACE='$(INTERFACE)' php_cpp + +static:: + $(MAKE) -f $(TOP)/Makefile $(SWIGLIB) CXXSRCS='$(CXXSRCS)' SWIG='$(SWIG)' \ + SWIGOPT='$(SWIGOPT)' TARGET='myphp' INTERFACE='$(INTERFACE)' php_cpp_static + +clean:: + $(MAKE) -f $(TOP)/Makefile php_clean + rm -f $(TARGET).php + +check: all + $(MAKE) -f $(TOP)/Makefile php_run diff --git a/Examples/php/extend/example.cxx b/Examples/php/extend/example.cxx new file mode 100644 index 000000000..450d75608 --- /dev/null +++ b/Examples/php/extend/example.cxx @@ -0,0 +1,4 @@ +/* File : example.cxx */ + +#include "example.h" + diff --git a/Examples/php/extend/example.h b/Examples/php/extend/example.h new file mode 100644 index 000000000..b27ab9711 --- /dev/null +++ b/Examples/php/extend/example.h @@ -0,0 +1,56 @@ +/* File : example.h */ + +#include <cstdio> +#include <iostream> +#include <vector> +#include <string> +#include <cmath> + +class Employee { +private: + std::string name; +public: + Employee(const char* n): name(n) {} + virtual std::string getTitle() { return getPosition() + " " + getName(); } + virtual std::string getName() { return name; } + virtual std::string getPosition() const { return "Employee"; } + virtual ~Employee() { printf("~Employee() @ %p\n", this); } +}; + + +class Manager: public Employee { +public: + Manager(const char* n): Employee(n) {} + virtual std::string getPosition() const { return "Manager"; } +}; + + +class EmployeeList { + std::vector<Employee*> list; +public: + EmployeeList() { + list.push_back(new Employee("Bob")); + list.push_back(new Employee("Jane")); + list.push_back(new Manager("Ted")); + } + void addEmployee(Employee *p) { + list.push_back(p); + std::cout << "New employee added. Current employees are:" << std::endl; + std::vector<Employee*>::iterator i; + for (i=list.begin(); i!=list.end(); i++) { + std::cout << " " << (*i)->getTitle() << std::endl; + } + } + const Employee *get_item(int i) { + return list[i]; + } + ~EmployeeList() { + std::vector<Employee*>::iterator i; + std::cout << "~EmployeeList, deleting " << list.size() << " employees." << std::endl; + for (i=list.begin(); i!=list.end(); i++) { + delete *i; + } + std::cout << "~EmployeeList empty." << std::endl; + } +}; + diff --git a/Examples/php/extend/example.i b/Examples/php/extend/example.i new file mode 100644 index 000000000..c8ec32e09 --- /dev/null +++ b/Examples/php/extend/example.i @@ -0,0 +1,15 @@ +/* File : example.i */ +%module(directors="1") example +%{ +#include "example.h" +%} + +%include "std_vector.i" +%include "std_string.i" + +/* turn on director wrapping for Manager */ +%feature("director") Employee; +%feature("director") Manager; + +%include "example.h" + diff --git a/Examples/php/extend/index.html b/Examples/php/extend/index.html new file mode 100644 index 000000000..32c6a4913 --- /dev/null +++ b/Examples/php/extend/index.html @@ -0,0 +1,19 @@ +<html> +<head> +<title>SWIG:Examples:php:extend</title> +</head> + +<body bgcolor="#ffffff"> + + +<tt>SWIG/Examples/php/extend/</tt> +<hr> + +<H2>Extending a simple C++ class in PHP</H2> + +<p> +This example illustrates the extending of a C++ class with cross language polymorphism. + +<hr> +</body> +</html> diff --git a/Examples/php/extend/runme.php b/Examples/php/extend/runme.php new file mode 100644 index 000000000..158683142 --- /dev/null +++ b/Examples/php/extend/runme.php @@ -0,0 +1,76 @@ +<?php + +# This file illustrates the cross language polymorphism using directors. + +require("example.php"); + +# CEO class, which overrides Employee::getPosition(). + +class CEO extends Manager { + function getPosition() { + return "CEO"; + } +} + +# Create an instance of our employee extension class, CEO. The calls to +# getName() and getPosition() are standard, the call to getTitle() uses +# the director wrappers to call CEO.getPosition. + +$e = new CEO("Alice"); +print $e->getName() . " is a " . $e->getPosition() . "\n"; +printf("Just call her \"%s\"\n", $e->getTitle()); +print "----------------------\n"; + +# Create a new EmployeeList instance. This class does not have a C++ +# director wrapper, but can be used freely with other classes that do. + +$list = new EmployeeList(); + +# EmployeeList owns its items, so we must surrender ownership of objects +# we add. This involves first clearing the ->disown member to tell the +# C++ director to start reference counting. + +$e->thisown = 0; +$list->addEmployee($e); +print "----------------------\n"; + +# Now we access the first four items in list (three are C++ objects that +# EmployeeList's constructor adds, the last is our CEO). The virtual +# methods of all these instances are treated the same. For items 0, 1, and +# 2, both all methods resolve in C++. For item 3, our CEO, getTitle calls +# getPosition which resolves in PHP. The call to getPosition is +# slightly different, however, from the e.getPosition() call above, since +# now the object reference has been "laundered" by passing through +# EmployeeList as an Employee*. Previously, PHP resolved the call +# immediately in CEO, but now PHP thinks the object is an instance of +# class Employee (actually EmployeePtr). So the call passes through the +# Employee proxy class and on to the C wrappers and C++ director, +# eventually ending up back at the CEO implementation of getPosition(). +# The call to getTitle() for item 3 runs the C++ Employee::getTitle() +# method, which in turn calls getPosition(). This virtual method call +# passes down through the C++ director class to the PHP implementation +# in CEO. All this routing takes place transparently. + +print "(position, title) for items 0-3:\n"; + +printf(" %s, \"%s\"\n", $list->get_item(0)->getPosition(), $list->get_item(0)->getTitle()); +printf(" %s, \"%s\"\n", $list->get_item(1)->getPosition(), $list->get_item(1)->getTitle()); +printf(" %s, \"%s\"\n", $list->get_item(2)->getPosition(), $list->get_item(2)->getTitle()); +printf(" %s, \"%s\"\n", $list->get_item(3)->getPosition(), $list->get_item(3)->getTitle()); +print "----------------------\n"; + +# Time to delete the EmployeeList, which will delete all the Employee* +# items it contains. The last item is our CEO, which gets destroyed as its +# reference count goes to zero. The PHP destructor runs, and is still +# able to call the getName() method since the underlying C++ object still +# exists. After this destructor runs the remaining C++ destructors run as +# usual to destroy the object. + +unset($list); +print "----------------------\n"; + +# All done. + +print "php exit\n"; + +?> diff --git a/Examples/python/callback/example.h b/Examples/python/callback/example.h index 1a0e8c432..2a0194999 100644 --- a/Examples/python/callback/example.h +++ b/Examples/python/callback/example.h @@ -1,6 +1,5 @@ /* File : example.h */ -#include <cstdio> #include <iostream> class Callback { diff --git a/Examples/test-suite/director_detect.i b/Examples/test-suite/director_detect.i index 355e5ee91..fcb01b3e9 100644 --- a/Examples/test-suite/director_detect.i +++ b/Examples/test-suite/director_detect.i @@ -15,7 +15,9 @@ %feature("director") Foo; %newobject Foo::cloner(); +%newobject Foo::get_class(); %newobject Bar::cloner(); +%newobject Bar::get_class(); %inline { diff --git a/Examples/test-suite/director_protected.i b/Examples/test-suite/director_protected.i index df3418506..fee45b4a6 100644 --- a/Examples/test-suite/director_protected.i +++ b/Examples/test-suite/director_protected.i @@ -11,6 +11,13 @@ %newobject *::create(); +#ifdef SWIGPHP +// TODO: Currently we do not track the dynamic type of returned objects +// in PHP, so we need the factory helper. +%include factory.i +%factory(Foo *Bar::create, Bar); +#endif + %rename(a) Bar::hello; %rename(s) Foo::p; %rename(q) Foo::r; diff --git a/Examples/test-suite/director_stl.i b/Examples/test-suite/director_stl.i index cbcb4ba85..46946e513 100644 --- a/Examples/test-suite/director_stl.i +++ b/Examples/test-suite/director_stl.i @@ -17,7 +17,11 @@ %feature("director") Foo; %feature("director:except") { +#ifndef SWIGPHP if ($error != NULL) { +#else + if ($error == FAILURE) { +#endif throw Swig::DirectorMethodException(); } } diff --git a/Examples/test-suite/li_factory.i b/Examples/test-suite/li_factory.i index dcd8b308a..7c59d53b2 100644 --- a/Examples/test-suite/li_factory.i +++ b/Examples/test-suite/li_factory.i @@ -6,6 +6,9 @@ %newobject Geometry::clone; %factory(Geometry *Geometry::create, Point, Circle); %factory(Geometry *Geometry::clone, Point, Circle); +#ifdef SWIGPHP +%rename(clone_) clone; +#endif %factory(Geometry *Point::clone, Point, Circle); %factory(Geometry *Circle::clone, Point, Circle); diff --git a/Examples/test-suite/php/Makefile.in b/Examples/test-suite/php/Makefile.in index 021417fd2..679b46b54 100644 --- a/Examples/test-suite/php/Makefile.in +++ b/Examples/test-suite/php/Makefile.in @@ -17,7 +17,7 @@ include $(srcdir)/../common.mk TARGETPREFIX =# Should be php_ for Windows, empty otherwise # Custom tests - tests with additional commandline options -# none! +prefix.cpptest: SWIGOPT += -prefix Project # write out tests without a _runme.php missingcpptests: diff --git a/Examples/test-suite/php/director_abstract_runme.php b/Examples/test-suite/php/director_abstract_runme.php new file mode 100644 index 000000000..ca3d676da --- /dev/null +++ b/Examples/test-suite/php/director_abstract_runme.php @@ -0,0 +1,62 @@ +<?php + +require "tests.php"; +require "director_abstract.php"; + +// No new functions +check::functions(array(foo_ping,foo_pong,example0_getxsize,example0_color,example0_get_color,example1_getxsize,example1_color,example1_get_color,example2_getxsize,example2_color,example2_get_color,example4_getxsize,example4_color,example4_get_color,example3_i_color,example3_i_get_color,g,a_f)); +// No new classes +check::classes(array(director_abstract,Foo,Example0,Example1,Example2,Example4,Example3_i,A)); +// now new vars +check::globals(array()); + +class MyFoo extends Foo { + function ping() { + return "MyFoo::ping()"; + } +} + +$a = new MyFoo(); + +check::equal($a->ping(), "MyFoo::ping()", "MyFoo::ping failed"); + +check::equal($a->pong(), "Foo::pong();MyFoo::ping()", "MyFoo::pong failed"); + +class MyExample1 extends Example1 { + function Color($r, $g, $b) { + return $r; + } +} + +class MyExample2 extends Example1 { + function Color($r, $g, $b) { + return $g; + } +} + +class MyExample3 extends Example1 { + function Color($r, $g, $b) { + return $b; + } +} + +$me1 = new MyExample1(); +check::equal($me1->Color(1, 2, 3), 1, "Example1_get_color failed"); + +$me2 = new MyExample2(1, 2); +check::equal($me2->Color(1, 2, 3), 2, "Example2_get_color failed"); + +$me3 = new MyExample3(); +check::equal($me3->Color(1, 2, 3), 3, "Example3_get_color failed"); + +$class = new ReflectionClass('Example1'); +check::equal($class->isAbstract(), true, "Example1 abstractness failed"); + +$class = new ReflectionClass('Example2'); +check::equal($class->isAbstract(), true, "Example2 abstractness failed"); + +$class = new ReflectionClass('Example3_i'); +check::equal($class->isAbstract(), true, "Example3_i abstractness failed"); + +check::done(); +?> diff --git a/Examples/test-suite/php/director_basic_runme.php b/Examples/test-suite/php/director_basic_runme.php new file mode 100644 index 000000000..de6b50502 --- /dev/null +++ b/Examples/test-suite/php/director_basic_runme.php @@ -0,0 +1,58 @@ +<?php + +require "tests.php"; +require "director_basic.php"; + +// No new functions +check::functions(array(foo_ping,foo_pong,foo_get_self,a_f,a_rg,a1_ff,myclass_method,myclass_vmethod,myclass_pmethod,myclass_cmethod,myclass_get_self,myclass_call_pmethod,myclasst_i_method)); +// No new classes +check::classes(array(Foo,A,A1,Bar,MyClass,MyClassT_i)); +// now new vars +check::globals(array(bar_x)); + +class PhpFoo extends Foo { + function ping() { + return "PhpFoo::ping()"; + } +} + +$a = new PhpFoo(); + +check::equal($a->ping(), "PhpFoo::ping()", "ping failed"); + +check::equal($a->pong(), "Foo::pong();PhpFoo::ping()", "pong failed"); + +$b = new Foo(); + +check::equal($b->ping(), "Foo::ping()", "ping failed"); + +check::equal($b->pong(), "Foo::pong();Foo::ping()", "pong failed"); + +$a = new A1(1); + +check::equal($a->rg(2), 2, "rg failed"); + +class PhpClass extends MyClass { + function vmethod($b) { + $b->x = $b->x + 31; + return $b; + } +} + +$b = new Bar(3); +$d = new MyClass(); +$c = new PhpClass(); + +$cc = MyClass::get_self($c); +$dd = MyClass::get_self($d); + +$bc = $cc->cmethod($b); +$bd = $dd->cmethod($b); + +$cc->method($b); + +check::equal($bc->x, 34, "bc failed"); +check::equal($bd->x, 16, "bd failed"); + +check::done(); +?> diff --git a/Examples/test-suite/php/director_classic_runme.php b/Examples/test-suite/php/director_classic_runme.php new file mode 100644 index 000000000..d2da1b1ba --- /dev/null +++ b/Examples/test-suite/php/director_classic_runme.php @@ -0,0 +1,150 @@ +<?php + +require "tests.php"; +require "director_classic.php"; + +// No new functions +check::functions(array(being_id,person_id,child_id,grandchild_id,caller_delcallback,caller_setcallback,caller_resetcallback,caller_call,caller_baseclass)); +// No new classes +check::classes(array(Being,Person,Child,GrandChild,OrphanPerson,OrphanChild,Caller)); +// now new vars +check::globals(array()); + +class TargetLangPerson extends Person { + function id() { + $identifier = "TargetLangPerson"; + return $identifier; + } +} + +class TargetLangChild extends Child { + function id() { + $identifier = "TargetLangChild"; + return $identifier; + } +} + +class TargetLangGrandChild extends GrandChild { + function id() { + $identifier = "TargetLangGrandChild"; + return $identifier; + } +} + +# Semis - don't override id() in target language +class TargetLangSemiPerson extends Person { + # No id() override +} + +class TargetLangSemiChild extends Child { + # No id() override +} + +class TargetLangSemiGrandChild extends GrandChild { + # No id() override +} + +# Orphans - don't override id() in C++ +class TargetLangOrphanPerson extends OrphanPerson { + function id() { + $identifier = "TargetLangOrphanPerson"; + return $identifier; + } +} + +class TargetLangOrphanChild extends OrphanChild { + function id() { + $identifier = "TargetLangOrphanChild"; + return $identifier; + } +} + +function mycheck($person, $expected) { + $debug = 0; + # Normal target language polymorphic call + $ret = $person->id(); + if ($debug) + print $ret . "\n"; + check::equal($ret, $expected, "#1 failed"); + + # Polymorphic call from C++ + $caller = new Caller(); + $caller->setCallback($person); + $ret = $caller->call(); + if ($debug) + print $ret . "\n"; + check::equal($ret, $expected, "#2 failed"); + + # Polymorphic call of object created in target language and passed to + # C++ and back again + $baseclass = $caller->baseClass(); + $ret = $baseclass->id(); + if ($debug) + print $ret . "\n"; + # TODO: Currently we do not track the dynamic type of returned + # objects, so in case it's possible that the dynamic type is not equal + # to the static type, we skip this check. + if (get_parent_class($person) === false) + check::equal($ret, $expected, "#3 failed"); + + $caller->resetCallback(); + if ($debug) + print "----------------------------------------\n"; +} + +$person = new Person(); +mycheck($person, "Person"); +unset($person); + +$person = new Child(); +mycheck($person, "Child"); +unset($person); + +$person = new GrandChild(); +mycheck($person, "GrandChild"); +unset($person); + +$person = new TargetLangPerson(); +mycheck($person, "TargetLangPerson"); +unset($person); + +$person = new TargetLangChild(); +mycheck($person, "TargetLangChild"); +unset($person); + +$person = new TargetLangGrandChild(); +mycheck($person, "TargetLangGrandChild"); +unset($person); + +# Semis - don't override id() in target language +$person = new TargetLangSemiPerson(); +mycheck($person, "Person"); +unset($person); + +$person = new TargetLangSemiChild(); +mycheck($person, "Child"); +unset($person); + +$person = new TargetLangSemiGrandChild(); +mycheck($person, "GrandChild"); +unset($person); + +# Orphans - don't override id() in C++ +$person = new OrphanPerson(); +mycheck($person, "Person"); +unset($person); + +$person = new OrphanChild(); +mycheck($person, "Child"); +unset($person); + +$person = new TargetLangOrphanPerson(); +mycheck($person, "TargetLangOrphanPerson"); +unset($person); + +$person = new TargetLangOrphanChild(); +mycheck($person, "TargetLangOrphanChild"); +unset($person); + +check::done(); +?> diff --git a/Examples/test-suite/php/director_default_runme.php b/Examples/test-suite/php/director_default_runme.php new file mode 100644 index 000000000..f97fc7425 --- /dev/null +++ b/Examples/test-suite/php/director_default_runme.php @@ -0,0 +1,20 @@ +<?php + +require "tests.php"; +require "director_default.php"; + +// No new functions +check::functions(array(foo_msg,foo_getmsg,bar_msg,bar_getmsg,defaultsbase_defaultargs,defaultsderived_defaultargs)); +// No new classes +check::classes(array(Foo,Bar,DefaultsBase,DefaultsDerived)); +// now new vars +check::globals(array()); + +$f = new Foo(); +$f = new Foo(1); + +$f = new Bar(); +$f = new Bar(1); + +check::done(); +?> diff --git a/Examples/test-suite/php/director_detect_runme.php b/Examples/test-suite/php/director_detect_runme.php new file mode 100644 index 000000000..cc19c0302 --- /dev/null +++ b/Examples/test-suite/php/director_detect_runme.php @@ -0,0 +1,55 @@ +<?php + +require "tests.php"; +require "director_detect.php"; + +// No new functions +check::functions(array(foo_cloner,foo_get_value,foo_get_class,foo_just_do_it,bar_baseclass,bar_cloner,bar_get_value,bar_get_class,bar_just_do_it)); +// No new classes +check::classes(array(A,Foo,Bar)); +// now new vars +check::globals(array()); + +class MyBar extends Bar { + function __construct($val = 2) { + parent::__construct(); + $this->val = $val; + } + + function get_value() { + $this->val = $this->val + 1; + return $this->val; + } + + function get_class() { + $this->val = $this->val + 1; + return new A(); + } + + function just_do_it() { + $this->val = $this->val + 1; + } + + /* clone is a reserved keyword */ + function clone_() { + return new MyBar($this->val); + } +} + +$b = new MyBar(); + +$f = $b->baseclass(); + +$v = $f->get_value(); +$a = $f->get_class(); +$f->just_do_it(); + +$c = $b->clone_(); +$vc = $c->get_value(); + +check::equal($v, 3, "f: Bad virtual detection"); +check::equal($b->val, 5, "b: Bad virtual detection"); +check::equal($vc, 6, "c: Bad virtual detection"); + +check::done(); +?> diff --git a/Examples/test-suite/php/director_enum_runme.php b/Examples/test-suite/php/director_enum_runme.php new file mode 100644 index 000000000..8f6487a28 --- /dev/null +++ b/Examples/test-suite/php/director_enum_runme.php @@ -0,0 +1,25 @@ +<?php + +require "tests.php"; +require "director_enum.php"; + +// No new functions +check::functions(array(foo_say_hello,foo_say_hi,foo_say_bye,foo_say_hi_ref,foo_ping,foo_ping_ref,foo_ping_member_enum,a_f,a2_f)); +// No new classes +check::classes(array(director_enum,Foo,A,B,A2,B2)); +// now new vars +check::globals(array()); + +class MyFoo extends Foo { + function say_hi($val) { + return $val; + } +} + +$b = new Foo(); +$a = new MyFoo(); + +check::equal($a->say_hi(director_enum::hello), $b->say_hello(director_enum::hi), "say failed"); + +check::done(); +?> diff --git a/Examples/test-suite/php/director_exception_runme.php b/Examples/test-suite/php/director_exception_runme.php new file mode 100644 index 000000000..33e6e9b52 --- /dev/null +++ b/Examples/test-suite/php/director_exception_runme.php @@ -0,0 +1,78 @@ +<?php + +require "tests.php"; +require "director_exception.php"; + +// No new functions +check::functions(array(foo_ping,foo_pong,launder,bar_ping,bar_pong,bar_pang)); +// No new classes +check::classes(array(director_exception,Foo,Exception1,Exception2,Base,Bar)); +// now new vars +check::globals(array()); + +class MyException extends Exception { + function __construct($a, $b) { + $this->msg = $a . $b; + } +} + +class MyFoo extends Foo { + function ping() { + throw new Exception("MyFoo::ping() EXCEPTION"); + } +} + +class MyFoo2 extends Foo { + function ping() { + return true; + } +} + +class MyFoo3 extends Foo { + function ping() { + throw new MyException("foo", "bar"); + } +} + +# Check that the Exception raised by MyFoo.ping() is returned by +# MyFoo.pong(). +$ok = 0; +$a = new MyFoo(); +# TODO: Currently we do not track the dynamic type of returned +# objects, so we skip the launder() call. +#$b = director_exception::launder($a); +$b = $a; +try { + $b->pong(); +} catch (Exception $e) { + $ok = 1; + check::equal($e->getMessage(), "MyFoo::ping() EXCEPTION", "Unexpected error message #1"); +} +check::equal($ok, 1, "Got no exception while expected one #1"); + +# Check that the director can return an exception which requires two +# arguments to the constructor, without mangling it. +$ok = 0; +$a = new MyFoo3(); +#$b = director_exception::launder($a); +$b = $a; +try { + $b->pong(); +} catch (Exception $e) { + $ok = 1; + check::equal($e->msg, "foobar", "Unexpected error message #2"); +} +check::equal($ok, 1, "Got no exception while expected one #2"); + +try { + throw new Exception2(); +} catch (Exception2 $e2) { +} + +try { + throw new Exception1(); +} catch (Exception1 $e1) { +} + +check::done(); +?> diff --git a/Examples/test-suite/php/director_extend_runme.php b/Examples/test-suite/php/director_extend_runme.php new file mode 100644 index 000000000..f282df17d --- /dev/null +++ b/Examples/test-suite/php/director_extend_runme.php @@ -0,0 +1,29 @@ +<?php + +require "tests.php"; +require "director_extend.php"; + +// No new functions +check::functions(array(spobject_getfoobar,spobject_dummy,spobject_exceptionmethod)); +// No new classes +check::classes(array(SpObject)); +// now new vars +check::globals(array()); + +class MyObject extends SpObject{ + function __construct() { + parent::__construct(); + return; + } + + function getFoo() { + return 123; + } +} + +$m = new MyObject(); +check::equal($m->dummy(), 666, "1st call"); +check::equal($m->dummy(), 666, "2st call"); // Locked system + +check::done(); +?> diff --git a/Examples/test-suite/php/director_finalizer_runme.php b/Examples/test-suite/php/director_finalizer_runme.php new file mode 100644 index 000000000..0fcddfd8b --- /dev/null +++ b/Examples/test-suite/php/director_finalizer_runme.php @@ -0,0 +1,61 @@ +<?php + +require "tests.php"; +require "director_finalizer.php"; + +// No new functions +check::functions(array(foo_orstatus,deletefoo,getstatus,launder,resetstatus)); +// No new classes +check::classes(array(director_finalizer,Foo)); +// now new vars +check::globals(array()); + +class MyFoo extends Foo { + function __destruct() { + $this->orStatus(2); + if (method_exists(parent, "__destruct")) { + parent::__destruct(); + } + } +} + +resetStatus(); + +$a = new MyFoo(); +unset($a); + +check::equal(getStatus(), 3, "getStatus() failed #1"); + +resetStatus(); + +$a = new MyFoo(); +launder($a); + +check::equal(getStatus(), 0, "getStatus() failed #2"); + +unset($a); + +check::equal(getStatus(), 3, "getStatus() failed #3"); + +resetStatus(); + +$a = new MyFoo(); +$a->thisown = 0; +deleteFoo($a); +unset($a); + +check::equal(getStatus(), 3, "getStatus() failed #4"); + +resetStatus(); + +$a = new MyFoo(); +$a->thisown = 0; +deleteFoo(launder($a)); +unset($a); + +check::equal(getStatus(), 3, "getStatus() failed #5"); + +resetStatus(); + +check::done(); +?> diff --git a/Examples/test-suite/php/director_frob_runme.php b/Examples/test-suite/php/director_frob_runme.php new file mode 100644 index 000000000..548b0b804 --- /dev/null +++ b/Examples/test-suite/php/director_frob_runme.php @@ -0,0 +1,19 @@ +<?php + +require "tests.php"; +require "director_frob.php"; + +// No new functions +check::functions(array(alpha_abs_method,bravo_abs_method,charlie_abs_method,ops_opint,ops_opintstarstarconst,ops_opintamp,ops_opintstar,ops_opconstintintstar,prims_ull,prims_callull,corecallbacks_on3dengineredrawn,corecallbacks_on3dengineredrawn2)); +// No new classes +check::classes(array(Alpha,Bravo,Charlie,Delta,Ops,Prims,corePoint3d,coreCallbacks_On3dEngineRedrawnData,coreCallbacksOn3dEngineRedrawnData,coreCallbacks)); +// now new vars +check::globals(array(corecallbacks_on3dengineredrawndata__eye,corecallbacks_on3dengineredrawndata__at,corecallbackson3dengineredrawndata__eye,corecallbackson3dengineredrawndata__at)); + +$foo = new Bravo(); +$s = $foo->abs_method(); + +check::equal($s, "Bravo::abs_method()", "s failed"); + +check::done(); +?> diff --git a/Examples/test-suite/php/director_nested_runme.php b/Examples/test-suite/php/director_nested_runme.php new file mode 100644 index 000000000..b4d345886 --- /dev/null +++ b/Examples/test-suite/php/director_nested_runme.php @@ -0,0 +1,75 @@ +<?php +// Sample test file + +require "tests.php"; +require "director_nested.php"; + +// No new functions +check::functions(array(foo_int_advance,foo_int_do_advance,bar_step,bar_do_advance,bar_do_step,foobar_int_get_value,foobar_int_get_name,foobar_int_name,foobar_int_get_self,foobar_int_do_advance,foobar_int_do_step)); +// No new classes +check::classes(array(Foo_int,Bar,FooBar_int)); +// now new vars +check::globals(array()); + +class A extends FooBar_int { + function do_step() { + return "A::do_step;"; + } + + function get_value() { + return "A::get_value"; + } +} + +$a = new A(); +check::equal($a->step(), "Bar::step;Foo::advance;Bar::do_advance;A::do_step;", "Bad A virtual resolution"); + +class B extends FooBar_int { + function do_advance() { + return "B::do_advance;" . $this->do_step(); + } + + function do_step() { + return "B::do_step;"; + } + + function get_value() { + return 1; + } +} + +$b = new B(); + +check::equal($b->step(), "Bar::step;Foo::advance;B::do_advance;B::do_step;", "Bad B virtual resolution"); + +class C extends FooBar_int { + function do_advance() { + return "C::do_advance;" . parent::do_advance(); + } + + function do_step() { + return "C::do_step;"; + } + + function get_value() { + return 2; + } + + function get_name() { + return parent::get_name() . " hello"; + } +} + +$cc = new C(); +# TODO: Currently we do not track the dynamic type of returned +# objects, so we skip the get_self() call. +#$c = Foobar_int::get_self($cc); +$c = $cc; +$c->advance(); + +check::equal($c->get_name(), "FooBar::get_name hello", "get_name failed"); + +check::equal($c->name(), "FooBar::get_name hello", "name failed"); + +check::done(); +?> diff --git a/Examples/test-suite/php/director_profile_runme.php b/Examples/test-suite/php/director_profile_runme.php new file mode 100644 index 000000000..c72421341 --- /dev/null +++ b/Examples/test-suite/php/director_profile_runme.php @@ -0,0 +1,53 @@ +<?php + +require "tests.php"; +require "director_profile.php"; + +// No new functions +check::functions(array(b_fn,b_vfi,b_fi,b_fj,b_fk,b_fl,b_get_self,b_vfs,b_fs)); +// No new classes +check::classes(array(A,B)); +// now new vars +check::globals(array()); + +class MyB extends B { + function vfi($a) { + return $a+3; + } +} + +$a = new A(); +$myb = new MyB(); +$b = B::get_self($myb); + +$i = 50000; +$a = 1; + +while ($i) { + $a = $b->fi($a); #1 + $a = $b->fi($a); #2 + $a = $b->fi($a); #3 + $a = $b->fi($a); #4 + $a = $b->fi($a); #5 + $a = $b->fi($a); #6 + $a = $b->fi($a); #7 + $a = $b->fi($a); #8 + $a = $b->fi($a); #9 + $a = $b->fi($a); #10 + $a = $b->fi($a); #1 + $a = $b->fi($a); #2 + $a = $b->fi($a); #3 + $a = $b->fi($a); #4 + $a = $b->fi($a); #5 + $a = $b->fi($a); #6 + $a = $b->fi($a); #7 + $a = $b->fi($a); #8 + $a = $b->fi($a); #9 + $a = $b->fi($a); #20 + $i -= 1; +} + +print $a . "\n"; + +check::done(); +?> diff --git a/Examples/test-suite/php/director_protected_runme.php b/Examples/test-suite/php/director_protected_runme.php new file mode 100644 index 000000000..73bcba1fd --- /dev/null +++ b/Examples/test-suite/php/director_protected_runme.php @@ -0,0 +1,54 @@ +<?php + +require "tests.php"; +require "director_protected.php"; + +// No new functions +check::functions(array(foo_pong,foo_s,foo_q,foo_ping,foo_pang,foo_used,bar_create,bar_pong,bar_used,bar_ping,bar_pang,a_draw,b_draw)); +// No new classes +check::classes(array(Foo,Bar,PrivateFoo,A,B,AA,BB)); +// now new vars +check::globals(array(bar_a)); + +class FooBar extends Bar { + protected function ping() { + return "FooBar::ping();"; + } +} + +class FooBar2 extends Bar { + function ping() { + return "FooBar2::ping();"; + } + + function pang() { + return "FooBar2::pang();"; + } +} + +$b = new Bar(); +$f = $b->create(); +$fb = new FooBar(); +$fb2 = new FooBar2(); + +check::equal($fb->used(), "Foo::pang();Bar::pong();Foo::pong();FooBar::ping();", "bad FooBar::used"); + +check::equal($fb2->used(), "FooBar2::pang();Bar::pong();Foo::pong();FooBar2::ping();", "bad FooBar2::used"); + +check::equal($b->pong(), "Bar::pong();Foo::pong();Bar::ping();", "bad Bar::pong"); + +check::equal($f->pong(), "Bar::pong();Foo::pong();Bar::ping();", "bad Foo::pong"); + +check::equal($fb->pong(), "Bar::pong();Foo::pong();FooBar::ping();", "bad FooBar::pong"); + +$method = new ReflectionMethod('Bar', 'ping'); +check::equal($method->isProtected(), true, "Boo::ping should be protected"); + +$method = new ReflectionMethod('Foo', 'ping'); +check::equal($method->isProtected(), true, "Foo::ping should be protected"); + +$method = new ReflectionMethod('FooBar', 'pang'); +check::equal($method->isProtected(), true, "FooBar::pang should be protected"); + +check::done(); +?> diff --git a/Examples/test-suite/php/director_stl_runme.php b/Examples/test-suite/php/director_stl_runme.php new file mode 100644 index 000000000..29addd261 --- /dev/null +++ b/Examples/test-suite/php/director_stl_runme.php @@ -0,0 +1,60 @@ +<?php + +require "tests.php"; +require "director_stl.php"; + +// No new functions +check::functions(array(foo_bar,foo_ping,foo_pong,foo_tping,foo_tpong,foo_pident,foo_vident,foo_vsecond,foo_tpident,foo_tvident,foo_tvsecond,foo_vidents,foo_tvidents)); +// No new classes +check::classes(array(Foo)); +// now new vars +check::globals(array()); + +class MyFoo extends Foo { + function ping($s) { + return "MyFoo::ping():" . $s; + } + + function pident($arg) { + return $arg; + } + + function vident($v) { + return $v; + } + + function vidents($v) { + return $v; + } + + function vsecond($v1, $v2) { + return $v2; + } +} + +$a = new MyFoo(); + +$a->tping("hello"); +$a->tpong("hello"); + +# TODO: automatic conversion between PHP arrays and std::pair or +# std::vector is not yet implemented. +/*$p = array(1, 2); +$a->pident($p); +$v = array(3, 4); +$a->vident($v); + +$a->tpident($p); +$a->tvident($v); + +$v1 = array(3, 4); +$v2 = array(5, 6); + +$a->tvsecond($v1, $v2); + +$vs = array("hi", "hello"); +$vs; +$a->tvidents($vs);*/ + +check::done(); +?> diff --git a/Examples/test-suite/php/director_string_runme.php b/Examples/test-suite/php/director_string_runme.php new file mode 100644 index 000000000..b239e47fb --- /dev/null +++ b/Examples/test-suite/php/director_string_runme.php @@ -0,0 +1,34 @@ +<?php + +require "tests.php"; +require "director_string.php"; + +// No new functions +check::functions(array(a_get_first,a_call_get_first,a_string_length,a_process_text,a_call_process_func,stringvector_size,stringvector_is_empty,stringvector_clear,stringvector_push,stringvector_pop)); +// No new classes +check::classes(array(A,StringVector)); +// now new vars +check::globals(array(a,a_call,a_m_strings,stringvector)); + +class B extends A { + function get_first() { + return parent::get_first() . " world!"; + } + + function process_text($string) { + parent::process_text($string); + $this->smem = "hello"; + } +} + +$b = new B("hello"); + +$b->get(0); +check::equal($b->get_first(),"hello world!", "get_first failed"); + +$b->call_process_func(); + +check::equal($b->smem, "hello", "smem failed"); + +check::done(); +?> diff --git a/Examples/test-suite/php/director_thread_runme.php b/Examples/test-suite/php/director_thread_runme.php new file mode 100644 index 000000000..8df25d969 --- /dev/null +++ b/Examples/test-suite/php/director_thread_runme.php @@ -0,0 +1,29 @@ +<?php + +require "tests.php"; +require "director_thread.php"; + +// No new functions +check::functions(array(millisecondsleep,foo_stop,foo_run,foo_do_foo)); +// No new classes +check::classes(array(director_thread,Foo)); +// now new vars +check::globals(array(foo_val)); + +class Derived extends Foo { + function do_foo() { + $this->val = $this->val - 1; + } +} + +$d = new Derived(); +$d->run(); + +if ($d->val >= 0) { + check::fail($d->val); +} + +$d->stop(); + +check::done(); +?> diff --git a/Examples/test-suite/php/director_unroll_runme.php b/Examples/test-suite/php/director_unroll_runme.php new file mode 100644 index 000000000..626b1f07d --- /dev/null +++ b/Examples/test-suite/php/director_unroll_runme.php @@ -0,0 +1,29 @@ +<?php + +require "tests.php"; +require "director_unroll.php"; + +// No new functions +check::functions(array(foo_ping,foo_pong)); +// No new classes +check::classes(array(Foo,Bar)); +// now new vars +check::globals(array(bar)); + +class MyFoo extends Foo { + function ping() { + return "MyFoo::ping()"; + } +} + +$a = new MyFoo(); + +$b = new Bar(); + +$b->set($a); +$c = $b->get(); + +check::equal($a->this, $c->this, "this failed"); + +check::done(); +?> diff --git a/Examples/test-suite/php/evil_diamond_prop_runme.php b/Examples/test-suite/php/evil_diamond_prop_runme.php index 6f66d01c5..6cf984d8e 100644 --- a/Examples/test-suite/php/evil_diamond_prop_runme.php +++ b/Examples/test-suite/php/evil_diamond_prop_runme.php @@ -31,7 +31,8 @@ $spam=new spam(); check::is_a($spam,"spam"); check::equal(1,$spam->_foo,"1==spam->_foo"); check::equal(2,$spam->_bar,"2==spam->_bar"); -check::equal(3,$spam->_baz,"3==spam->_baz"); +// multiple inheritance not supported in PHP +check::equal(null,$spam->_baz,"null==spam->_baz"); check::equal(4,$spam->_spam,"4==spam->_spam"); check::done(); diff --git a/Examples/test-suite/php/li_carrays_runme.php b/Examples/test-suite/php/li_carrays_runme.php index babf8443b..8972b9fc2 100644 --- a/Examples/test-suite/php/li_carrays_runme.php +++ b/Examples/test-suite/php/li_carrays_runme.php @@ -11,5 +11,11 @@ check::classes(array(doubleArray)); // now new vars check::globals(array()); +$d = new doubleArray(10); + +$d->setitem(0, 7); +$d->setitem(5, $d->getitem(0) + 3); +check::equal($d->getitem(0) + $d->getitem(5), 17., "7+10==17"); + check::done(); ?> diff --git a/Examples/test-suite/php/li_factory_runme.php b/Examples/test-suite/php/li_factory_runme.php new file mode 100644 index 000000000..6623e2a8c --- /dev/null +++ b/Examples/test-suite/php/li_factory_runme.php @@ -0,0 +1,22 @@ +<?php + +require "tests.php"; +require "li_factory.php"; + +// No new functions +check::functions(array(geometry_draw,geometry_create,geometry_clone_,point_draw,point_width,point_clone_,circle_draw,circle_radius,circle_clone_)); +// No new classes +check::classes(array(Geometry,Point,Circle)); +// now new vars +check::globals(array()); + +$circle = Geometry::create(Geometry::CIRCLE); +$r = $circle->radius(); +check::equal($r, 1.5, "r failed"); + +$point = Geometry::create(Geometry::POINT); +$w = $point->width(); +check::equal($w, 1.0, "w failed"); + +check::done(); +?> diff --git a/Examples/test-suite/php/newobject1_runme.php b/Examples/test-suite/php/newobject1_runme.php new file mode 100644 index 000000000..b105ad755 --- /dev/null +++ b/Examples/test-suite/php/newobject1_runme.php @@ -0,0 +1,20 @@ +<?php +// Sample test file + +require "tests.php"; +require "newobject1.php"; + +// No new functions +check::functions(array(foo_makefoo,foo_makemore,foo_foocount)); +// No new classes +check::classes(array(Foo)); +// now new vars +check::globals(array()); + +$foo = Foo::makeFoo(); +check::equal(get_class($foo), "Foo", "static failed"); +$bar = $foo->makeMore(); +check::equal(get_class($bar), "Foo", "regular failed"); + +check::done(); +?> diff --git a/Examples/test-suite/php/prefix_runme.php b/Examples/test-suite/php/prefix_runme.php new file mode 100644 index 000000000..6adc1375e --- /dev/null +++ b/Examples/test-suite/php/prefix_runme.php @@ -0,0 +1,19 @@ +<?php +// Sample test file + +require "tests.php"; +require "prefix.php"; + +// No new functions +check::functions(array(foo_get_self)); +// No new classes +check::classes(array(ProjectFoo)); +// now new vars +check::globals(array()); + +$f = new ProjectFoo(); +// This resulted in "Fatal error: Class 'Foo' not found" +$f->get_self(); + +check::done(); +?> diff --git a/Examples/test-suite/php/tests.php b/Examples/test-suite/php/tests.php index b62e878fc..ad58ac9d8 100644 --- a/Examples/test-suite/php/tests.php +++ b/Examples/test-suite/php/tests.php @@ -34,8 +34,11 @@ class check { $df=array_flip($df[internal]); foreach($_original_functions[internal] as $func) unset($df[$func]); // Now chop out any get/set accessors - foreach(array_keys($df) as $func) if (GETSET && ereg('_[gs]et$',$func)) $extrags[]=$func; - else $extra[]=$func; + foreach(array_keys($df) as $func) + if ((GETSET && ereg('_[gs]et$',$func)) || ereg('^new_', $func) + || ereg('_(alter|get)_newobject$', $func)) + $extrags[]=$func; + else $extra[]=$func; // $extra=array_keys($df); } if ($gs) return $extrags; diff --git a/Examples/test-suite/prefix.i b/Examples/test-suite/prefix.i new file mode 100644 index 000000000..b0cb31205 --- /dev/null +++ b/Examples/test-suite/prefix.i @@ -0,0 +1,14 @@ +// Test that was failing for PHP - the value of the -prefix option was +// ignored +%module prefix + +%inline %{ + +class Foo { +public: + Foo *get_self() { + return this; + } +}; + +%} diff --git a/Lib/php/director.swg b/Lib/php/director.swg new file mode 100644 index 000000000..6a9d4df3c --- /dev/null +++ b/Lib/php/director.swg @@ -0,0 +1,198 @@ +/* ----------------------------------------------------------------------------- + * See the LICENSE file for information on copyright, usage and redistribution + * of SWIG, and the README file for authors - http://www.swig.org/release.html. + * + * director.swg + * + * This file contains support for director classes that proxy + * method calls from C++ to PHP extensions. + * ----------------------------------------------------------------------------- */ + +#ifndef SWIG_DIRECTOR_PHP_HEADER_ +#define SWIG_DIRECTOR_PHP_HEADER_ + +#ifdef __cplusplus + +#include <string> +#include <map> + +/* + Use -DSWIG_DIRECTOR_STATIC if you prefer to avoid the use of the + 'Swig' namespace. This could be useful for multi-modules projects. +*/ +#ifdef SWIG_DIRECTOR_STATIC +/* Force anonymous (static) namespace */ +#define Swig +#endif + +namespace Swig { + /* memory handler */ + struct GCItem + { + virtual ~GCItem() {} + + virtual int get_own() const + { + return 0; + } + }; + + struct GCItem_var + { + GCItem_var(GCItem *item = 0) : _item(item) + { + } + + GCItem_var& operator=(GCItem *item) + { + GCItem *tmp = _item; + _item = item; + delete tmp; + return *this; + } + + ~GCItem_var() + { + delete _item; + } + + GCItem * operator->() const + { + return _item; + } + + private: + GCItem *_item; + }; + + struct GCItem_Object : GCItem + { + GCItem_Object(int own) : _own(own) + { + } + + virtual ~GCItem_Object() + { + } + + int get_own() const + { + return _own; + } + + private: + int _own; + }; + + template <typename Type> + struct GCItem_T : GCItem + { + GCItem_T(Type *ptr) : _ptr(ptr) + { + } + + virtual ~GCItem_T() + { + delete _ptr; + } + + private: + Type *_ptr; + }; + + class Director { + protected: + zval *swig_self; + typedef std::map<void*, GCItem_var> ownership_map; + mutable ownership_map owner; + public: + Director(zval* self) : swig_self(self) { + } + + ~Director() { + for (ownership_map::iterator i = owner.begin(); i != owner.end(); i++) { + owner.erase(i); + } + } + + bool is_overriden_method(char *cname, char *lc_fname) { + zval classname; + zend_class_entry **ce; + zend_function *mptr; + int name_len = strlen(lc_fname); + + ZVAL_STRING(&classname, cname, 0); + if (zend_lookup_class(Z_STRVAL_P(&classname), Z_STRLEN_P(&classname), &ce TSRMLS_CC) != SUCCESS) { + return false; + } + if (zend_hash_find(&(*ce)->function_table, lc_fname, name_len + 1, (void**) &mptr) != SUCCESS) { + return false; + } + // common.scope points to the declaring class + return strcmp(mptr->common.scope->name, cname); + } + + template <typename Type> + void swig_acquire_ownership(Type *vptr) const + { + if (vptr) { + owner[vptr] = new GCItem_T<Type>(vptr); + } + } + }; + + /* base class for director exceptions */ + class DirectorException { + protected: + std::string swig_msg; + public: + DirectorException(int code, const char *hdr, const char* msg) + : swig_msg(hdr) + { + if (strlen(msg)) { + swig_msg += " "; + swig_msg += msg; + } + SWIG_ErrorCode() = code; + SWIG_ErrorMsg() = swig_msg.c_str(); + } + + static void raise(int code, const char *hdr, const char* msg) + { + throw DirectorException(code, hdr, msg); + } + }; + + /* attempt to call a pure virtual method via a director method */ + class DirectorPureVirtualException : public Swig::DirectorException + { + public: + DirectorPureVirtualException(const char* msg) + : DirectorException(E_ERROR, "Swig director pure virtual method called", msg) + { + } + + static void raise(const char *msg) + { + throw DirectorPureVirtualException(msg); + } + }; + /* any php exception that occurs during a director method call */ + class DirectorMethodException : public Swig::DirectorException + { + public: + DirectorMethodException(const char* msg = "") + : DirectorException(E_ERROR, "Swig director method error", msg) + { + } + + static void raise(const char *msg) + { + throw DirectorMethodException(msg); + } + }; +} + +#endif /* __cplusplus */ + +#endif diff --git a/Lib/php/factory.i b/Lib/php/factory.i new file mode 100644 index 000000000..c4e082dd2 --- /dev/null +++ b/Lib/php/factory.i @@ -0,0 +1,109 @@ +/* + Implement a more natural wrap for factory methods, for example, if + you have: + + ---- geometry.h -------- + struct Geometry { + enum GeomType{ + POINT, + CIRCLE + }; + + virtual ~Geometry() {} + virtual int draw() = 0; + + // + // Factory method for all the Geometry objects + // + static Geometry *create(GeomType i); + }; + + struct Point : Geometry { + int draw() { return 1; } + double width() { return 1.0; } + }; + + struct Circle : Geometry { + int draw() { return 2; } + double radius() { return 1.5; } + }; + + // + // Factory method for all the Geometry objects + // + Geometry *Geometry::create(GeomType type) { + switch (type) { + case POINT: return new Point(); + case CIRCLE: return new Circle(); + default: return 0; + } + } + ---- geometry.h -------- + + + You can use the %factory with the Geometry::create method as follows: + + %newobject Geometry::create; + %factory(Geometry *Geometry::create, Point, Circle); + %include "geometry.h" + + and Geometry::create will return a 'Point' or 'Circle' instance + instead of the plain 'Geometry' type. For example, in python: + + circle = Geometry.create(Geometry.CIRCLE) + r = circle.radius() + + where circle is a Circle proxy instance. + + NOTES: remember to fully qualify all the type names and don't + use %factory inside a namespace declaration, ie, instead of + + namespace Foo { + %factory(Geometry *Geometry::create, Point, Circle); + } + + use + + %factory(Foo::Geometry *Foo::Geometry::create, Foo::Point, Foo::Circle); + + +*/ + +/* for loop for macro with one argument */ +%define %_formacro_1(macro, arg1,...)macro(arg1) +#if #__VA_ARGS__ != "__fordone__" +%_formacro_1(macro, __VA_ARGS__) +#endif +%enddef + +/* for loop for macro with one argument */ +%define %formacro_1(macro,...)%_formacro_1(macro,__VA_ARGS__,__fordone__)%enddef +%define %formacro(macro,...)%_formacro_1(macro,__VA_ARGS__,__fordone__)%enddef + +/* for loop for macro with two arguments */ +%define %_formacro_2(macro, arg1, arg2, ...)macro(arg1, arg2) +#if #__VA_ARGS__ != "__fordone__" +%_formacro_2(macro, __VA_ARGS__) +#endif +%enddef + +/* for loop for macro with two arguments */ +%define %formacro_2(macro,...)%_formacro_2(macro, __VA_ARGS__, __fordone__)%enddef + +%define %_factory_dispatch(Type) +if (!dcast) { + Type *dobj = dynamic_cast<Type *>($1); + if (dobj) { + dcast = 1; + SWIG_SetPointerZval(return_value, SWIG_as_voidptr(dobj),$descriptor(Type *), $owner); + } +}%enddef + +%define %factory(Method,Types...) +%typemap(out) Method { + int dcast = 0; + %formacro(%_factory_dispatch, Types) + if (!dcast) { + SWIG_SetPointerZval(return_value, SWIG_as_voidptr($1),$descriptor, $owner); + } +}%enddef diff --git a/Lib/php/php.swg b/Lib/php/php.swg index 087525be4..42cb8db4a 100644 --- a/Lib/php/php.swg +++ b/Lib/php/php.swg @@ -89,6 +89,14 @@ $1 = *tmp; } +%typemap(directorout) SWIGTYPE ($&1_ltype tmp) +{ + if(SWIG_ConvertPtr(*$input, (void **) &tmp, $&1_descriptor, 0) < 0 || tmp == NULL) { + SWIG_PHP_Error(E_ERROR, "Type error in argument $argnum of $symname. Expected $&1_descriptor"); + } + $result = *tmp; +} + %typemap(in) SWIGTYPE *, SWIGTYPE [] { @@ -176,17 +184,42 @@ ZVAL_LONG(return_value,$1); } +%typemap(directorin) int, + unsigned int, + short, + unsigned short, + long, + unsigned long, + signed char, + unsigned char, + size_t, + enum SWIGTYPE +{ + ZVAL_LONG($input,$1_name); +} + %typemap(out) bool { ZVAL_BOOL(return_value,($1)?1:0); } +%typemap(directorin) bool +{ + ZVAL_BOOL($input,($1_name)?1:0); +} + %typemap(out) float, double { ZVAL_DOUBLE(return_value,$1); } +%typemap(directorin) float, + double +{ + ZVAL_DOUBLE($input,$1_name); +} + %typemap(out) char { ZVAL_STRINGL(return_value,&$1, 1, 1); @@ -209,6 +242,13 @@ SWIG_SetPointerZval(return_value, (void *)$1, $1_descriptor, $owner); %} +%typemap(directorin) SWIGTYPE *, + SWIGTYPE [], + SWIGTYPE & +%{ + SWIG_SetPointerZval($input, (void *)&$1_name, $1_descriptor, $owner); +%} + %typemap(out) SWIGTYPE *DYNAMIC, SWIGTYPE &DYNAMIC { @@ -230,6 +270,19 @@ } #endif +%typemap(directorin) SWIGTYPE +#ifdef __cplusplus +{ + SWIG_SetPointerZval($input, SWIG_as_voidptr(&$1_name), $&1_descriptor, 2); +} +#else +{ + $&1_ltype resultobj = ($&1_ltype) emalloc(sizeof($1_type)); + memcpy(resultobj, &$1, sizeof($1_type)); + SWIG_SetPointerZval($input, (void *)resultobj, $&1_descriptor, 2); +} +#endif + %typemap(out) void ""; %typemap(out) char [ANY] diff --git a/Lib/php/phprun.swg b/Lib/php/phprun.swg index 131c5ba3a..992745323 100644 --- a/Lib/php/phprun.swg +++ b/Lib/php/phprun.swg @@ -13,6 +13,7 @@ extern "C" { #include "zend.h" #include "zend_API.h" #include "php.h" +#include "ext/standard/php_string.h" #ifdef ZEND_RAW_FENTRY /* ZEND_RAW_FENTRY was added somewhere between 5.2.0 and 5.2.3 */ @@ -84,6 +85,7 @@ typedef struct { static ZEND_RSRC_DTOR_FUNC(SWIG_landfill) { (void)rsrc; } #define SWIG_SetPointerZval(a,b,c,d) SWIG_ZTS_SetPointerZval(a,b,c,d TSRMLS_CC) +#define SWIG_as_voidptr(a) const_cast< void * >(static_cast< const void * >(a)) static void SWIG_ZTS_SetPointerZval(zval *z, void *ptr, swig_type_info *type, int newobject TSRMLS_DC) { @@ -101,7 +103,31 @@ SWIG_ZTS_SetPointerZval(zval *z, void *ptr, swig_type_info *type, int newobject value=(swig_object_wrapper *)emalloc(sizeof(swig_object_wrapper)); value->ptr=ptr; value->newobject=newobject; - ZEND_REGISTER_RESOURCE(z, value, *(int *)(type->clientdata)); + if (newobject <= 1) { + ZEND_REGISTER_RESOURCE(z, value, *(int *)(type->clientdata)); + } else { + value->newobject = 0; + zval *resource; + MAKE_STD_ZVAL(resource); + ZEND_REGISTER_RESOURCE(resource, value, *(int *)(type->clientdata)); + zend_class_entry **ce = NULL; + zval *classname; + MAKE_STD_ZVAL(classname); + /* _p_Foo -> Foo */ + ZVAL_STRING(classname, (char*)type->name+3, 1); + /* class names are stored in lowercase */ + php_strtolower(Z_STRVAL_PP(&classname), Z_STRLEN_PP(&classname)); + if (zend_lookup_class(Z_STRVAL_P(classname), Z_STRLEN_P(classname), &ce TSRMLS_CC) != SUCCESS) { + /* class does not exists */ + object_init(z); + } else { + object_init_ex(z, *ce); + } + z->refcount = 1; + z->is_ref = 1; + zend_hash_update(HASH_OF(z), (char*)"_cPtr", sizeof("_cPtr"), (void*)&resource, sizeof(zval), NULL); + FREE_ZVAL(classname); + } return; } zend_error(E_ERROR, "Type: %s not registered with zend",type->name); @@ -156,7 +182,7 @@ SWIG_ZTS_ConvertResourcePtr(zval *z, swig_type_info *ty, int flags TSRMLS_DC) { char *type_name; value = (swig_object_wrapper *) zend_list_find(z->value.lval, &type); - if ( flags && SWIG_POINTER_DISOWN ) { + if ( flags & SWIG_POINTER_DISOWN ) { value->newobject = 0; } p = value->ptr; diff --git a/Lib/php/std_string.i b/Lib/php/std_string.i index 08a7cdac9..eff6951b5 100644 --- a/Lib/php/std_string.i +++ b/Lib/php/std_string.i @@ -35,6 +35,11 @@ namespace std { $1.assign(Z_STRVAL_PP($input), Z_STRLEN_PP($input)); %} + %typemap(directorout) string %{ + convert_to_string_ex($input); + $result.assign(Z_STRVAL_PP($input), Z_STRLEN_PP($input)); + %} + %typemap(typecheck,precedence=SWIG_TYPECHECK_STRING) const string& %{ $1 = ( Z_TYPE_PP($input) == IS_STRING ) ? 1 : 0; %} @@ -43,6 +48,10 @@ namespace std { ZVAL_STRINGL($result, const_cast<char*>($1.data()), $1.size(), 1); %} + %typemap(directorin) string %{ + ZVAL_STRINGL($input, const_cast<char*>($1_name.data()), $1_name.size(), 1); + %} + %typemap(out) const string & %{ ZVAL_STRINGL($result, const_cast<char*>($1->data()), $1->size(), 1); %} @@ -63,6 +72,14 @@ namespace std { $1 = &temp; %} + %typemap(directorout) string & (std::string *temp) %{ + convert_to_string_ex($input); + temp = new std::string; + temp->assign(Z_STRVAL_PP($input), Z_STRLEN_PP($input)); + swig_acquire_ownership(temp); + $result = temp; + %} + %typemap(argout) string & %{ ZVAL_STRINGL(*($input), const_cast<char*>($1->data()), $1->size(), 1); %} diff --git a/Lib/php/utils.i b/Lib/php/utils.i index facf54196..becaf598a 100644 --- a/Lib/php/utils.i +++ b/Lib/php/utils.i @@ -37,6 +37,10 @@ %{ CONVERT_IN($1,$1_ltype,$input); %} +%typemap(directorout) TYPE, const TYPE & +%{ + CONVERT_IN($result,$1_ltype,$input); +%} %enddef %fragment("t_output_helper","header") %{ @@ -59,6 +59,7 @@ Major contributors include: Martin Froehlich <MartinFroehlich@ACM.org> (Guile) Marcio Luis Teixeira <marciot@holly.colostate.edu> (Guile) Duncan Temple Lang (R) + Miklos Vajna <vmiklos@frugalware.org> (PHP directors) Past contributors include: James Michael DuPont, Clark McGrew, Dustin Mitchell, Ian Cooke, Catalin Dumitrescu, Baran diff --git a/Source/Modules/overload.cxx b/Source/Modules/overload.cxx index 511e55004..79656517a 100644 --- a/Source/Modules/overload.cxx +++ b/Source/Modules/overload.cxx @@ -718,6 +718,10 @@ String *Swig_overload_dispatch(Node *n, const_String_or_char_ptr fmt, int *maxar Parm *pi = Getattr(ni, "wrap:parms"); int num_required = emit_num_required(pi); int num_arguments = emit_num_arguments(pi); + if (GetFlag(n, "wrap:this")) { + num_required++; + num_arguments++; + } if (num_arguments > *maxargs) *maxargs = num_arguments; int varargs = emit_isvarargs(pi); @@ -751,7 +755,7 @@ String *Swig_overload_dispatch(Node *n, const_String_or_char_ptr fmt, int *maxar Printf(f, "}\n"); Delete(lfmt); } - if (print_typecheck(f, j, pj)) { + if (print_typecheck(f, (GetFlag(n, "wrap:this") ? j + 1 : j), pj)) { Printf(f, "if (_v) {\n"); num_braces++; } diff --git a/Source/Modules/php.cxx b/Source/Modules/php.cxx index e3b22f1d3..8d65df889 100644 --- a/Source/Modules/php.cxx +++ b/Source/Modules/php.cxx @@ -62,6 +62,11 @@ PHP Options (available with -php)\n\ */ #define SWIG_PTR "_cPtr" +/* This is the name of the hash where the variables existing only in PHP + * classes are stored. + */ +#define SWIG_DATA "_pData" + static int constructors = 0; static String *NOTCLASS = NewString("Not a class"); static Node *classnode = 0; @@ -73,8 +78,11 @@ static String *shadow_classname = 0; static File *f_begin = 0; static File *f_runtime = 0; +static File *f_runtime_h = 0; static File *f_h = 0; static File *f_phpcode = 0; +static File *f_directors = 0; +static File *f_directors_h = 0; static String *phpfilename = 0; static String *s_header; @@ -117,6 +125,7 @@ static enum { membervar, staticmembervar, constructor, + directorconstructor, destructor } wrapperType = standard; @@ -175,7 +184,9 @@ void SwigPHP_emit_resource_registrations() { class PHP : public Language { public: - PHP() { } + PHP() { + director_language = 1; + } /* Test to see if a type corresponds to something wrapped with a shadow class. */ @@ -256,8 +267,21 @@ public: String *filen; String *s_type; + /* Check if directors are enabled for this module. */ + Node *mod = Getattr(n, "module"); + if (mod) { + Node *options = Getattr(mod, "options"); + if (options && Getattr(options, "directors")) { + allow_directors(); + } + } + + /* Set comparison with null for ConstructorToFunction */ + setSubclassInstanceCheck(NewString("$arg->type != IS_NULL")); + /* Initialize all of the output files */ String *outfile = Getattr(n, "outfile"); + String *outfile_h = Getattr(n, "outfile_h"); /* main output file */ f_begin = NewFile(outfile, "w", SWIG_output_files()); @@ -265,7 +289,7 @@ public: FileErrorDisplay(outfile); SWIG_exit(EXIT_FAILURE); } - f_runtime = NewString(""); + f_runtime = NewStringEmpty(); /* sections of the output file */ s_init = NewString("/* init section */\n"); @@ -282,6 +306,16 @@ public: s_oinit = NewString("/* oinit subsection */\n"); pragma_phpinfo = NewStringEmpty(); s_phpclasses = NewString("/* PHP Proxy Classes */\n"); + f_directors_h = NewStringEmpty(); + f_directors = NewStringEmpty(); + + if (directorsEnabled()) { + f_runtime_h = NewFile(outfile_h, "w", SWIG_output_files()); + if (!f_runtime_h) { + FileErrorDisplay(outfile_h); + SWIG_exit(EXIT_FAILURE); + } + } /* Register file targets with the SWIG file handler */ Swig_register_filebyname("begin", f_begin); @@ -292,6 +326,8 @@ public: Swig_register_filebyname("rshutdown", r_shutdown); Swig_register_filebyname("header", s_header); Swig_register_filebyname("wrapper", s_wrappers); + Swig_register_filebyname("director", f_directors); + Swig_register_filebyname("director_h", f_directors_h); Swig_banner(f_begin); @@ -299,12 +335,25 @@ public: Printf(f_runtime, "#define SWIGPHP\n"); Printf(f_runtime, "\n"); + if (directorsEnabled()) { + Printf(f_runtime, "#define SWIG_DIRECTORS\n"); + } + /* Set the module name */ module = Copy(Getattr(n, "name")); cap_module = NewStringf("%(upper)s", module); if (!prefix) prefix = NewStringEmpty(); + if (directorsEnabled()) { + Swig_banner(f_directors_h); + Printf(f_directors_h, "\n"); + Printf(f_directors_h, "#ifndef SWIG_%s_WRAP_H_\n", cap_module); + Printf(f_directors_h, "#define SWIG_%s_WRAP_H_\n\n", cap_module); + + Printf(f_directors, "\n#include \"%s\"\n\n", Swig_file_filename(outfile_h)); + } + /* PHP module file */ filen = NewStringEmpty(); Printv(filen, SWIG_output_directory(), module, ".php", NIL); @@ -371,6 +420,39 @@ public: Printf(s_header, " SWIG_ErrorCode() = default_error_code;\n"); Printf(s_header, "}\n"); + Append(s_header, "\n"); + Printf(s_header, "ZEND_NAMED_FUNCTION(_wrap_swig_%s_alter_newobject) {\n", module); + Append(s_header, " zval **args[2];\n"); + Append(s_header, " swig_object_wrapper *value;\n"); + Append(s_header, " int type;\n"); + Append(s_header, " int thisown;\n"); + Append(s_header, "\n"); + Append(s_header, " SWIG_ResetError();\n"); + Append(s_header, " if(ZEND_NUM_ARGS() != 2 || zend_get_parameters_array_ex(2, args) != SUCCESS) {\n"); + Append(s_header, " WRONG_PARAM_COUNT;\n"); + Append(s_header, " }\n"); + Append(s_header, "\n"); + Append(s_header, " value = (swig_object_wrapper *) zend_list_find((*args[0])->value.lval, &type);\n"); + Append(s_header, " value->newobject = zval_is_true(*args[1]);\n"); + Append(s_header, "\n"); + Append(s_header, " return;\n"); + Append(s_header, "}\n"); + Printf(s_header, "ZEND_NAMED_FUNCTION(_wrap_swig_%s_get_newobject) {\n", module); + Append(s_header, " zval **args[1];\n"); + Append(s_header, " swig_object_wrapper *value;\n"); + Append(s_header, " int type;\n"); + Append(s_header, "\n"); + Append(s_header, " SWIG_ResetError();\n"); + Append(s_header, " if(ZEND_NUM_ARGS() != 1 || zend_get_parameters_array_ex(1, args) != SUCCESS) {\n"); + Append(s_header, " WRONG_PARAM_COUNT;\n"); + Append(s_header, " }\n"); + Append(s_header, "\n"); + Append(s_header, " value = (swig_object_wrapper *) zend_list_find((*args[0])->value.lval, &type);\n"); + Append(s_header, " RETVAL_LONG(value->newobject);\n"); + Append(s_header, "\n"); + Append(s_header, " return;\n"); + Append(s_header, "}\n"); + Printf(s_header, "#define SWIG_name \"%s\"\n", module); /* Printf(s_header,"#ifdef HAVE_CONFIG_H\n"); Printf(s_header,"#include \"config.h\"\n"); @@ -387,6 +469,11 @@ public: Printf(s_header, "}\n"); Printf(s_header, "#endif\n\n"); + if (directorsEnabled()) { + // Insert director runtime + Swig_insert_file("director.swg", s_header); + } + /* Create the .h file too */ filen = NewStringEmpty(); Printv(filen, SWIG_output_directory(), "php_", module, ".h", NIL); @@ -523,13 +610,27 @@ public: * function really needs totally redoing. */ + if (directorsEnabled()) { + Dump(f_directors_h, f_runtime_h); + Printf(f_runtime_h, "\n"); + Printf(f_runtime_h, "#endif\n"); + Close(f_runtime_h); + } + Printf(s_header, "/* end header section */\n"); Printf(s_wrappers, "/* end wrapper section */\n"); Printf(s_vdecl, "/* end vdecl subsection */\n"); Dump(f_runtime, f_begin); - Printv(f_begin, s_header, s_vdecl, s_wrappers, NIL); - Printv(f_begin, all_cs_entry, "\n\n", s_entry, "{NULL, NULL, NULL}\n};\n\n", NIL); + Printv(f_begin, s_header, NIL); + if (directorsEnabled()) { + Dump(f_directors, f_begin); + } + Printv(f_begin, s_vdecl, s_wrappers, NIL); + Printv(f_begin, all_cs_entry, "\n\n", s_entry, + " SWIG_ZEND_NAMED_FE(swig_", module, "_alter_newobject,_wrap_swig_", module, "_alter_newobject,NULL)\n" + " SWIG_ZEND_NAMED_FE(swig_", module, "_get_newobject,_wrap_swig_", module, "_get_newobject,NULL)\n" + "{NULL, NULL, NULL}\n};\n\n", NIL); Printv(f_begin, s_init, NIL); Delete(s_header); Delete(s_wrappers); @@ -572,6 +673,10 @@ public: int maxargs; String *tmp = NewStringEmpty(); + if (Swig_directorclass(n) && wrapperType == directorconstructor) { + /* We have an extra 'this' parameter. */ + SetFlag(n, "wrap:this"); + } String *dispatch = Swig_overload_dispatch(n, "return %s(INTERNAL_FUNCTION_PARAM_PASSTHRU);", &maxargs); /* Generate a dispatch wrapper for all overloaded functions */ @@ -633,6 +738,7 @@ public: ParmList *l = Getattr(n, "parms"); String *nodeType = Getattr(n, "nodeType"); int newobject = GetFlag(n, "feature:new"); + int constructor = (Cmp(nodeType, "constructor") == 0); Parm *p; int i; @@ -688,12 +794,26 @@ public: int num_required = emit_num_required(l); numopt = num_arguments - num_required; + if (wrapperType == directorconstructor) + num_arguments++; + if (num_arguments > 0) { - String *args = NewStringf("zval **args[%d]", num_arguments); + String *args = NewStringEmpty(); + if (wrapperType == directorconstructor) + Wrapper_add_local(f, "arg0", "zval *arg0"); + Printf(args, "zval **args[%d]", num_arguments); Wrapper_add_local(f, "args", args); Delete(args); args = NULL; } + if (is_member_director(n)) { + Wrapper_add_local(f, "director", "Swig::Director *director = 0"); + Printf(f->code, "director = dynamic_cast<Swig::Director*>(arg1);\n"); + Wrapper_add_local(f, "upcall", "bool upcall = false"); + Printf(f->code, "upcall = !director->is_overriden_method((char *)\"%s\", (char *)\"%s\");\n", + Swig_class_name(Swig_methodclass(n)), name); + } + // This generated code may be called: // 1) as an object method, or // 2) as a class-method/function (without a "this_ptr") @@ -718,6 +838,8 @@ public: } Printf(f->code, "WRONG_PARAM_COUNT;\n}\n\n"); } + if (wrapperType == directorconstructor) + Printf(f->code, "arg0 = *args[0];\n \n"); /* Now convert from PHP to C variables */ // At this point, argcount if used is the number of deliberately passed args @@ -729,7 +851,10 @@ public: // _this and not the first argument. // This may mean looking at Language::memberfunctionHandler - for (i = 0, p = l; i < num_arguments; i++) { + int limit = num_arguments; + if (wrapperType == directorconstructor) + limit--; + for (i = 0, p = l; i < limit; i++) { String *source; /* Skip ignored arguments */ @@ -740,7 +865,11 @@ public: SwigType *pt = Getattr(p, "type"); - source = NewStringf("args[%d]", i); + if (wrapperType == directorconstructor) { + source = NewStringf("args[%d]", i+1); + } else { + source = NewStringf("args[%d]", i); + } String *ln = Getattr(p, "lname"); @@ -772,6 +901,8 @@ public: Delete(source); } + Swig_director_emit_dynamic_cast(n, f); + /* Insert constraint checking code */ for (p = l; p;) { if ((tm = Getattr(p, "tmap:check"))) { @@ -854,7 +985,7 @@ public: /* Error handling code */ Printf(f->code, "fail:\n"); Printv(f->code, cleanup, NIL); - Printv(f->code, "zend_error(SWIG_ErrorCode(),\"%s\",SWIG_ErrorMsg());", NIL); + Printv(f->code, "zend_error_noreturn(SWIG_ErrorCode(),\"%s\",SWIG_ErrorMsg());", NIL); Printf(f->code, "}\n"); @@ -903,7 +1034,7 @@ public: // Method or static method or plain function. const char *methodname = 0; String *output = s_oowrappers; - if (newobject) { + if (constructor) { class_has_ctor = true; methodname = "__construct"; } else if (wrapperType == memberfn) { @@ -1346,9 +1477,20 @@ public: while (last_handled_i < i) { Printf(prepare, "case %d: ", ++last_handled_i); } - if (Cmp(d, "void") != 0) - Printf(prepare, "$r="); - Printf(prepare, "%s(%s); break;\n", iname, invoke_args); + if (Cmp(d, "void") != 0) { + if ((!directorsEnabled() || !Swig_directorclass(n)) && !newobject) { + Append(prepare, "$r="); + } else { + Printf(prepare, "$this->%s=", SWIG_PTR); + } + } + if (!directorsEnabled() || !Swig_directorclass(n) || !newobject) { + Printf(prepare, "%s(%s); break;\n", iname, invoke_args); + } else if (!i) { + Printf(prepare, "%s($_this%s); break;\n", iname, invoke_args); + } else { + Printf(prepare, "%s($_this, %s); break;\n", iname, invoke_args); + } } if (i || wrapperType == memberfn) Printf(invoke_args, ","); @@ -1357,9 +1499,18 @@ public: Printf(prepare, "\t\t"); if (had_a_case) Printf(prepare, "default: "); - if (Cmp(d, "void") != 0) - Printf(prepare, "$r="); - Printf(prepare, "%s(%s);\n", iname, invoke_args); + if (Cmp(d, "void") != 0) { + if ((!directorsEnabled() || !Swig_directorclass(n)) && !newobject) { + Append(prepare, "$r="); + } else { + Printf(prepare, "$this->%s=", SWIG_PTR); + } + } + if (!directorsEnabled() || !Swig_directorclass(n) || !newobject) { + Printf(prepare, "%s(%s);\n", iname, invoke_args); + } else { + Printf(prepare, "%s($_this, %s);\n", iname, invoke_args); + } if (had_a_case) Printf(prepare, "\t\t}\n"); Delete(invoke_args); @@ -1368,10 +1519,9 @@ public: Printf(output, "\n"); // If it's a member function or a class constructor... - if (wrapperType == memberfn || (newobject && current_class)) { - // We don't need this code if the wrapped class has a copy ctor - // since the flat function new_CLASSNAME will handle it for us. - if (newobject && !Getattr(current_class, "allocate:copy_constructor")) { + if (wrapperType == memberfn || (constructor && current_class)) { + String *acc = Getattr(n, "access"); + if (constructor) { const char * arg0; if (max_num_of_arguments > 0) { arg0 = Char(arg_names[0]); @@ -1382,29 +1532,40 @@ public: } SwigType *t = Getattr(current_class, "classtype"); String *mangled_type = SwigType_manglestr(SwigType_ltype(t)); - Printf(output, "\tfunction %s(%s) {\n", methodname, args); - Printf(output, "\t\tif (is_resource($%s) && get_resource_type($%s) == '_p%s') {\n", arg0, arg0, mangled_type); + Printf(output, "\t%s function %s(%s) {\n", acc, methodname, args); + Printf(output, "\t\tif (is_resource($%s) && get_resource_type($%s) === '_p%s') {\n", arg0, arg0, mangled_type); Printf(output, "\t\t\t$this->%s=$%s;\n", SWIG_PTR, arg0); Printf(output, "\t\t\treturn;\n"); Printf(output, "\t\t}\n"); } else { - Printf(output, "\tfunction %s(%s) {\n", methodname, args); + Printf(output, "\t%s function %s(%s) {\n", acc, methodname, args); } } else { Printf(output, "\tstatic function %s(%s) {\n", methodname, args); } - Delete(args); - args = NULL; - for (int i = 0; i < max_num_of_arguments; ++i) { - Delete(arg_names[i]); - } - free(arg_names); - arg_names = NULL; - - Printf(output, "%s", prepare); - if (newobject) { - Printf(output, "\t\t$this->%s=%s;\n", SWIG_PTR, invoke); + if (!newobject) + Printf(output, "%s", prepare); + if (constructor) { + if (!directorsEnabled() || !Swig_directorclass(n)) { + Printf(output, "\t\t$this->%s=%s;\n", SWIG_PTR, invoke); + } else { + Node *parent = Swig_methodclass(n); + String *classname = Swig_class_name(parent); + Printf(output, "\t\tif (get_class($this) === '%s') {\n", classname); + Printf(output, "\t\t\t$_this = null;\n"); + Printf(output, "\t\t} else {\n"); + Printf(output, "\t\t\t$_this = $this;\n"); + Printf(output, "\t\t}\n"); + if (!Len(prepare)) { + if (num_arguments > 1) { + Printf(output, "\t\t$this->%s=%s($_this, %s);\n", SWIG_PTR, iname, args); + } else { + Printf(output, "\t\t$this->%s=%s($_this);\n", SWIG_PTR, iname); + } + } + } + Printf(output, "%s", prepare); } else if (Cmp(d, "void") == 0) { if (Cmp(invoke, "$r") != 0) Printf(output, "\t\t%s;\n", invoke); @@ -1412,7 +1573,31 @@ public: if (Cmp(invoke, "$r") != 0) Printf(output, "\t\t$r=%s;\n", invoke); if (Len(ret_types) == 1) { - Printf(output, "\t\treturn is_resource($r) ? new %s%s($r) : $r;\n", prefix, Getattr(classLookup(d), "sym:name")); + /* If it has an abstract base, then we can't create a new + * base object. */ + int hasabstractbase = 0; + Node *bases = Getattr(Swig_methodclass(n), "bases"); + if (bases) { + Iterator i = First(bases); + while(i.item) { + if (Getattr(i.item, "abstract")) { + hasabstractbase = 1; + break; + } + i = Next(i); + } + } + if (newobject || !hasabstractbase) { + /* + * _p_Foo -> Foo, _p_ns__Bar -> Bar + * TODO: do this in a more elegant way + */ + Printf(output, "\t\tif (is_resource($r)) $class='%s'.substr(get_resource_type($r), (strpos(get_resource_type($r), '__') ? strpos(get_resource_type($r), '__') + 2 : 3));\n", prefix); + Printf(output, "\t\treturn is_resource($r) ? new $class($r) : $r;\n"); + } else { + Printf(output, "\t\t$this->%s = $r;\n", SWIG_PTR); + Printf(output, "\t\treturn $this;\n"); + } } else { Printf(output, "\t\tif (!is_resource($r)) return $r;\n"); Printf(output, "\t\tswitch (get_resource_type($r)) {\n"); @@ -1455,6 +1640,15 @@ public: Delete(prepare); Delete(invoke); free(arg_values); + + Delete(args); + args = NULL; + + for (int i = 0; i < max_num_of_arguments; ++i) { + Delete(arg_names[i]); + } + free(arg_names); + arg_names = NULL; } DelWrapper(f); @@ -1587,7 +1781,7 @@ public: * Pragma directive. * * %pragma(php) code="String" # Includes a string in the .php file - * %pragma(php) include="file.pl" # Includes a file in the .php file + * %pragma(php) include="file.php" # Includes a file in the .php file */ virtual int pragmaDirective(Node *n) { @@ -1698,53 +1892,57 @@ public: } Printf(s_phpclasses, "class %s%s ", prefix, shadow_classname); + String *baseclass = NULL; if (base.item) { - String *baseclass = Getattr(base.item, "sym:name"); + baseclass = Getattr(base.item, "sym:name"); if (!baseclass) baseclass = Getattr(base.item, "name"); Printf(s_phpclasses, "extends %s%s ", prefix, baseclass); + } else if (GetFlag(n, "feature:exceptionclass")) { + Append(s_phpclasses, "extends Exception "); } Printf(s_phpclasses, "{\n\tpublic $%s=null;\n", SWIG_PTR); + Printf(s_phpclasses, "\tprotected $%s=array();\n", SWIG_DATA); // Write property SET handlers ki = First(shadow_set_vars); if (ki.key) { // This class has setters. - // FIXME: just ignore setting an unknown property name for now. Printf(s_phpclasses, "\n\tfunction __set($var,$value) {\n"); // FIXME: tune this threshold... if (Len(shadow_set_vars) <= 2) { // Not many setters, so avoid call_user_func. while (ki.key) { key = ki.key; - Printf(s_phpclasses, "\t\tif ($var == '%s') return %s($this->%s,$value);\n", key, ki.item, SWIG_PTR); + Printf(s_phpclasses, "\t\tif ($var === '%s') return %s($this->%s,$value);\n", key, ki.item, SWIG_PTR); ki = Next(ki); } } else { Printf(s_phpclasses, "\t\t$func = '%s_'.$var.'_set';\n", shadow_classname); - Printf(s_phpclasses, "\t\tif (function_exists($func)) call_user_func($func,$this->%s,$value);\n", SWIG_PTR); + Printf(s_phpclasses, "\t\tif (function_exists($func)) return call_user_func($func,$this->%s,$value);\n", SWIG_PTR); + } + Printf(s_phpclasses, "\t\tif ($var === 'thisown') return swig_%s_alter_newobject($this->%s,$value);\n", module, SWIG_PTR); + Printf(s_phpclasses, "\t\telse $this->%s[$var] = $value;\n", SWIG_DATA); + if (baseclass) { + Printf(s_phpclasses, "\t\treturn %s%s::__set($var,$value);\n", prefix, baseclass); } Printf(s_phpclasses, "\t}\n"); /* Create __isset for PHP 5.1 and later; PHP 5.0 will just ignore it. */ Printf(s_phpclasses, "\n\tfunction __isset($var) {\n"); - // FIXME: tune this threshold, but it should probably be different to - // that for __set() and __get() as we don't need to call_user_func() - // here... - if (Len(shadow_set_vars) == 1) { - // Only one setter, so just check the name. - Printf(s_phpclasses, "\t\treturn "); - while (ki.key) { - key = ki.key; - Printf(s_phpclasses, "$var == '%s'", ki.key); - ki = Next(ki); - if (ki.key) Printf(s_phpclasses, " || "); - } - Printf(s_phpclasses, ";\n"); - } else { - Printf(s_phpclasses, "\t\treturn function_exists('%s_'.$var.'_set');\n", shadow_classname); - } + Printf(s_phpclasses, "\t\tif (function_exists('%s_'.$var.'_set')) return true;\n", shadow_classname); + Printf(s_phpclasses, "\t\tif ($var === 'thisown') return true;\n"); + Printf(s_phpclasses, "\t\telse return array_key_exists($var, $this->%s);\n", SWIG_DATA); + Printf(s_phpclasses, "\t}\n"); + } else { + Printf(s_phpclasses, "\n\tfunction __set($var,$value) {\n"); + Printf(s_phpclasses, "\t\tif ($var === 'thisown') return swig_%s_alter_newobject($this->%s,$value);\n", module, SWIG_PTR); + Printf(s_phpclasses, "\t\telse $this->%s[$var] = $value;\n", SWIG_DATA); + Printf(s_phpclasses, "\t}\n"); + Printf(s_phpclasses, "\n\tfunction __isset($var) {\n"); + Printf(s_phpclasses, "\t\tif ($var === 'thisown') return true;\n"); + Printf(s_phpclasses, "\t\telse return array_key_exists($var, $this->%s);\n", SWIG_DATA); Printf(s_phpclasses, "\t}\n"); } // Write property GET handlers @@ -1758,15 +1956,26 @@ public: // Not many getters, so avoid call_user_func. while (ki.key) { key = ki.key; - Printf(s_phpclasses, "\t\tif ($var == '%s') return %s($this->%s);\n", key, ki.item, SWIG_PTR); + Printf(s_phpclasses, "\t\tif ($var === '%s') return %s($this->%s);\n", key, ki.item, SWIG_PTR); ki = Next(ki); } } else { Printf(s_phpclasses, "\t\t$func = '%s_'.$var.'_get';\n", shadow_classname); Printf(s_phpclasses, "\t\tif (function_exists($func)) return call_user_func($func,$this->%s);\n", SWIG_PTR); } + Printf(s_phpclasses, "\t\telse if ($var === 'thisown') return swig_%s_get_newobject($this->%s);\n", module, SWIG_PTR); + Printf(s_phpclasses, "\t\telse if(array_key_exists($var, $this->%s)) return $this->%s[$var];\n", SWIG_DATA, SWIG_DATA); // Reading an unknown property name gives null in PHP. - Printf(s_phpclasses, "\t\treturn null;\n"); + if (base.item) { + Printf(s_phpclasses, "\t\treturn %s%s::__get($var);\n", prefix, baseclass); + } else { + Printf(s_phpclasses, "\t\treturn null;\n"); + } + Printf(s_phpclasses, "\t}\n"); + } else { + Printf(s_phpclasses, "\n\tfunction __get($var) {\n"); + Printf(s_phpclasses, "\t\tif ($var === 'thisown') return swig_example_get_newobject($this->%s);\n", SWIG_PTR); + Printf(s_phpclasses, "\t\telse return $this->%s[$var];\n", SWIG_DATA); Printf(s_phpclasses, "\t}\n"); } @@ -1960,7 +2169,47 @@ public: virtual int constructorHandler(Node *n) { constructors++; - wrapperType = constructor; + if (Swig_directorclass(n)) { + String *name = GetChar(Swig_methodclass(n), "name"); + String *ctype = GetChar(Swig_methodclass(n), "classtype"); + String *sname = GetChar(Swig_methodclass(n), "sym:name"); + String *args = NewStringEmpty(); + ParmList *p = Getattr(n, "parms"); + int i; + + for (i = 0; p; p = nextSibling(p), i++) { + if (i) { + Printf(args, ", "); + } + if (Strcmp(GetChar(p, "type"), SwigType_str(GetChar(p, "type"), 0))) { + SwigType *t = Getattr(p, "type"); + Printf(args, "%s", SwigType_rcaststr(t, 0)); + if (SwigType_isreference(t)) { + Append(args, "*"); + } + } + Printf(args, "arg%d", i+1); + } + + /* director ctor code is specific for each class */ + Delete(director_ctor_code); + director_ctor_code = NewStringEmpty(); + director_prot_ctor_code = NewStringEmpty(); + Printf(director_ctor_code, "if ( arg0->type == IS_NULL ) { /* not subclassed */\n"); + Printf(director_prot_ctor_code, "if ( arg0->type == IS_NULL ) { /* not subclassed */\n"); + Printf(director_ctor_code, " result = (%s *)new %s(%s);\n", ctype, ctype, args); + Printf(director_prot_ctor_code, " SWIG_PHP_Error(E_ERROR, \"accessing abstract class or protected constructor\");\n", name, name, args); + if (i) { + Insert(args, 0, ", "); + } + Printf(director_ctor_code, "} else {\n result = (%s *)new SwigDirector_%s(arg0%s);\n}\n", ctype, sname, args); + Printf(director_prot_ctor_code, "} else {\n result = (%s *)new SwigDirector_%s(arg0%s);\n}\n", ctype, sname, args); + Delete(args); + + wrapperType = directorconstructor; + } else { + wrapperType = constructor; + } Language::constructorHandler(n); wrapperType = standard; @@ -2013,6 +2262,9 @@ public: Append(f->code, actioncode); Delete(actioncode); + Append(f->code, "return;\n"); + Append(f->code, "fail:\n"); + Append(f->code, "zend_error_noreturn(SWIG_ErrorCode(),\"%s\",SWIG_ErrorMsg());\n"); Printf(f->code, "}\n"); Wrapper_print(f, s_wrappers); @@ -2031,6 +2283,397 @@ public: return SWIG_OK; } + int classDirectorInit(Node *n) { + String *declaration = Swig_director_declaration(n); + Printf(f_directors_h, "%s\n", declaration); + Printf(f_directors_h, "public:\n"); + Delete(declaration); + return Language::classDirectorInit(n); + } + + int classDirectorEnd(Node *n) { + Printf(f_directors_h, "};\n"); + return Language::classDirectorEnd(n); + } + + int classDirectorConstructor(Node *n) { + Node *parent = Getattr(n, "parentNode"); + String *decl = Getattr(n, "decl"); + String *supername = Swig_class_name(parent); + String *classname = NewStringEmpty(); + Printf(classname, "SwigDirector_%s", supername); + + /* insert self parameter */ + Parm *p; + ParmList *superparms = Getattr(n, "parms"); + ParmList *parms = CopyParmList(superparms); + String *type = NewString("zval"); + SwigType_add_pointer(type); + p = NewParm(type, NewString("self")); + set_nextSibling(p, parms); + parms = p; + + if (!Getattr(n, "defaultargs")) { + /* constructor */ + { + Wrapper *w = NewWrapper(); + String *call; + String *basetype = Getattr(parent, "classtype"); + String *target = Swig_method_decl(0, decl, classname, parms, 0, 0); + call = Swig_csuperclass_call(0, basetype, superparms); + Printf(w->def, "%s::%s: %s, Swig::Director(self) {", classname, target, call); + Append(w->def, "}"); + Delete(target); + Wrapper_print(w, f_directors); + Delete(call); + DelWrapper(w); + } + + /* constructor header */ + { + String *target = Swig_method_decl(0, decl, classname, parms, 0, 1); + Printf(f_directors_h, " %s;\n", target); + Delete(target); + } + } + return Language::classDirectorConstructor(n); + } + + int classDirectorDefaultConstructor(Node *n) { + return Language::classDirectorDefaultConstructor(n); + } + + int classDirectorMethod(Node *n, Node *parent, String *super) { + int is_void = 0; + int is_pointer = 0; + String *decl; + String *type; + String *name; + String *classname; + String *c_classname = Getattr(parent, "name"); + String *declaration; + ParmList *l; + Wrapper *w; + String *tm; + String *wrap_args = NewStringEmpty(); + String *return_type; + String *value = Getattr(n, "value"); + String *storage = Getattr(n, "storage"); + bool pure_virtual = false; + int status = SWIG_OK; + int idx; + bool ignored_method = GetFlag(n, "feature:ignore") ? true : false; + + if (Cmp(storage, "virtual") == 0) { + if (Cmp(value, "0") == 0) { + pure_virtual = true; + } + } + + classname = Getattr(parent, "sym:name"); + type = Getattr(n, "type"); + name = Getattr(n, "name"); + + w = NewWrapper(); + declaration = NewStringEmpty(); + + /* determine if the method returns a pointer */ + decl = Getattr(n, "decl"); + is_pointer = SwigType_ispointer_return(decl); + is_void = (Cmp(type, "void") == 0 && !is_pointer); + + /* form complete return type */ + return_type = Copy(type); + { + SwigType *t = Copy(decl); + SwigType *f = SwigType_pop_function(t); + SwigType_push(return_type, t); + Delete(f); + Delete(t); + } + + /* virtual method definition */ + l = Getattr(n, "parms"); + String *target; + String *pclassname = NewStringf("SwigDirector_%s", classname); + String *qualified_name = NewStringf("%s::%s", pclassname, name); + SwigType *rtype = Getattr(n, "conversion_operator") ? 0 : type; + target = Swig_method_decl(rtype, decl, qualified_name, l, 0, 0); + Printf(w->def, "%s", target); + Delete(qualified_name); + Delete(target); + /* header declaration */ + target = Swig_method_decl(rtype, decl, name, l, 0, 1); + Printf(declaration, " virtual %s", target); + Delete(target); + + // Get any exception classes in the throws typemap + ParmList *throw_parm_list = 0; + + if ((throw_parm_list = Getattr(n, "throws")) || Getattr(n, "throw")) { + Parm *p; + int gencomma = 0; + + Append(w->def, " throw("); + Append(declaration, " throw("); + + if (throw_parm_list) + Swig_typemap_attach_parms("throws", throw_parm_list, 0); + for (p = throw_parm_list; p; p = nextSibling(p)) { + if ((tm = Getattr(p, "tmap:throws"))) { + if (gencomma++) { + Append(w->def, ", "); + Append(declaration, ", "); + } + String *str = SwigType_str(Getattr(p, "type"), 0); + Append(w->def, str); + Append(declaration, str); + Delete(str); + } + } + + Append(w->def, ")"); + Append(declaration, ")"); + } + + Append(w->def, " {"); + Append(declaration, ";\n"); + + /* declare method return value + * if the return value is a reference or const reference, a specialized typemap must + * handle it, including declaration of c_result ($result). + */ + if (!is_void) { + if (!(ignored_method && !pure_virtual)) { + String *cres = SwigType_lstr(return_type, "c_result"); + Printf(w->code, "%s;\n", cres); + Delete(cres); + } + } + + if (ignored_method) { + if (!pure_virtual) { + if (!is_void) + Printf(w->code, "return "); + String *super_call = Swig_method_call(super, l); + Printf(w->code, "%s;\n", super_call); + Delete(super_call); + } else { + Printf(w->code, "Swig::DirectorPureVirtualException::raise(\"Attempted to invoke pure virtual method %s::%s\");\n", SwigType_namestr(c_classname), + SwigType_namestr(name)); + } + } else { + /* attach typemaps to arguments (C/C++ -> PHP) */ + String *parse_args = NewStringEmpty(); + + /* remove the wrapper 'w' since it was producing spurious temps */ + Swig_typemap_attach_parms("in", l, 0); + Swig_typemap_attach_parms("directorin", l, 0); + Swig_typemap_attach_parms("directorargout", l, w); + + Parm *p; + char source[256]; + + int outputs = 0; + if (!is_void) + outputs++; + + /* build argument list and type conversion string */ + idx = 0; + p = l; + int use_parse = 0; + while (p != NULL) { + if (checkAttribute(p, "tmap:in:numinputs", "0")) { + p = Getattr(p, "tmap:in:next"); + continue; + } + + if (Getattr(p, "tmap:directorargout") != 0) + outputs++; + + String *pname = Getattr(p, "name"); + String *ptype = Getattr(p, "type"); + + if ((tm = Getattr(p, "tmap:directorin")) != 0) { + String *parse = Getattr(p, "tmap:directorin:parse"); + if (!parse) { + sprintf(source, "obj%d", idx++); + String *input = NewStringf("&%s", source); + Replaceall(tm, "$input", input); + Delete(input); + Replaceall(tm, "$owner", "0"); + Printv(wrap_args, "zval ", source, ";\n", NIL); + Printf(wrap_args, "args[%d] = &%s;\n", idx - 1, source); + + Printv(wrap_args, tm, "\n", NIL); + Putc('O', parse_args); + } else { + use_parse = 1; + Append(parse_args, parse); + Replaceall(tm, "$input", pname); + Replaceall(tm, "$owner", "0"); + if (Len(tm) == 0) + Append(tm, pname); + } + p = Getattr(p, "tmap:directorin:next"); + continue; + } else if (Cmp(ptype, "void")) { + Swig_warning(WARN_TYPEMAP_DIRECTORIN_UNDEF, input_file, line_number, + "Unable to use type %s as a function argument in director method %s::%s (skipping method).\n", SwigType_str(ptype, 0), + SwigType_namestr(c_classname), SwigType_namestr(name)); + status = SWIG_NOWRAP; + break; + } + p = nextSibling(p); + } + Append(w->code, "int error;\n"); + if (!idx) { + Printf(w->code, "zval **args = NULL;\n", idx); + } else { + Printf(w->code, "zval *args[%d];\n", idx); + } + Append(w->code, "zval *result, funcname;\n"); + Append(w->code, "MAKE_STD_ZVAL(result);\n"); + Printf(w->code, "ZVAL_STRING(&funcname, (char *)\"%s\", 0);\n", name); + Append(w->code, "if (!swig_self) {\n"); + Append(w->code, " SWIG_PHP_Error(E_ERROR, \"this pointer is NULL\");"); + Append(w->code, "}\n\n"); + + /* wrap complex arguments to zvals */ + Printv(w->code, wrap_args, NIL); + + Append(w->code, "call_user_function(EG(function_table), (zval**)&swig_self, &funcname,\n"); + Printf(w->code, " result, %d, args TSRMLS_CC);\n", idx); + + /* exception handling */ + tm = Swig_typemap_lookup("director:except", n, "result", 0); + if (!tm) { + tm = Getattr(n, "feature:director:except"); + if (tm) + tm = Copy(tm); + } + if ((tm) && Len(tm) && (Strcmp(tm, "1") != 0)) { + Replaceall(tm, "$error", "error"); + Printv(w->code, Str(tm), "\n", NIL); + } + Delete(tm); + + /* marshal return value from PHP to C/C++ type */ + + String *cleanup = NewStringEmpty(); + String *outarg = NewStringEmpty(); + + idx = 0; + + /* marshal return value */ + if (!is_void) { + /* this seems really silly. the node's type excludes + * qualifier/pointer/reference markers, which have to be retrieved + * from the decl field to construct return_type. but the typemap + * lookup routine uses the node's type, so we have to swap in and + * out the correct type. it's not just me, similar silliness also + * occurs in Language::cDeclaration(). + */ + Setattr(n, "type", return_type); + tm = Swig_typemap_lookup("directorout", n, "result", w); + Setattr(n, "type", type); + if (tm != 0) { + Replaceall(tm, "$input", "&result"); + char temp[24]; + sprintf(temp, "%d", idx); + Replaceall(tm, "$argnum", temp); + + /* TODO check this */ + if (Getattr(n, "wrap:disown")) { + Replaceall(tm, "$disown", "SWIG_POINTER_DISOWN"); + } else { + Replaceall(tm, "$disown", "0"); + } + Replaceall(tm, "$result", "c_result"); + Printv(w->code, tm, "\n", NIL); + Delete(tm); + } else { + Swig_warning(WARN_TYPEMAP_DIRECTOROUT_UNDEF, input_file, line_number, + "Unable to use return type %s in director method %s::%s (skipping method).\n", SwigType_str(return_type, 0), SwigType_namestr(c_classname), + SwigType_namestr(name)); + status = SWIG_ERROR; + } + } + + /* marshal outputs */ + for (p = l; p;) { + if ((tm = Getattr(p, "tmap:directorargout")) != 0) { + Replaceall(tm, "$input", "result"); + Replaceall(tm, "$result", Getattr(p, "name")); + Printv(w->code, tm, "\n", NIL); + p = Getattr(p, "tmap:directorargout:next"); + } else { + p = nextSibling(p); + } + } + + Append(w->code, "FREE_ZVAL(result);\n"); + + Delete(parse_args); + Delete(cleanup); + Delete(outarg); + } + + if (!is_void) { + if (!(ignored_method && !pure_virtual)) { + String *rettype = SwigType_str(return_type, 0); + if (!SwigType_isreference(return_type)) { + Printf(w->code, "return (%s) c_result;\n", rettype); + } else { + Printf(w->code, "return (%s) *c_result;\n", rettype); + } + Delete(rettype); + } + } else { + Append(w->code, "return;\n"); + } + + Append(w->code, "fail:\n"); + Append(w->code, "zend_error_noreturn(SWIG_ErrorCode(),\"%s\",SWIG_ErrorMsg());\n"); + Append(w->code, "}\n"); + + // We expose protected methods via an extra public inline method which makes a straight call to the wrapped class' method + String *inline_extra_method = NewStringEmpty(); + if (dirprot_mode() && !is_public(n) && !pure_virtual) { + Printv(inline_extra_method, declaration, NIL); + String *extra_method_name = NewStringf("%sSwigPublic", name); + Replaceall(inline_extra_method, name, extra_method_name); + Replaceall(inline_extra_method, ";\n", " {\n "); + if (!is_void) + Printf(inline_extra_method, "return "); + String *methodcall = Swig_method_call(super, l); + Printv(inline_extra_method, methodcall, ";\n }\n", NIL); + Delete(methodcall); + Delete(extra_method_name); + } + + /* emit the director method */ + if (status == SWIG_OK) { + if (!Getattr(n, "defaultargs")) { + Wrapper_print(w, f_directors); + Printv(f_directors_h, declaration, NIL); + Printv(f_directors_h, inline_extra_method, NIL); + } + } + + /* clean up */ + Delete(wrap_args); + Delete(return_type); + Delete(pclassname); + DelWrapper(w); + return status; + } + + int classDirectorDisown(Node *n) { + /* avoid a warning */ + n = n; + return SWIG_OK; + } }; /* class PHP */ static PHP *maininstance = 0; |