summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES.current21
-rw-r--r--Doc/Manual/Php.html386
-rw-r--r--Examples/php/callback/Makefile22
-rw-r--r--Examples/php/callback/example.cxx4
-rw-r--r--Examples/php/callback/example.h22
-rw-r--r--Examples/php/callback/example.i13
-rw-r--r--Examples/php/callback/index.html19
-rw-r--r--Examples/php/callback/runme.php47
-rw-r--r--Examples/php/extend/Makefile22
-rw-r--r--Examples/php/extend/example.cxx4
-rw-r--r--Examples/php/extend/example.h56
-rw-r--r--Examples/php/extend/example.i15
-rw-r--r--Examples/php/extend/index.html19
-rw-r--r--Examples/php/extend/runme.php76
-rw-r--r--Examples/python/callback/example.h1
-rw-r--r--Examples/test-suite/director_detect.i2
-rw-r--r--Examples/test-suite/director_protected.i7
-rw-r--r--Examples/test-suite/director_stl.i4
-rw-r--r--Examples/test-suite/li_factory.i3
-rw-r--r--Examples/test-suite/php/Makefile.in2
-rw-r--r--Examples/test-suite/php/director_abstract_runme.php62
-rw-r--r--Examples/test-suite/php/director_basic_runme.php58
-rw-r--r--Examples/test-suite/php/director_classic_runme.php150
-rw-r--r--Examples/test-suite/php/director_default_runme.php20
-rw-r--r--Examples/test-suite/php/director_detect_runme.php55
-rw-r--r--Examples/test-suite/php/director_enum_runme.php25
-rw-r--r--Examples/test-suite/php/director_exception_runme.php78
-rw-r--r--Examples/test-suite/php/director_extend_runme.php29
-rw-r--r--Examples/test-suite/php/director_finalizer_runme.php61
-rw-r--r--Examples/test-suite/php/director_frob_runme.php19
-rw-r--r--Examples/test-suite/php/director_nested_runme.php75
-rw-r--r--Examples/test-suite/php/director_profile_runme.php53
-rw-r--r--Examples/test-suite/php/director_protected_runme.php54
-rw-r--r--Examples/test-suite/php/director_stl_runme.php60
-rw-r--r--Examples/test-suite/php/director_string_runme.php34
-rw-r--r--Examples/test-suite/php/director_thread_runme.php29
-rw-r--r--Examples/test-suite/php/director_unroll_runme.php29
-rw-r--r--Examples/test-suite/php/evil_diamond_prop_runme.php3
-rw-r--r--Examples/test-suite/php/li_carrays_runme.php6
-rw-r--r--Examples/test-suite/php/li_factory_runme.php22
-rw-r--r--Examples/test-suite/php/newobject1_runme.php20
-rw-r--r--Examples/test-suite/php/prefix_runme.php19
-rw-r--r--Examples/test-suite/php/tests.php7
-rw-r--r--Examples/test-suite/prefix.i14
-rw-r--r--Lib/php/director.swg198
-rw-r--r--Lib/php/factory.i109
-rw-r--r--Lib/php/php.swg53
-rw-r--r--Lib/php/phprun.swg30
-rw-r--r--Lib/php/std_string.i17
-rw-r--r--Lib/php/utils.i4
-rw-r--r--README1
-rw-r--r--Source/Modules/overload.cxx6
-rw-r--r--Source/Modules/php.cxx759
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>-&gt;_cPtr</tt>. This is exactly what happens without directors
+and is true even if directors are enabled for the particular class in
+question. When a class <i>derived</i> from a proxy class is created,
+however, SWIG then creates an instance of the corresponding C++ director
+class. The reason for this difference is that user-defined subclasses
+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>-&gt;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-&gt;thisown = 0;
+$c-&gt;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 &amp;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") %{
diff --git a/README b/README
index 179b3ff56..5fde83996 100644
--- a/README
+++ b/README
@@ -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;