Merge branch 'gsoc2017-php7-classes-via-c-api'
diff --git a/.gitignore b/.gitignore
index 0006e6e..b51da0f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -184,11 +184,8 @@
 
 # PHP
 Examples/test-suite/php/php_*.h
-Examples/test-suite/php/*.php
-!Examples/test-suite/php/*runme.php
-!Examples/test-suite/php/skel.php
 Examples/php/*/php_*.h
-Examples/php/*/example.php
+Examples/php/pragmas/example.php
 
 # Python
 #   Based on https://github.com/github/gitignore/blob/master/Python.gitignore
diff --git a/.travis.yml b/.travis.yml
index 7f78609..c11479f 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -141,22 +141,6 @@
       dist: xenial
     - compiler: gcc
       os: linux
-      env: SWIGLANG=php VER=7.0
-      dist: xenial
-    - compiler: gcc
-      os: linux
-      env: SWIGLANG=php VER=7.1
-      dist: xenial
-    - compiler: gcc
-      os: linux
-      env: SWIGLANG=php VER=7.2
-      dist: xenial
-    - compiler: gcc
-      os: linux
-      env: SWIGLANG=php VER=7.3
-      dist: xenial
-    - compiler: gcc
-      os: linux
       env: SWIGLANG=php VER=7.4
       dist: xenial
     - compiler: gcc
@@ -165,6 +149,30 @@
       dist: xenial
     - compiler: gcc
       os: linux
+      env: SWIGLANG=php VER=7.0 CONFIGOPTS=--enable-cpp11-testing CPPSTD=c++11
+      dist: bionic
+    - compiler: gcc
+      os: linux
+      env: SWIGLANG=php VER=7.1 CONFIGOPTS=--enable-cpp11-testing CPPSTD=c++11
+      dist: bionic
+    - compiler: gcc
+      os: linux
+      env: SWIGLANG=php VER=7.2 CONFIGOPTS=--enable-cpp11-testing CPPSTD=c++11
+      dist: bionic
+    - compiler: gcc
+      os: linux
+      env: SWIGLANG=php VER=7.3 CONFIGOPTS=--enable-cpp11-testing CPPSTD=c++11
+      dist: bionic
+    - compiler: gcc
+      os: linux
+      env: SWIGLANG=php VER=7.4 CONFIGOPTS=--enable-cpp11-testing CPPSTD=c++11
+      dist: bionic
+    - compiler: gcc
+      os: linux
+      env: SWIGLANG=php VER=8.0 CONFIGOPTS=--enable-cpp11-testing CPPSTD=c++11
+      dist: bionic
+    - compiler: gcc
+      os: linux
       env: SWIGLANG=python # 2.7
       dist: xenial
     - compiler: gcc
diff --git a/CHANGES.current b/CHANGES.current
index 004b562..8306b17 100644
--- a/CHANGES.current
+++ b/CHANGES.current
@@ -7,6 +7,23 @@
 Version 4.1.0 (in progress)
 ===========================
 
+2021-05-04: olly
+	    [PHP] #1982 #1457 https://sourceforge.net/p/swig/bugs/1339/
+	    SWIG now only use PHP's C API to implement its wrappers, and no
+	    longer generates PHP code to define classes.  The wrappers should
+	    be almost entirely compatible with those generated before, but
+	    faster and without some previously hard-to-fix bugs.
+
+	    The main notable difference is SWIG no longer generates a .php
+	    wrapper at all by default (only if %pragma(php) code=... or
+	    %pragma(php) include=... are specified in the interface file).
+	    This also means you need to load the module via extension=...
+	    in php.ini, rather than letting the dl() in the generated
+	    .php wrapper load it (but dl() has only worked for command-line
+	    PHP for some years now).
+
+	    *** POTENTIAL INCOMPATIBILITY ***
+
 2021-04-30: olly
 	    #1984 Remove support for $source and $target.
 	    These were officially deprecated in 2001, and attempts to use them have
diff --git a/Doc/Manual/Php.html b/Doc/Manual/Php.html
index 1752168..0e90006 100644
--- a/Doc/Manual/Php.html
+++ b/Doc/Manual/Php.html
@@ -84,16 +84,21 @@
 </pre></div>
 
 <p>
-This will produce 3 files example_wrap.c, php_example.h and
-example.php. The first file, <tt>example_wrap.c</tt> contains all of
+This will produce 2 files: example_wrap.c and php_example.h.
+The first file, <tt>example_wrap.c</tt> contains all of
 the C code needed to build a PHP extension. The second file,
 <tt>php_example.h</tt> contains the header information needed if
 you wish to statically link the extension into the php interpreter.
-The third file,
-<tt>example.php</tt> can be included by PHP scripts.  It attempts to
-dynamically load the extension and contains extra php code specified
-in the interface file.  If wrapping C++ code with PHP classes, it will
-also contain PHP class wrappers.
+</p>
+
+<p>
+If the interface file uses <tt>%pragma(php) include=</tt>... or
+<tt>%pragma(php) code=</tt>... then SWIG will also generate a third file,
+<tt>example.php</tt> to contain what these specify.  In SWIG &lt; 4.1.0,
+this third file was always generated as it defined the PHP classes, etc
+(but this is now done via C code in <tt>example_wrap.c</tt>) and also
+contained code to dynamically load the extension (but this used the
+PHP <tt>dl()</tt> function, which isn't recommended nowadays).
 </p>
 
 <p>
@@ -139,22 +144,30 @@
 
 <p>
 To test the extension from a PHP script, you first need to tell PHP to
-load it.  To do this, add a line like this to the <tt>[PHP]</tt> section of
-<tt>php.ini</tt>:
+load it.  Assuming you're using PHP 7.2 or higher, the recommended (and
+simplest!) way to do this is to copy it to PHP's default extension directory
+and add a line like this to the <tt>[PHP]</tt> section of <tt>php.ini</tt>:
 </p>
 
 <div class="code"><pre>
-        extension=/path/to/modulename.so
+        extension=modulename
 </pre></div>
 
 <p>
-If the module is in PHP's default extension directory, you can omit the path.
+PHP &lt; 7.2 doesn't support loading by just the module name, so you need
+to specify the filename of the module to be specified, which varies
+between platforms.  And for any PHP version, if the module is not in PHP's
+default extension directory, you also need to specify the path, for example:
 </p>
 
+<div class="code"><pre>
+	extension=/path/to/modulename.so
+</pre></div>
+
 <p>
-For some SAPIs (for example, the CLI SAPI) you can instead use the
-<a href="https://www.php.net/manual/en/function.dl.php">dl() function</a> to load
-an extension at run time, by adding a line like this to the start of each
+If you're using the PHP CLI SAPI it's possible (but not recommended) to use the
+<a href="https://www.php.net/manual/en/function.dl.php">dl() function</a> to
+load an extension at run time, by adding a line like this to the start of each
 PHP script which uses your extension:
 </p>
 
@@ -163,23 +176,11 @@
 </pre></div>
 
 <p>
-But note that <tt>dl()</tt> isn't supported when running PHP through a
-webserver - you'll need to use <tt>extension</tt> in <tt>php.ini</tt> as
-described above.
-</p>
-
-<p>
-The PHP module which SWIG generates will also attempt to do the <tt>dl()</tt>
-call for you if the extension isn't already loaded:
-</p>
-
-<div class="code"><pre>
-        include("example.php");
-</pre></div>
-
-<p>
-This PHP module also defines the PHP classes for the wrapped API, so you'll
-almost certainly want to include it anyway.
+But to do this portably you need to take into account that pathnames and the
+filename extension vary by platform, and for security reasons PHP no longer
+supports <tt>dl()</tt> when running PHP through a webserver.  Overall it's
+better to instead use <tt>extension</tt> in <tt>php.ini</tt> as described
+above.
 </p>
 
 <H2><a name="Php_nn2">32.2 Basic PHP interface</a></H2>
@@ -219,24 +220,19 @@
 </p>
 
 <div class="code"><pre>
-include("example.php");
-
 echo "PI = " . PI . "\n";
-
 echo "E = " . E . "\n";
-
 </pre>
 </div>
 
 <p>
-There's one peculiarity of how constants work in PHP which it is useful
-to note (this is not specific to SWIG though) - if you try to use an undeclared
-constant, PHP will emit a warning (or a notice in PHP 7.1 and earlier) and then
-expand the constant to a string version of the constant's name.  Unfortunately
-it is easy to miss the warning message if you're using PHP in a webserver as
-it will probably end up in error.log or similar.  Apparently this will throw
-an Error in a future version of PHP, but until then it's something to be
-aware of.
+There's one peculiarity of how constants work in PHP prior to PHP 8
+which it is useful to note (this is not specific to SWIG though) - if you try
+to use an undeclared constant, PHP will emit a warning (or a notice in PHP 7.1
+and earlier) and then expand the constant to a string version of the constant's
+name.  Unfortunately it is easy to miss the warning message if you're using PHP
+in a webserver as it will probably end up in error.log or similar.  PHP 8.0
+made this an error.
 </p>
 
 <p>
@@ -256,8 +252,6 @@
 
 <div class="code">
 <pre>
-include("example.php");
-
 if(EASY_TO_MISPEL) {
   ...
 } else {
@@ -298,7 +292,6 @@
 </p>
 
 <div class="code"><pre>
-include("example.php");
 print seki_get();
 seki_set( seki_get() * 2); # The C variable is now 4.
 print seki_get();
@@ -306,8 +299,10 @@
 
 <p>
 SWIG supports global variables of all C datatypes including pointers
-and complex objects.  Additional types can be supported by using the
-<tt>varinit</tt> typemap.
+and complex objects.  To support additional types, you just need to
+supply the standard <tt>in</tt> and <tt>out</tt> typemaps, which get
+used because of the wrapping as <tt>_get()</tt> and <tt>_set()</tt>
+functions.
 </p>
 
 <p>
@@ -342,7 +337,6 @@
 </p>
 
 <div class="code"><pre>
-include("example.php");
 $a = foo(2);
 $b = bar(3.5, -1.5);
 $c = bar(3.5);  # Use default argument for 2nd parameter
@@ -467,8 +461,6 @@
 <div class="code"><pre>
 &lt;?php
 
-include("example.php");
-
 $in1=copy_intp(3);
 $in2=copy_intp(5);
 $result=new_intp();
@@ -500,8 +492,6 @@
 <div class="code"><pre>
 &lt;?php
 
-include("example.php");
-
 $in1 = 3;
 $in2 = 5;
 $result= add($in1, $in2);  # Note using variables for the input is unnecessary.
@@ -537,8 +527,6 @@
 <div class="code"><pre>
 &lt;?php
 
-include("example.php");
-
 $in1 = 3;
 $in2 = 5;
 $result = 0;
@@ -602,7 +590,6 @@
 
 <div class="code"><pre>
 &lt;?php
-  require "vector.php";
 
   $v = new Vector();
   $v-&gt;x = 3;
@@ -717,8 +704,6 @@
 </p>
 
 <div class="code"><pre>
-include("example.php");
-
 echo "There have now been " . Ko::threats() . " threats\n";
 
 </pre></div>
@@ -752,7 +737,6 @@
 
 would be executed in PHP as,
 <div class="code"><pre>
-include("example.php");
 Ko::threats();
 </pre></div>
 
@@ -779,9 +763,8 @@
 
 
 <p>
-To place PHP code in the generated "example.php" file one can use the
-<b>code</b> pragma. The code is inserted after loading the shared
-object.
+You can get SWIG to generate an "example.php" file by specifying
+the code to put in it using the <b>code</b> pragma.
 </p>
 
 <div class="code"><pre>
@@ -985,8 +968,6 @@
 
 <div class="targetlang">
 <pre>
-require("mymodule.php");
-
 class MyFoo extends Foo {
   function one() {
     print "one from php\n";
@@ -1008,8 +989,8 @@
 <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.
+PHP object.  Indeed, this is quite similar to <tt>struct swig_object_wrapper</tt>
+which is used to implement the PHP proxy classes.
 </p>
 
 <p>
@@ -1064,12 +1045,12 @@
 <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
+PHP, SWIG creates an instance of the original C++ class and stores it
+in the <tt>struct swig_object_wrapper</tt>. This is true whether or not
+directors are enabled for the particular class in question. However
+when a class <i>derived</i> from a proxy class is created, SWIG instead
+creates an instance of the corresponding C++ director class.
+The reason for this difference is that user-defined subclasses
 may override or extend methods of the original class, so the director
 class is needed to route calls to these methods correctly. For
 unmodified proxy classes, all methods are ultimately implemented in C++
@@ -1157,7 +1138,12 @@
 <div class="code">
 <pre>
 %feature("director:except") {
-  if ($error == FAILURE) {
+#if SWIG_VERSION &gt;= 0x040100
+  if ($error != NULL)
+#else
+  if ($error == FAILURE)
+#endif
+  {
     throw Swig::DirectorMethodException();
   }
 }
@@ -1165,6 +1151,20 @@
 </div>
 
 <p>
+If you only need to support SWIG >= 4.1.0, you can just use the
+<tt>($error != NULL)</tt> condition.
+</p>
+
+<p>
+In SWIG 4.1.0, <tt>$error</tt> was changed in the SWIG/PHP director
+implementation to make it work more like how it does for other languages.
+Previously, <tt>$error</tt> didn't actually indicate an exception, but instead
+was only set to <tt>FAILURE</tt> if there was a problem calling the PHP method.
+Now <tt>$error</tt> indicates if the PHP method threw a PHP exception, and
+directorout typemaps for PHP no longer need to be gated by <tt>if (EG(exception))</tt>.
+</p>
+
+<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.
diff --git a/Examples/Makefile.in b/Examples/Makefile.in
index 34296c5..16973c9 100644
--- a/Examples/Makefile.in
+++ b/Examples/Makefile.in
@@ -1053,6 +1053,7 @@
 PHP_INCLUDE = @PHPINC@
 PHP_SO      = @PHP_SO@
 PHP_SCRIPT  = $(SRCDIR)$(RUNME).php
+PHP_EXTENSION = example$(PHP_SO)
 
 # -------------------------------------------------------------------
 # Build a PHP dynamically loadable module (C)
@@ -1077,7 +1078,7 @@
 # -----------------------------------------------------------------
 
 php_run:
-	$(RUNTOOL) $(PHP) -n -d extension_dir=. -d display_errors=stderr $(PHP_SCRIPT) $(RUNPIPE)
+	$(RUNTOOL) $(PHP) -n -d extension_dir=. -d extension=$(PHP_EXTENSION) -d display_errors=stderr -r 'set_error_handler(function($$n,$$s,$$f,$$l){if($$f!==Null){print$$f;if($$l!==Null)print":$$l";print": ";}print"$$s\n";exit(1);});include($$argv[1]);' $(PHP_SCRIPT) $(RUNPIPE)
 
 # -----------------------------------------------------------------
 # Version display
diff --git a/Examples/php/callback/runme.php b/Examples/php/callback/runme.php
index fe4cd8b..e709320 100644
--- a/Examples/php/callback/runme.php
+++ b/Examples/php/callback/runme.php
@@ -2,8 +2,6 @@
 
 # This file illustrates the cross language polymorphism using directors.
 
-require("example.php");
-
 # Class, which overwrites Callback::run().
 
 class PhpCallback extends Callback {
diff --git a/Examples/php/class/runme.php b/Examples/php/class/runme.php
index 88b4cfc..0f66769 100644
--- a/Examples/php/class/runme.php
+++ b/Examples/php/class/runme.php
@@ -2,8 +2,6 @@
 
 # This example illustrates how member variables are wrapped.
 
-require("example.php");
-
 # ----- Object creation -----
 
 print "Creating some objects:\n";
diff --git a/Examples/php/constants/runme.php b/Examples/php/constants/runme.php
index ef92382..e561626 100644
--- a/Examples/php/constants/runme.php
+++ b/Examples/php/constants/runme.php
@@ -1,7 +1,5 @@
 <?php
 
-require "example.php";
-
 print "ICONST  = " . ICONST . " (should be 42)\n";
 print "FCONST  = " . FCONST . " (should be 2.1828)\n";
 print "CCONST  = " . CCONST . " (should be 'x')\n";
diff --git a/Examples/php/cpointer/runme.php b/Examples/php/cpointer/runme.php
index 3a0f19e..f552ed7 100644
--- a/Examples/php/cpointer/runme.php
+++ b/Examples/php/cpointer/runme.php
@@ -1,7 +1,5 @@
 <?php
 
-	require "example.php";
-
 	# First create some objects using the pointer library.
 
 	print "Testing the pointer library\n";
diff --git a/Examples/php/disown/runme.php b/Examples/php/disown/runme.php
index 9bdaecb..e847dc7 100644
--- a/Examples/php/disown/runme.php
+++ b/Examples/php/disown/runme.php
@@ -4,8 +4,6 @@
 # created by SWIG.  In this case, all of our C++ classes
 # get converted into function calls.
 
-require("example.php");
-
 # ----- Object creation -----
 
 print "Creating some objects:\n";
diff --git a/Examples/php/enum/runme.php b/Examples/php/enum/runme.php
index 23d4fbc..bf5ba88 100644
--- a/Examples/php/enum/runme.php
+++ b/Examples/php/enum/runme.php
@@ -1,7 +1,5 @@
 <?php
 
-require "example.php";
-
 # ----- Object creation -----
 
 # Print out the value of some enums
diff --git a/Examples/php/extend/runme.php b/Examples/php/extend/runme.php
index 94a59e6..93eedee 100644
--- a/Examples/php/extend/runme.php
+++ b/Examples/php/extend/runme.php
@@ -2,8 +2,6 @@
 
 # This file illustrates the cross language polymorphism using directors.
 
-require("example.php");
-
 # CEO class, which overrides Employee::getPosition().
 
 class CEO extends Manager {
diff --git a/Examples/php/funcptr/runme.php b/Examples/php/funcptr/runme.php
index 4590a5c..d76a19b 100644
--- a/Examples/php/funcptr/runme.php
+++ b/Examples/php/funcptr/runme.php
@@ -1,7 +1,5 @@
 <?php
 
-require "example.php";
-
 $a = 37;
 $b = 42;
 
diff --git a/Examples/php/overloading/runme.php b/Examples/php/overloading/runme.php
index ffa7e11..d4df899 100644
--- a/Examples/php/overloading/runme.php
+++ b/Examples/php/overloading/runme.php
@@ -4,8 +4,6 @@
 # created by SWIG.  In this case, all of our C++ classes
 # get converted into function calls.
 
-include("example.php");
-
 # ----- Object creation -----
 
 print "Creating some objects:\n";
diff --git a/Examples/php/pointer/runme.php b/Examples/php/pointer/runme.php
index e0ed35b..8dc3ab8 100644
--- a/Examples/php/pointer/runme.php
+++ b/Examples/php/pointer/runme.php
@@ -1,7 +1,5 @@
 <?php
 
-	require "example.php";
-
 	# First create some objects using the pointer library.
 
 	print "Testing the pointer library\n";
diff --git a/Examples/php/proxy/runme.php b/Examples/php/proxy/runme.php
index 8a29265..0f156b6 100644
--- a/Examples/php/proxy/runme.php
+++ b/Examples/php/proxy/runme.php
@@ -4,8 +4,6 @@
 # created by SWIG.  In this case, all of our C++ classes
 # get converted into function calls.
 
-include("example.php");
-
 # ----- Object creation -----
 
 print "Creating some objects:\n";
diff --git a/Examples/php/reference/runme.php b/Examples/php/reference/runme.php
index 722acdb..5c7fc9a 100644
--- a/Examples/php/reference/runme.php
+++ b/Examples/php/reference/runme.php
@@ -2,8 +2,6 @@
 
 # This file illustrates the manipulation of C++ references in PHP.
 
-require "example.php";
-
 # ----- Object creation -----
 
 print "Creating some objects:\n";
diff --git a/Examples/php/simple/runme.php b/Examples/php/simple/runme.php
index 7cb84dc..2795f32 100644
--- a/Examples/php/simple/runme.php
+++ b/Examples/php/simple/runme.php
@@ -1,7 +1,5 @@
 <?php
 
-require "example.php";
-
 # Call our gcd() function
 
 $x = "42 aaa";
diff --git a/Examples/php/sync/example.cxx b/Examples/php/sync/example.cxx
index 0942279..2001a5b 100644
--- a/Examples/php/sync/example.cxx
+++ b/Examples/php/sync/example.cxx
@@ -1,13 +1,19 @@
 #include "example.h"
-#include <stdio.h>
+#include <iostream>
 
 int x = 42;
-char *s = (char *)"Test";
+std::string s = "Test";
 
-void Sync::printer(void) {
+void Sync::printer() {
+    std::cout << "The value of global s is " << ::s << '\n';
+    std::cout << "The value of global x is " << ::x << '\n';
+    std::cout << "The value of class s is " << this->s << '\n';
+    std::cout << "The value of class x is " << this->x << '\n';
+}
 
-	printf("The value of global s is %s\n", s);
-	printf("The value of global x is %d\n", x);
-	printf("The value of class s is %s\n", s);
-	printf("The value of class x is %d\n", x);
+void Sync::all_change() {
+    ::s = "global change";
+    ++::x;
+    this->s = "local change";
+    ++this->x;
 }
diff --git a/Examples/php/sync/example.h b/Examples/php/sync/example.h
index d67ec21..381473f 100644
--- a/Examples/php/sync/example.h
+++ b/Examples/php/sync/example.h
@@ -1,9 +1,14 @@
-extern char *s;
+#include <string>
+
+extern std::string s;
 extern int x;
 
 class Sync {
-	public:
-		int x;
-		char *s;
-		void printer(void);
+  public:
+    int x;
+    std::string s;
+    void printer();
+    void all_change();
+
+    Sync() : x(0) { }
 };
diff --git a/Examples/php/sync/example.i b/Examples/php/sync/example.i
index 17ff87c..bc10e0f 100644
--- a/Examples/php/sync/example.i
+++ b/Examples/php/sync/example.i
@@ -1,5 +1,7 @@
 %module example
 
+%include <std_string.i>
+
 %{
 #include "example.h"
 %}
diff --git a/Examples/php/sync/runme.php b/Examples/php/sync/runme.php
index cdda2f2..f01a9b3 100644
--- a/Examples/php/sync/runme.php
+++ b/Examples/php/sync/runme.php
@@ -1,12 +1,29 @@
 <?
 
-// Load module and PHP classes.
-include("example.php");
+echo "PHP reading globals: string is '", s_get(), "' and value is ", x_get(), "\n";
 
-echo "Got new object\n";
-echo "Got string $s and value $x \n";
-
-$s = new Sync();
+$o = new Sync();
 echo "Got new object\n";
 
-$s->printer();
+echo "PHP reading object: string is '", $o->s, "' and value is ", $o->x, "\n";
+
+$o->printer();
+
+example::s_set("global string");
+example::x_set(42);
+
+$o->s = "object string";
+$o->x = 1234;
+
+echo "PHP reading globals: string is '", example::s_get(), "' and int is ", example::x_get(), "\n";
+echo "PHP reading object: string is '", $o->s, "' and int is ", $o->x, "\n";
+
+$o->printer();
+
+echo "Calling all_change() method\n";
+$o->all_change();
+
+echo "PHP reading globals: string is '", example::s_get(), "' and int is ", example::x_get(), "\n";
+echo "PHP reading object: string is '", $o->s, "' and int is ", $o->x, "\n";
+
+$o->printer();
diff --git a/Examples/php/value/runme.php b/Examples/php/value/runme.php
index 977b99c..754f942 100644
--- a/Examples/php/value/runme.php
+++ b/Examples/php/value/runme.php
@@ -1,8 +1,5 @@
 <?php
 
-	require "example.php";
-
-
 	$v = new Vector();
         $v->x = 1.0;
         $v->y = 2.0;
@@ -34,6 +31,6 @@
 
 	echo "\nNow I'm going to clean up the return result\n";
 
-#	free($r);
+	unset($r);
 
 	echo "Good\n";
diff --git a/Examples/php/variables/runme.php b/Examples/php/variables/runme.php
index 126b542..a14fede 100644
--- a/Examples/php/variables/runme.php
+++ b/Examples/php/variables/runme.php
@@ -1,6 +1,5 @@
 <?php
 
-	require "example.php";
 	echo "\nVariables (values printed from C)\n";
 
 	print_vars();
diff --git a/Examples/test-suite/allowexcept.i b/Examples/test-suite/allowexcept.i
index c901295..229e65c 100644
--- a/Examples/test-suite/allowexcept.i
+++ b/Examples/test-suite/allowexcept.i
@@ -26,17 +26,6 @@
 struct XYZ {
 };
 
-// The operator& trick doesn't work for SWIG/PHP because the generated code
-// takes the address of the variable in the code in the "vinit" section.
-#ifdef SWIGPHP
-%{
-struct XYZ {
-  void foo() {}
-private:
-  XYZ& operator=(const XYZ& other); // prevent assignment used in normally generated set method
-};
-%}
-#else
 %{
 struct XYZ {
   void foo() {}
@@ -45,7 +34,6 @@
   XYZ* operator&(); // prevent dereferencing used in normally generated get method
 };
 %}
-#endif
 #if defined(SWIGUTL)
 %exception {
   /* 
diff --git a/Examples/test-suite/director_exception.i b/Examples/test-suite/director_exception.i
index 71366be..9ff7f38 100644
--- a/Examples/test-suite/director_exception.i
+++ b/Examples/test-suite/director_exception.i
@@ -18,22 +18,7 @@
 
 %include "std_string.i"
 
-#ifdef SWIGPHP
-
-%feature("director:except") {
-  if ($error == FAILURE) {
-    Swig::DirectorMethodException::raise("$symname");
-  }
-}
-
-%exception {
-  try { $action }
-  catch (Swig::DirectorException &) { SWIG_fail; }
-}
-
-#endif
-
-#ifdef SWIGPYTHON
+#if defined SWIGPHP || defined SWIGPYTHON
 
 %feature("director:except") {
   if ($error != NULL) {
diff --git a/Examples/test-suite/director_protected.i b/Examples/test-suite/director_protected.i
index 0299b23..122addb 100644
--- a/Examples/test-suite/director_protected.i
+++ b/Examples/test-suite/director_protected.i
@@ -11,13 +11,6 @@
 
 %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 46946e5..cbcb4ba 100644
--- a/Examples/test-suite/director_stl.i
+++ b/Examples/test-suite/director_stl.i
@@ -17,11 +17,7 @@
 %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/global_vars.i b/Examples/test-suite/global_vars.i
index d562d1e..46133fe 100644
--- a/Examples/test-suite/global_vars.i
+++ b/Examples/test-suite/global_vars.i
@@ -33,4 +33,8 @@
     b = "string b";
     x = 1234;
   }
+
+  int read_x() { return x; }
+
+  std::string read_b() { return b; }
 %}
diff --git a/Examples/test-suite/import_nomodule.i b/Examples/test-suite/import_nomodule.i
index 60ef7e0..48e1195 100644
--- a/Examples/test-suite/import_nomodule.i
+++ b/Examples/test-suite/import_nomodule.i
@@ -8,7 +8,7 @@
 
 %import "import_nomodule.h"
 
-#if !defined(SWIGJAVA) && !defined(SWIGRUBY) && !defined(SWIGCSHARP) && !defined(SWIGD) && !defined(SWIGPYTHON_BUILTIN)
+#if !defined(SWIGJAVA) && !defined(SWIGRUBY) && !defined(SWIGCSHARP) && !defined(SWIGD) && !defined(SWIGPYTHON_BUILTIN) && !defined(SWIGPHP)
 
 /**
  * The proxy class does not have Bar derived from Foo, yet an instance of Bar
@@ -16,8 +16,8 @@
  * language modules).
  * 
  * This violation of the type system is not possible in Java, C# and D due to
- * static type checking. It's also not (currently) possible in Ruby, but this may
- * be fixable (needs more investigation).
+ * static type checking. It's also not (currently) possible in PHP or Ruby, but
+ * this may be fixable (needs more investigation).
  */
 
 %newobject create_Foo;
diff --git a/Examples/test-suite/php/Makefile.in b/Examples/test-suite/php/Makefile.in
index 003e283..b64918d 100644
--- a/Examples/test-suite/php/Makefile.in
+++ b/Examples/test-suite/php/Makefile.in
@@ -71,9 +71,9 @@
 # found, runs testcase.php, except for multicpptests.
 run_testcase = \
 	if [ -f $(SCRIPTDIR)/$(SCRIPTPREFIX)$*$(SCRIPTSUFFIX) ]; then \
-	  $(MAKE) -f $(top_builddir)/$(EXAMPLES)/Makefile PHP_SCRIPT=$(SCRIPTDIR)/$(SCRIPTPREFIX)$*$(SCRIPTSUFFIX) RUNTOOL='$(RUNTOOL)' php_run; \
+	  $(MAKE) -f $(top_builddir)/$(EXAMPLES)/Makefile PHP_EXTENSION=$(TARGETPREFIX)$*@PHP_SO@ PHP_SCRIPT=$(SCRIPTDIR)/$(SCRIPTPREFIX)$*$(SCRIPTSUFFIX) RUNTOOL='$(RUNTOOL)' php_run; \
 	elif [ -f $(SCRIPTDIR)/$(SCRIPTPREFIX)$*.php -a ! -f $(top_srcdir)/$(EXAMPLES)/$(TEST_SUITE)/$*.list ]; then \
-	  $(MAKE) -f $(top_builddir)/$(EXAMPLES)/Makefile PHP_SCRIPT=$(SCRIPTDIR)/$(SCRIPTPREFIX)$*.php RUNTOOL='$(RUNTOOL)' php_run; \
+	  $(MAKE) -f $(top_builddir)/$(EXAMPLES)/Makefile PHP_EXTENSION=$(TARGETPREFIX)$*@PHP_SO@ PHP_SCRIPT=$(SCRIPTDIR)/$(SCRIPTPREFIX)$*.php RUNTOOL='$(RUNTOOL)' php_run; \
 	fi
 
 # Clean: remove the generated .php file
diff --git a/Examples/test-suite/php/abstract_inherit_ok_runme.php b/Examples/test-suite/php/abstract_inherit_ok_runme.php
index e5000e0..6cfea23 100644
--- a/Examples/test-suite/php/abstract_inherit_ok_runme.php
+++ b/Examples/test-suite/php/abstract_inherit_ok_runme.php
@@ -1,7 +1,6 @@
 <?php
 
 require "tests.php";
-require "abstract_inherit_ok.php";
 
 check::classes(array('Foo','Spam'));
 $spam=new Spam();
diff --git a/Examples/test-suite/php/abstract_inherit_runme.php b/Examples/test-suite/php/abstract_inherit_runme.php
index a5ecfbc..6a0180d 100644
--- a/Examples/test-suite/php/abstract_inherit_runme.php
+++ b/Examples/test-suite/php/abstract_inherit_runme.php
@@ -1,7 +1,6 @@
 <?php
 
 require "tests.php";
-require "abstract_inherit.php";
 
 check::classes(array('Foo','Bar','Spam','NRFilter_i','NRRCFilter_i','NRRCFilterpro_i','NRRCFilterpri_i'));
 // This constructor attempt should fail as there isn't one
diff --git a/Examples/test-suite/php/add_link_runme.php b/Examples/test-suite/php/add_link_runme.php
index 92c76ac..073dee3 100644
--- a/Examples/test-suite/php/add_link_runme.php
+++ b/Examples/test-suite/php/add_link_runme.php
@@ -1,10 +1,9 @@
 <?php
 
 require "tests.php";
-require "add_link.php";
 
-// No new functions, except the flat functions
-check::functions(array('new_foo','foo_blah'));
+// No new functions
+check::functions(array());
 
 check::classes(array('Foo'));
 
diff --git a/Examples/test-suite/php/argout_runme.php b/Examples/test-suite/php/argout_runme.php
index f9f8f93..d233cf0 100644
--- a/Examples/test-suite/php/argout_runme.php
+++ b/Examples/test-suite/php/argout_runme.php
@@ -1,7 +1,6 @@
 <?php
 
 require "tests.php";
-require "argout.php";
 
 check::functions(array('incp','incr','inctr','new_intp','copy_intp','delete_intp','intp_assign','intp_value','voidhandle','handle'));
 
@@ -22,16 +21,10 @@
 check::equal(5,intp_value($tr),"5==$tr");
 
 # Check the voidhandle call, first with null
-unset($handle);
-# FIXME: Call-time pass-by-reference has been deprecated for ages, and was
-# removed in PHP 5.4.  We need to rework 
-#voidhandle(&$handle);
-#check::resource($handle,"_p_void",'$handle is not _p_void');
-#$handledata=handle($handle);
-#check::equal($handledata,"Here it is","\$handledata != \"Here it is\"");
-
 $handle=NULL;
 voidhandle($handle);
-check::isnull($handle,'$handle not null');
+check::equal(get_class($handle),"SWIG\\_p_void",'$handle is not _p_void');
+$handledata=handle($handle);
+check::equal($handledata,"Here it is","\$handledata != \"Here it is\"");
 
 check::done();
diff --git a/Examples/test-suite/php/arrayptr_runme.php b/Examples/test-suite/php/arrayptr_runme.php
deleted file mode 100644
index 0af1415..0000000
--- a/Examples/test-suite/php/arrayptr_runme.php
+++ /dev/null
@@ -1,13 +0,0 @@
-<?php
-
-require "tests.php";
-require "arrayptr.php";
-
-// No new functions
-check::functions(array('foo'));
-// No new classes
-check::classes(array());
-// now new vars
-check::globals(array());
-
-check::done();
diff --git a/Examples/test-suite/php/arrays_global_runme.php b/Examples/test-suite/php/arrays_global_runme.php
index 03ae02a..6a97db9 100644
--- a/Examples/test-suite/php/arrays_global_runme.php
+++ b/Examples/test-suite/php/arrays_global_runme.php
@@ -1,11 +1,11 @@
 <?php
 
 require "tests.php";
-require "arrays_global.php";
 
-check::functions(array('test_a','test_b','new_simplestruct','new_material'));
+check::functions(array('test_a','test_b'));
 check::classes(array('arrays_global','SimpleStruct','Material'));
-check::globals(array('array_c','array_sc','array_uc','array_s','array_us','array_i','array_ui','array_l','array_ul','array_ll','array_f','array_d','array_struct','array_structpointers','array_ipointers','array_enum','array_enumpointers','array_const_i','beginstring_fix44a','beginstring_fix44b','beginstring_fix44c','beginstring_fix44d','beginstring_fix44e','beginstring_fix44f','chitmat','hitmat_val','hitmat','simplestruct_double_field'));
+check::globals(array('array_c','array_sc','array_uc','array_s','array_us','array_i','array_ui','array_l','array_ul','array_ll','array_f','array_d','array_struct','array_structpointers','array_ipointers','array_enum','array_enumpointers','array_const_i','BeginString_FIX44a','BeginString_FIX44b','BeginString_FIX44c','BeginString_FIX44d','BeginString_FIX44e','BeginString_FIX44f','chitMat','hitMat_val','hitMat'));
+
 // The size of array_c is 2, but the last byte is \0, so we can only store a
 // single byte string in it.
 check::set("array_c","Z");
diff --git a/Examples/test-suite/php/arrays_global_twodim_runme.php b/Examples/test-suite/php/arrays_global_twodim_runme.php
index a664889..e6fbf52 100644
--- a/Examples/test-suite/php/arrays_global_twodim_runme.php
+++ b/Examples/test-suite/php/arrays_global_twodim_runme.php
@@ -1,11 +1,11 @@
 <?php
 
 require "tests.php";
-require "arrays_global_twodim.php";
 
-check::functions(array('fn_taking_arrays','get_2d_array','new_simplestruct','new_material'));
+check::functions(array('fn_taking_arrays','get_2d_array',));
 check::classes(array('arrays_global_twodim','SimpleStruct','Material'));
-check::globals(array('array_c','array_sc','array_uc','array_s','array_us','array_i','array_ui','array_l','array_ul','array_ll','array_f','array_d','array_struct','array_structpointers','array_ipointers','array_enum','array_enumpointers','array_const_i','chitmat','hitmat_val','hitmat','simplestruct_double_field'));
+check::globals(array('array_c','array_sc','array_uc','array_s','array_us','array_i','array_ui','array_l','array_ul','array_ll','array_f','array_d','array_struct','array_structpointers','array_ipointers','array_enum','array_enumpointers','array_const_i','chitMat','hitMat_val','hitMat'));
+
 $a1=array(10,11,12,13);
 $a2=array(14,15,16,17);
 $a=array($a1,$a2);
diff --git a/Examples/test-suite/php/arrays_runme.php b/Examples/test-suite/php/arrays_runme.php
index 8ad3a1a..eb06fbf 100644
--- a/Examples/test-suite/php/arrays_runme.php
+++ b/Examples/test-suite/php/arrays_runme.php
@@ -1,10 +1,10 @@
 <?php
+
 require "tests.php";
-require "arrays.php";
 
 check::functions(array('fn_taking_arrays','newintpointer','setintfrompointer','getintfrompointer','array_pointer_func'));
 check::classes(array('arrays','SimpleStruct','ArrayStruct','CartPoseData_t'));
-check::globals(array('simplestruct_double_field','arraystruct_array_c','arraystruct_array_sc','arraystruct_array_uc','arraystruct_array_s','arraystruct_array_us','arraystruct_array_i','arraystruct_array_ui','arraystruct_array_l','arraystruct_array_ul','arraystruct_array_ll','arraystruct_array_f','arraystruct_array_d','arraystruct_array_struct','arraystruct_array_structpointers','arraystruct_array_ipointers','arraystruct_array_enum','arraystruct_array_enumpointers','arraystruct_array_const_i','cartposedata_t_p'));
+check::globals(array());
 
 $ss=new simplestruct();
 check::classname('simplestruct',$ss);
diff --git a/Examples/test-suite/php/arrays_scope_runme.php b/Examples/test-suite/php/arrays_scope_runme.php
index 34976ec..f18037c 100644
--- a/Examples/test-suite/php/arrays_scope_runme.php
+++ b/Examples/test-suite/php/arrays_scope_runme.php
@@ -1,14 +1,13 @@
 <?php
 
 require "tests.php";
-require "arrays_scope.php";
 
-// New functions
-check::functions(array('new_bar','bar_blah'));
+// No new functions
+check::functions(array());
 // New classes
 check::classes(array('arrays_scope','Bar'));
-// New vars
-check::globals(array('bar_adata','bar_bdata','bar_cdata'));
+// No new globals
+check::globals(array());
 
 $bar=new bar();
 
diff --git a/Examples/test-suite/php/callback_runme.php b/Examples/test-suite/php/callback_runme.php
index 590c282..7171856 100644
--- a/Examples/test-suite/php/callback_runme.php
+++ b/Examples/test-suite/php/callback_runme.php
@@ -1,8 +1,10 @@
 <?php
 
 require "tests.php";
-require "callback.php";
+
 // In 2.0.6 and earlier, the constant was misnamed.
-if (gettype(callback::FOO_I_Cb_Ptr) !== 'resource') die("callback::FOO_I_Cb_Ptr not a resource\n");
+check::equal(gettype(callback::FOO_I_Cb_Ptr), 'object', "callback::FOO_I_Cb_Ptr not an object");
+
+check::equal(get_class(callback::FOO_I_Cb_Ptr), 'SWIG\_p_f_int__int', "callback::FOO_I_Cb_Ptr not expected class");
 
 check::done();
diff --git a/Examples/test-suite/php/casts_runme.php b/Examples/test-suite/php/casts_runme.php
index 84f4ba6..6234f0f 100644
--- a/Examples/test-suite/php/casts_runme.php
+++ b/Examples/test-suite/php/casts_runme.php
@@ -1,13 +1,12 @@
 <?php
 
 require "tests.php";
-require "casts.php";
 
 // No new functions
-check::functions(array('new_a','a_hello','new_b'));
-// No new classes
+check::functions(array());
+// New classes
 check::classes(array('A','B'));
-// now new vars
+// No new vars
 check::globals(array());
 
 # Make sure $b inherits hello() from class A
diff --git a/Examples/test-suite/php/char_strings_runme.php b/Examples/test-suite/php/char_strings_runme.php
index 58e444a..7dc8fe7 100644
--- a/Examples/test-suite/php/char_strings_runme.php
+++ b/Examples/test-suite/php/char_strings_runme.php
@@ -1,7 +1,6 @@
 <?php
 
 require "tests.php";
-require "char_strings.php";
 
 $CPLUSPLUS_MSG = "A message from the deep dark world of C++, where anything is possible.";
 $OTHERLAND_MSG_10 = "Little message from the safe world.10";
diff --git a/Examples/test-suite/php/class_ignore_runme.php b/Examples/test-suite/php/class_ignore_runme.php
index 7025fcc..f52bc1a 100644
--- a/Examples/test-suite/php/class_ignore_runme.php
+++ b/Examples/test-suite/php/class_ignore_runme.php
@@ -1,9 +1,8 @@
 <?php
 
 require "tests.php";
-require "class_ignore.php";
 
-check::functions(array('do_blah','new_bar','bar_blah','new_boo','boo_away','new_far','new_hoo'));
+check::functions(array('do_blah'));
 check::classes(array('class_ignore','Bar','Boo','Far','Hoo'));
 // No new vars
 check::globals(array());
diff --git a/Examples/test-suite/php/conversion_namespace_runme.php b/Examples/test-suite/php/conversion_namespace_runme.php
index 60c2885..858bf89 100644
--- a/Examples/test-suite/php/conversion_namespace_runme.php
+++ b/Examples/test-suite/php/conversion_namespace_runme.php
@@ -1,7 +1,6 @@
 <?php
 
 require "tests.php";
-require "conversion_namespace.php";
 
 check::classes(array("Foo","Bar"));
 $bar=new Bar;
diff --git a/Examples/test-suite/php/conversion_ns_template_runme.php b/Examples/test-suite/php/conversion_ns_template_runme.php
index bfdfd9f..4157b10 100644
--- a/Examples/test-suite/php/conversion_ns_template_runme.php
+++ b/Examples/test-suite/php/conversion_ns_template_runme.php
@@ -1,7 +1,6 @@
 <?php
 
 require "tests.php";
-require "conversion_ns_template.php";
 
 check::classes(array("conversion_ns_template","Foo_One","Bar_One","Hi"));
 // this is too hard, I'm not sure what to test for,
diff --git a/Examples/test-suite/php/conversion_runme.php b/Examples/test-suite/php/conversion_runme.php
index 6658c9d..858bf89 100644
--- a/Examples/test-suite/php/conversion_runme.php
+++ b/Examples/test-suite/php/conversion_runme.php
@@ -1,7 +1,6 @@
 <?php
 
 require "tests.php";
-require "conversion.php";
 
 check::classes(array("Foo","Bar"));
 $bar=new Bar;
diff --git a/Examples/test-suite/php/cpp11_strongly_typed_enumerations_runme.php b/Examples/test-suite/php/cpp11_strongly_typed_enumerations_runme.php
index df4dd99..82296de 100644
--- a/Examples/test-suite/php/cpp11_strongly_typed_enumerations_runme.php
+++ b/Examples/test-suite/php/cpp11_strongly_typed_enumerations_runme.php
@@ -1,7 +1,6 @@
 <?php
 
 require "tests.php";
-require "cpp11_strongly_typed_enumerations.php";
 
 function enumCheck($actual, $expected) {
   check::equal($actual, $expected, "Enum value mismatch");
diff --git a/Examples/test-suite/php/cpp_basic_runme.php b/Examples/test-suite/php/cpp_basic_runme.php
index 8e145ab..fb34bc9 100644
--- a/Examples/test-suite/php/cpp_basic_runme.php
+++ b/Examples/test-suite/php/cpp_basic_runme.php
@@ -1,14 +1,13 @@
 <?php
 
 require "tests.php";
-require "cpp_basic.php";
 
 // New functions
-check::functions(array('foo_func1','foo_func2','foo___str__','foosubsub___str__','bar_test','bar_testfoo','get_func1_ptr','get_func2_ptr','test_func_ptr','fl_window_show'));
+check::functions(array('get_func1_ptr','get_func2_ptr','test_func_ptr'));
 // New classes
 check::classes(array('cpp_basic','Foo','FooSub','FooSubSub','Bar','Fl_Window'));
-// New vars
-check::globals(array('foo_num','foo_func_ptr','bar_fptr','bar_fref','bar_fval','bar_cint','bar_global_fptr','bar_global_fref','bar_global_fval'));
+// No new vars
+check::globals(array());
 
 $f = new Foo(3);
 $f->func_ptr = get_func1_ptr();
@@ -16,4 +15,12 @@
 $f->func_ptr = get_func2_ptr();
 check::equal(test_func_ptr($f, 7), -7*3, "get_func2_ptr() didn't work");
 
+// Test that custom properties work - standard PHP objects support them,
+// so PHP developers will expect them to work for SWIG-wrapped objects too.
+check::equal($f->custom_prop, NULL, "Test unset custom property");
+$f->custom_prop = "test";
+check::equal($f->custom_prop, "test", "Test custom property setting");
+$f->custom_prop = 42;
+check::equal($f->custom_prop, 42, "Test custom property setting");
+
 check::done();
diff --git a/Examples/test-suite/php/cpp_static_runme.php b/Examples/test-suite/php/cpp_static_runme.php
index f794fbd..a1fd85c 100644
--- a/Examples/test-suite/php/cpp_static_runme.php
+++ b/Examples/test-suite/php/cpp_static_runme.php
@@ -1,13 +1,12 @@
 <?php
 
 require "tests.php";
-require "cpp_static.php";
 
 // New functions
-check::functions(array('staticfunctiontest_static_func','staticfunctiontest_static_func_2','staticfunctiontest_static_func_3','is_python_builtin','staticmembertest_grab_int','staticbase_grab_statty_base','staticderived_grab_statty_derived'));
+check::functions(array('is_python_builtin'));
 // New classes
 check::classes(array('StaticMemberTest','StaticFunctionTest','cpp_static','StaticBase','StaticDerived'));
-// New vars
-check::globals(array('staticmembertest_static_int','staticbase_statty','staticderived_statty'));
+// No new vars
+check::globals(array());
 
 check::done();
diff --git a/Examples/test-suite/php/director_abstract_runme.php b/Examples/test-suite/php/director_abstract_runme.php
index b26878d..bb424f9 100644
--- a/Examples/test-suite/php/director_abstract_runme.php
+++ b/Examples/test-suite/php/director_abstract_runme.php
@@ -1,13 +1,12 @@
 <?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
+// New functions
+check::functions(array('g'));
+// New classes
 check::classes(array('director_abstract','Foo','Example0','Example1','Example2','Example4','Example3_i','A'));
-// now new vars
+// No new vars
 check::globals(array());
 
 class MyFoo extends Foo {
diff --git a/Examples/test-suite/php/director_basic_runme.php b/Examples/test-suite/php/director_basic_runme.php
index e92762d..db7adef 100644
--- a/Examples/test-suite/php/director_basic_runme.php
+++ b/Examples/test-suite/php/director_basic_runme.php
@@ -1,11 +1,12 @@
 <?php
 
 require "tests.php";
-require "director_basic.php";
 
-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','myclass_nonvirtual','myclass_nonoverride','myclass_call_nonvirtual','myclass_call_nonoverride','myclass_connect','constptrclass_getconstptr'));
+// No new functions
+check::functions(array());
 check::classes(array('Foo','A','A1','Bar','MyClass','MyClassT_i','ConstPtrClass'));
-check::globals(array('bar_x'));
+// No new vars
+check::globals(array());
 
 class PhpFoo extends Foo {
   function ping() {
diff --git a/Examples/test-suite/php/director_classic_runme.php b/Examples/test-suite/php/director_classic_runme.php
index cd61416..e2ac1f7 100644
--- a/Examples/test-suite/php/director_classic_runme.php
+++ b/Examples/test-suite/php/director_classic_runme.php
@@ -1,13 +1,12 @@
 <?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::functions(array());
+// New classes
 check::classes(array('Being','Person','Child','GrandChild','OrphanPerson','OrphanChild','Caller'));
-// now new vars
+// No new vars
 check::globals(array());
 
 class TargetLangPerson extends Person {
@@ -81,11 +80,7 @@
   $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");
+  check::equal($ret, $expected, "#3 failed");
 
   $caller->resetCallback();
   if ($debug)
diff --git a/Examples/test-suite/php/director_default_runme.php b/Examples/test-suite/php/director_default_runme.php
index cb3de56..92e4150 100644
--- a/Examples/test-suite/php/director_default_runme.php
+++ b/Examples/test-suite/php/director_default_runme.php
@@ -1,13 +1,12 @@
 <?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::functions(array());
+// New classes
 check::classes(array('Foo','Bar','DefaultsBase','DefaultsDerived'));
-// now new vars
+// No new vars
 check::globals(array());
 
 $f = new Foo();
diff --git a/Examples/test-suite/php/director_detect_runme.php b/Examples/test-suite/php/director_detect_runme.php
index 8ac288c..ae92f7a 100644
--- a/Examples/test-suite/php/director_detect_runme.php
+++ b/Examples/test-suite/php/director_detect_runme.php
@@ -1,13 +1,12 @@
 <?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::functions(array());
+// New classes
 check::classes(array('A','Foo','Bar'));
-// now new vars
+// No new vars
 check::globals(array());
 
 class MyBar extends Bar {
diff --git a/Examples/test-suite/php/director_enum_runme.php b/Examples/test-suite/php/director_enum_runme.php
index 18a96f7..21e07a5 100644
--- a/Examples/test-suite/php/director_enum_runme.php
+++ b/Examples/test-suite/php/director_enum_runme.php
@@ -1,13 +1,12 @@
 <?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::functions(array());
+// New classes
 check::classes(array('director_enum','Foo','A','B','A2','B2'));
-// now new vars
+// No new vars
 check::globals(array());
 
 class MyFoo extends Foo {
diff --git a/Examples/test-suite/php/director_exception_runme.php b/Examples/test-suite/php/director_exception_runme.php
index 297aaea..e49c12d 100644
--- a/Examples/test-suite/php/director_exception_runme.php
+++ b/Examples/test-suite/php/director_exception_runme.php
@@ -1,13 +1,12 @@
 <?php
 
 require "tests.php";
-require "director_exception.php";
 
-// No new functions
-check::functions(array('foo_ping','foo_pong','launder','bar_ping','bar_pong','returnalltypes_return_int','returnalltypes_return_double','returnalltypes_return_const_char_star','returnalltypes_return_std_string','returnalltypes_return_bar','returnalltypes_call_int','returnalltypes_call_double','returnalltypes_call_const_char_star','returnalltypes_call_std_string','returnalltypes_call_bar','is_python_builtin'));
-// No new classes
+// New functions
+check::functions(array('launder','is_python_builtin'));
+// New classes
 check::classes(array('director_exception','Foo','Exception1','Exception2','Base','Bar','ReturnAllTypes'));
-// now new vars
+// No new vars
 check::globals(array());
 
 class MyException extends Exception {
@@ -38,9 +37,7 @@
 # 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 = director_exception::launder($a);
 $b = $a;
 try {
   $b->pong();
diff --git a/Examples/test-suite/php/director_extend_runme.php b/Examples/test-suite/php/director_extend_runme.php
index 9650031..4189854 100644
--- a/Examples/test-suite/php/director_extend_runme.php
+++ b/Examples/test-suite/php/director_extend_runme.php
@@ -1,13 +1,12 @@
 <?php
 
 require "tests.php";
-require "director_extend.php";
 
 // No new functions
-check::functions(array('spobject_getfoobar','spobject_dummy','spobject_exceptionmethod'));
-// No new classes
+check::functions(array());
+// New classes
 check::classes(array('SpObject'));
-// now new vars
+// No new vars
 check::globals(array());
 
 class MyObject extends SpObject{
diff --git a/Examples/test-suite/php/director_finalizer_runme.php b/Examples/test-suite/php/director_finalizer_runme.php
index d29ca61..f3a0c2c 100644
--- a/Examples/test-suite/php/director_finalizer_runme.php
+++ b/Examples/test-suite/php/director_finalizer_runme.php
@@ -1,18 +1,22 @@
 <?php
 
 require "tests.php";
-require "director_finalizer.php";
 
-// No new functions
-check::functions(array('foo_orstatus','deletefoo','getstatus','launder','resetstatus'));
-// No new classes
+// New functions
+check::functions(array('deleteFoo','getStatus','launder','resetStatus'));
+// New classes
 check::classes(array('director_finalizer','Foo'));
-// now new vars
+// No new vars
 check::globals(array());
 
 class MyFoo extends Foo {
   function __destruct() {
-    $this->orStatus(2);
+    # It's not safe to call methods on the C++ object from the PHP destructor
+    # if the object has been disowned, since the C++ object will already have
+    # been destroyed by the time the PHP destructor runs.
+    if ($this->thisown) {
+      $this->orStatus(2);
+    }
     if (method_exists(get_parent_class(), "__destruct")) {
       parent::__destruct();
     }
@@ -41,19 +45,23 @@
 
 $a = new MyFoo();
 $a->thisown = 0;
+check::equal(getStatus(), 0, "shadow release does not fire destructor of disowned object");
+
 deleteFoo($a);
 unset($a);
 
-check::equal(getStatus(), 3, "getStatus() failed #4");
+# getStatus() would ideally return 3 here.
+check::equal(getStatus(), 1, "getStatus() failed #4");
 
 resetStatus();
 
 $a = new MyFoo();
 $a->thisown = 0;
-deleteFoo(launder($a));
+$g = launder($a);
 unset($a);
-
-check::equal(getStatus(), 3, "getStatus() failed #5");
+deleteFoo($g);
+# getStatus() would ideally return 3 here.
+check::equal(getStatus(), 1, "getStatus() failed #5");
 
 resetStatus();
 
diff --git a/Examples/test-suite/php/director_frob_runme.php b/Examples/test-suite/php/director_frob_runme.php
index cb3b79d..732b9d5 100644
--- a/Examples/test-suite/php/director_frob_runme.php
+++ b/Examples/test-suite/php/director_frob_runme.php
@@ -1,14 +1,13 @@
 <?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::functions(array());
+// 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'));
+// No new vars
+check::globals(array());
 
 $foo = new Bravo();
 $s = $foo->abs_method();
diff --git a/Examples/test-suite/php/director_nested_runme.php b/Examples/test-suite/php/director_nested_runme.php
index 4b5737b..adf9f62 100644
--- a/Examples/test-suite/php/director_nested_runme.php
+++ b/Examples/test-suite/php/director_nested_runme.php
@@ -1,13 +1,12 @@
 <?php
 
 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::functions(array());
+// New classes
 check::classes(array('Foo_int','Bar','FooBar_int'));
-// now new vars
+// No new vars
 check::globals(array());
 
 class A extends FooBar_int {
@@ -60,10 +59,7 @@
 }
 
 $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 = Foobar_int::get_self($cc);
 $c->advance();
 
 check::equal($c->get_name(), "FooBar::get_name hello", "get_name failed");
diff --git a/Examples/test-suite/php/director_overload_runme.php b/Examples/test-suite/php/director_overload_runme.php
index 248e841..07b485d 100644
--- a/Examples/test-suite/php/director_overload_runme.php
+++ b/Examples/test-suite/php/director_overload_runme.php
@@ -1,10 +1,8 @@
 <?php
 
 require "tests.php";
-require "director_overload.php";
 
-check::functions(array('new_overloadedClass','new_overloadedPointers','new_overloadedGetSet','overloadedclass_method1','overloadedclass_method3','overloadedclass_method2','overloadedpointers_method','overloadedpointers_notover','overloadedgetset_rw'));
-
+check::functions(array());
 check::classes(array('OverloadedClass','OverloadedPointers','OverloadedGetSet'));
 check::globals(array());
 
diff --git a/Examples/test-suite/php/director_pass_by_value_runme.php b/Examples/test-suite/php/director_pass_by_value_runme.php
index e8a5324..d376329 100644
--- a/Examples/test-suite/php/director_pass_by_value_runme.php
+++ b/Examples/test-suite/php/director_pass_by_value_runme.php
@@ -1,7 +1,6 @@
 <?php
 
 require "tests.php";
-require "director_pass_by_value.php";
 
 $passByVal = null;
 
diff --git a/Examples/test-suite/php/director_profile_runme.php b/Examples/test-suite/php/director_profile_runme.php
index e1ae5a3..219ec81 100644
--- a/Examples/test-suite/php/director_profile_runme.php
+++ b/Examples/test-suite/php/director_profile_runme.php
@@ -1,10 +1,9 @@
 <?php
 
 require "tests.php";
-require "director_profile.php";
 
-// New functions
-check::functions(array('b_c_fn','b_vfi','b_fi','b_fj','b_fk','b_fl','b_get_self','b_vfs','b_fs'));
+// No new functions
+check::functions(array());
 // New classes
 check::classes(array('A','B'));
 // No new vars
diff --git a/Examples/test-suite/php/director_protected_runme.php b/Examples/test-suite/php/director_protected_runme.php
index 2d9203a..b73c8f8 100644
--- a/Examples/test-suite/php/director_protected_runme.php
+++ b/Examples/test-suite/php/director_protected_runme.php
@@ -1,11 +1,12 @@
 <?php
 
 require "tests.php";
-require "director_protected.php";
 
-check::functions(array('foo_pong','foo_s','foo_q','foo_ping','foo_pang','foo_used','foo_cheer','bar_create','bar_callping','bar_callcheer','bar_cheer','bar_pong','bar_used','bar_ping','bar_pang','a_draw','b_draw'));
+// No new functions
+check::functions(array());
 check::classes(array('Foo','Bar','PrivateFoo','A','B','AA','BB'));
-check::globals(array('bar_a'));
+// No new vars
+check::globals(array());
 
 class FooBar extends Bar {
   protected function ping() {
diff --git a/Examples/test-suite/php/director_stl_runme.php b/Examples/test-suite/php/director_stl_runme.php
index 70a3316..654251a 100644
--- a/Examples/test-suite/php/director_stl_runme.php
+++ b/Examples/test-suite/php/director_stl_runme.php
@@ -1,13 +1,12 @@
 <?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::functions(array());
+// New classes
 check::classes(array('Foo'));
-// now new vars
+// No new vars
 check::globals(array());
 
 class MyFoo extends Foo {
diff --git a/Examples/test-suite/php/director_string_runme.php b/Examples/test-suite/php/director_string_runme.php
index afde255..3127f13 100644
--- a/Examples/test-suite/php/director_string_runme.php
+++ b/Examples/test-suite/php/director_string_runme.php
@@ -1,14 +1,13 @@
 <?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','stringvector_capacity','stringvector_reserve'));
-// No new classes
+check::functions(array());
+// New classes
 check::classes(array('A','StringVector'));
-// now new vars
-check::globals(array('a','a_call','a_m_strings','stringvector'));
+// No new vars
+check::globals(array());
 
 class B extends A {
   function get_first() {
diff --git a/Examples/test-suite/php/director_thread_runme.php b/Examples/test-suite/php/director_thread_runme.php
index dbcd141..190dbad 100644
--- a/Examples/test-suite/php/director_thread_runme.php
+++ b/Examples/test-suite/php/director_thread_runme.php
@@ -1,18 +1,17 @@
 <?php
 
 require "tests.php";
-require "director_thread.php";
 
 # Fails in a ZTS-build of PHP5 - see: https://github.com/swig/swig/pull/155
 # FIXME: Does this still fail in a threaded build of PHP7?
 exit(0);
 
-// No new functions
+// New functions
 check::functions(array('millisecondsleep','foo_stop','foo_run','foo_do_foo'));
-// No new classes
+// New classes
 check::classes(array('director_thread','Foo'));
-// now new vars
-check::globals(array('foo_val'));
+// No new vars
+check::globals(array());
 
 class Derived extends Foo {
   function do_foo() {
diff --git a/Examples/test-suite/php/director_unroll_runme.php b/Examples/test-suite/php/director_unroll_runme.php
index c462fe7..7f94753 100644
--- a/Examples/test-suite/php/director_unroll_runme.php
+++ b/Examples/test-suite/php/director_unroll_runme.php
@@ -1,14 +1,13 @@
 <?php
 
 require "tests.php";
-require "director_unroll.php";
 
 // No new functions
-check::functions(array('foo_ping','foo_pong'));
-// No new classes
+check::functions(array());
+// New classes
 check::classes(array('Foo','Bar'));
-// now new vars
-check::globals(array('bar'));
+// No new vars
+check::globals(array());
 
 class MyFoo extends Foo {
   function ping() {
@@ -23,9 +22,7 @@
 $b->set($a);
 $c = $b->get();
 
-// FIXME: This doesn't work for checking that they wrap the same C++ object
-// because the two objects have different PHP resources, and we can't easily
-// look inside those resources to see which C++ objects they refer to.
-//check::equal($a->_cPtr, $c->_cPtr, "_cPtr check failed");
+// FIXME: The python version checks that a.this == c.this, but we don't seem
+// to have a way to check this with the PHP bindings we generate.
 
 check::done();
diff --git a/Examples/test-suite/php/enum_scope_template_runme.php b/Examples/test-suite/php/enum_scope_template_runme.php
index 10924bc..d5aed62 100644
--- a/Examples/test-suite/php/enum_scope_template_runme.php
+++ b/Examples/test-suite/php/enum_scope_template_runme.php
@@ -1,14 +1,16 @@
 <?php
 require "tests.php";
-require "enum_scope_template.php";
 
+check::functions(array("chops"));
 check::classes(array("enum_scope_template", "TreeInt"));
-check::functions(array("chops","treeint_chops"));
-check::equal(0,TreeInt_Oak,"0==TreeInt_Oak");
-check::equal(1,TreeInt_Fir,"1==TreeInt_Fir");
-check::equal(2,TreeInt_Cedar,"2==TreeInt_Cedar");
-check::equal(TreeInt_Oak,chops(TreeInt_Oak),"TreeInt_Oak==chops(TreeInt_Oak)");
-check::equal(TreeInt_Fir,chops(TreeInt_Fir),"TreeInt_Fir==chops(TreeInt_Fir)");
-check::equal(TreeInt_Cedar,chops(TreeInt_Cedar),"TreeInt_Cedar==chops(TreeInt_Cedar)");
+// No new vars
+check::globals(array());
+
+check::equal(0,TreeInt::Oak,"0==TreeInt_Oak");
+check::equal(1,TreeInt::Fir,"1==TreeInt_Fir");
+check::equal(2,TreeInt::Cedar,"2==TreeInt_Cedar");
+check::equal(TreeInt::Oak,chops(TreeInt::Oak),"TreeInt_Oak==chops(TreeInt_Oak)");
+check::equal(TreeInt::Fir,chops(TreeInt::Fir),"TreeInt_Fir==chops(TreeInt_Fir)");
+check::equal(TreeInt::Cedar,chops(TreeInt::Cedar),"TreeInt_Cedar==chops(TreeInt_Cedar)");
 
 check::done();
diff --git a/Examples/test-suite/php/evil_diamond_ns_runme.php b/Examples/test-suite/php/evil_diamond_ns_runme.php
index bcf5aaf..f2115bf 100644
--- a/Examples/test-suite/php/evil_diamond_ns_runme.php
+++ b/Examples/test-suite/php/evil_diamond_ns_runme.php
@@ -1,7 +1,6 @@
 <?php
 
 require "tests.php";
-require "evil_diamond_ns.php";
 
 check::classes(array("evil_diamond_ns","foo","bar","baz","spam"));
 check::functions("test");
diff --git a/Examples/test-suite/php/evil_diamond_prop_runme.php b/Examples/test-suite/php/evil_diamond_prop_runme.php
index dd572fa..611f17d 100644
--- a/Examples/test-suite/php/evil_diamond_prop_runme.php
+++ b/Examples/test-suite/php/evil_diamond_prop_runme.php
@@ -1,7 +1,6 @@
 <?php
 
 require "tests.php";
-require "evil_diamond_prop.php";
 
 check::classes(array("evil_diamond_prop","foo","bar","baz","spam"));
 check::functions("test");
@@ -31,9 +30,7 @@
 check::equal(1,$spam->_foo,"1==spam->_foo");
 check::equal(2,$spam->_bar,"2==spam->_bar");
 // multiple inheritance not supported in PHP
-set_error_handler(function () {return true;}, E_NOTICE|E_WARNING); // Don't complain that _baz is unknown.
 check::equal(null,$spam->_baz,"null==spam->_baz");
-restore_error_handler();
 check::equal(4,$spam->_spam,"4==spam->_spam");
 
 check::done();
diff --git a/Examples/test-suite/php/evil_diamond_runme.php b/Examples/test-suite/php/evil_diamond_runme.php
index d298eeb..7d01c98 100644
--- a/Examples/test-suite/php/evil_diamond_runme.php
+++ b/Examples/test-suite/php/evil_diamond_runme.php
@@ -1,7 +1,6 @@
 <?php
 
 require "tests.php";
-require "evil_diamond.php";
 
 check::classes(array("evil_diamond","foo","bar","baz","spam"));
 check::functions("test");
diff --git a/Examples/test-suite/php/exception_order_runme.php b/Examples/test-suite/php/exception_order_runme.php
index 854cd3a..9a2c911 100644
--- a/Examples/test-suite/php/exception_order_runme.php
+++ b/Examples/test-suite/php/exception_order_runme.php
@@ -1,10 +1,9 @@
 <?
 require "tests.php";
-require "exception_order.php";
 
-check::functions(array('a_foo','a_bar','a_foobar','a_barfoo','is_python_builtin'));
+check::functions(array('is_python_builtin'));
 check::classes(array('A','E1','E2','E3','exception_order','ET_i','ET_d'));
-check::globals(array('efoovar','foovar','cfoovar','a_sfoovar','a_foovar','a_efoovar'));
+check::globals(array('efoovar','foovar','cfoovar'));
 
 $a = new A();
 try {
diff --git a/Examples/test-suite/php/extend_template_ns_runme.php b/Examples/test-suite/php/extend_template_ns_runme.php
index 4766124..4615867 100644
--- a/Examples/test-suite/php/extend_template_ns_runme.php
+++ b/Examples/test-suite/php/extend_template_ns_runme.php
@@ -1,7 +1,6 @@
 <?php
 
 require "tests.php";
-require "extend_template_ns.php";
 
 check::classes(array("extend_template_ns","Foo_One"));
 $foo=new Foo_One();
diff --git a/Examples/test-suite/php/extend_template_runme.php b/Examples/test-suite/php/extend_template_runme.php
index 58e3f6f..a6579dc 100644
--- a/Examples/test-suite/php/extend_template_runme.php
+++ b/Examples/test-suite/php/extend_template_runme.php
@@ -1,7 +1,6 @@
 <?php
 
 require "tests.php";
-require "extend_template.php";
 
 check::classes(array("Foo_0"));
 $foo=new Foo_0();
diff --git a/Examples/test-suite/php/global_vars_runme.php b/Examples/test-suite/php/global_vars_runme.php
new file mode 100644
index 0000000..c3fe7d8
--- /dev/null
+++ b/Examples/test-suite/php/global_vars_runme.php
@@ -0,0 +1,27 @@
+<?php
+
+require "tests.php";
+
+check::functions(array('init','read_b','read_x'));
+check::classes(array('A','global_vars'));
+check::globals(array('b','a','ap','cap','ar','x','xp','c_member','vp','h','hp','hr'));
+
+$an = new A();
+check::classname('A', $an);
+ap_set($an);
+check::classname('A', ap_get());
+check::equivalent(ap_get(), $an, "global var assignment");
+
+x_set(17);
+check::equal(x_get(), 17, "global var assignment");
+check::equal(read_x(), 17, "PHP global var change visible in C++");
+init();
+check::equal(x_get(), 1234, "C++ global var change visible in PHP");
+
+b_set('test');
+check::equal(b_get(), 'test', "global var assignment");
+check::equal(read_b(), 'test', "PHP global var change visible in C++");
+init();
+check::equal(b_get(), 'string b', "C++ global var change visible in PHP");
+
+check::done();
diff --git a/Examples/test-suite/php/grouping_runme.php b/Examples/test-suite/php/grouping_runme.php
index c040115..d2b849d 100644
--- a/Examples/test-suite/php/grouping_runme.php
+++ b/Examples/test-suite/php/grouping_runme.php
@@ -1,11 +1,10 @@
 <?php
 
 require "tests.php";
-require "grouping.php";
 
 check::functions(array("test1","test2","do_unary","negate"));
 check::equal(5,test1(5),"5==test1(5)");
-check::resource(test2(7),"_p_int","_p_int==test2(7)");
+check::equal(get_class(test2(7)),"SWIG\\_p_int","test2(7) is _p_int");
 check::globals(array('test3'));
 
 //check::equal(37,test3_get(),'37==test3_get()');
diff --git a/Examples/test-suite/php/ignore_parameter_runme.php b/Examples/test-suite/php/ignore_parameter_runme.php
index 4d5264c..d9db2d7 100644
--- a/Examples/test-suite/php/ignore_parameter_runme.php
+++ b/Examples/test-suite/php/ignore_parameter_runme.php
@@ -1,10 +1,9 @@
 <?php
 
 require "tests.php";
-require "ignore_parameter.php";
 
 // New functions
-check::functions(array('jaguar','lotus','tvr','ferrari','fiat','sportscars_daimler','sportscars_astonmartin','sportscars_bugatti','sportscars_lamborghini','sportscars_maseratti'));
+check::functions(array('jaguar','lotus','tvr','ferrari','fiat'));
 // New classes
 check::classes(array('ignore_parameter','SportsCars','MiniCooper','MorrisMinor','FordAnglia','AustinAllegro'));
 // No new vars
diff --git a/Examples/test-suite/php/import_nomodule_runme.php b/Examples/test-suite/php/import_nomodule_runme.php
index 6e4f5a7..e639c78 100644
--- a/Examples/test-suite/php/import_nomodule_runme.php
+++ b/Examples/test-suite/php/import_nomodule_runme.php
@@ -1,14 +1,16 @@
 <?php
-require "tests.php";
-require "import_nomodule.php";
 
-// No new functions
-check::functions(array('create_foo','delete_foo','test1','is_python_builtin'));
-// No new classes
-check::classes(array('import_nomodule','Bar'));
-// now new vars
+require "tests.php";
+
+check::functions(array('is_python_builtin'));
+check::classes(array('import_nomodule'));
+// No new globals
 check::globals(array());
 
+// SWIGPHP doesn't currently support the "violation of the type system" which
+// is tested by this testcase.
+exit(0);
+
 $f = import_nomodule::create_Foo();
 import_nomodule::test1($f,42);
 import_nomodule::delete_Foo($f);
diff --git a/Examples/test-suite/php/li_carrays_cpp_runme.php b/Examples/test-suite/php/li_carrays_cpp_runme.php
index cceb76d..ccbcf7d 100644
--- a/Examples/test-suite/php/li_carrays_cpp_runme.php
+++ b/Examples/test-suite/php/li_carrays_cpp_runme.php
@@ -1,16 +1,16 @@
 <?php
+
 require "tests.php";
-require "li_carrays_cpp.php";
 
 // Check functions.
-check::functions(array('new_intarray','delete_intarray','intarray_getitem','intarray_setitem','doublearray_getitem','doublearray_setitem','doublearray_cast','doublearray_frompointer','xyarray_getitem','xyarray_setitem','xyarray_cast','xyarray_frompointer','delete_abarray','abarray_getitem','abarray_setitem','shortarray_getitem','shortarray_setitem','shortarray_cast','shortarray_frompointer','sum_array'));
+check::functions(array('new_intArray','delete_intArray','intArray_getitem','intArray_setitem','new_ABArray','delete_ABArray','ABArray_getitem','ABArray_setitem','sum_array'));
 
 // Check classes.
 // NB An "li_carrays_cpp" class is created as a mock namespace.
 check::classes(array('li_carrays_cpp','doubleArray','AB','XY','XYArray','shortArray'));
 
 // Check global variables.
-check::globals(array('xy_x','xy_y','globalxyarray','ab_a','ab_b','globalabarray'));
+check::globals(array('globalXYArray','globalABArray'));
 
 $d = new doubleArray(10);
 
diff --git a/Examples/test-suite/php/li_carrays_runme.php b/Examples/test-suite/php/li_carrays_runme.php
index 97144ed..df54e04 100644
--- a/Examples/test-suite/php/li_carrays_runme.php
+++ b/Examples/test-suite/php/li_carrays_runme.php
@@ -1,16 +1,16 @@
 <?php
+
 require "tests.php";
-require "li_carrays.php";
 
 // Check functions.
-check::functions(array('new_intarray','delete_intarray','intarray_getitem','intarray_setitem','doublearray_getitem','doublearray_setitem','doublearray_cast','doublearray_frompointer','xyarray_getitem','xyarray_setitem','xyarray_cast','xyarray_frompointer','delete_abarray','abarray_getitem','abarray_setitem','shortarray_getitem','shortarray_setitem','shortarray_cast','shortarray_frompointer','sum_array'));
+check::functions(array('new_intArray','delete_intArray','intArray_getitem','intArray_setitem','new_ABArray','delete_ABArray','ABArray_getitem','ABArray_setitem','sum_array'));
 
 // Check classes.
 // NB An "li_carrays" class is created as a mock namespace.
 check::classes(array('li_carrays','doubleArray','AB','XY','XYArray','shortArray'));
 
 // Check global variables.
-check::globals(array('xy_x','xy_y','globalxyarray','ab_a','ab_b','globalabarray'));
+check::globals(array('globalXYArray','globalABArray'));
 
 $d = new doubleArray(10);
 
diff --git a/Examples/test-suite/php/li_factory_runme.php b/Examples/test-suite/php/li_factory_runme.php
index a865ae1..3ccdd03 100644
--- a/Examples/test-suite/php/li_factory_runme.php
+++ b/Examples/test-suite/php/li_factory_runme.php
@@ -1,13 +1,12 @@
 <?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::functions(array());
+// New classes
 check::classes(array('Geometry','Point','Circle'));
-// now new vars
+// No new vars
 check::globals(array());
 
 $circle = Geometry::create(Geometry::CIRCLE);
diff --git a/Examples/test-suite/php/li_std_string_runme.php b/Examples/test-suite/php/li_std_string_runme.php
index e83e7c0..680b7ce 100644
--- a/Examples/test-suite/php/li_std_string_runme.php
+++ b/Examples/test-suite/php/li_std_string_runme.php
@@ -1,11 +1,34 @@
 <?php
 
 require "tests.php";
-require "li_std_string.php";
 
-// Global variables
-//$s="initial string";
-//check::equal(GlobalString2_get() ,"global string 2", "GlobalString2 test 1");
+# Checking expected use of %typemap(in) std::string {}
+li_std_string::test_value("Fee");
+
+# Checking expected result of %typemap(out) std::string {}
+check::equal(li_std_string::test_value("Fi"), "Fi", "Test 1");
+
+# Checking expected use of %typemap(in) const std::string & {}
+li_std_string::test_const_reference("Fo");
+
+# Checking expected result of %typemap(out) const std::string& {}
+check::equal(li_std_string::test_const_reference("Fum"), "Fum", "Test 3");
+
+# Input and output typemaps for pointers and non-const references to
+# std::string are *not* supported; the following tests confirm
+# that none of these cases are slipping through.
+
+$stringPtr = li_std_string::test_pointer_out();
+
+li_std_string::test_pointer($stringPtr);
+
+$stringPtr = li_std_string::test_const_pointer_out();
+
+li_std_string::test_const_pointer($stringPtr);
+
+$stringPtr = li_std_string::test_reference_out();
+
+li_std_string::test_reference($stringPtr);
 
 // Global variables
 $s = "initial string";
@@ -24,8 +47,7 @@
 check::equal(Structure::StaticMemberString2(), "static member string 2", "StaticMemberString2 test 1");
 Structure::StaticMemberString2($s);
 check::equal(Structure::StaticMemberString2(), $s, "StaticMemberString2 test 2");
-// below broken ?
-//check::equal(Structure::ConstStaticMemberString(), "const static member string", "ConstStaticMemberString test");
+check::equal(Structure::ConstStaticMemberString(), "const static member string", "ConstStaticMemberString test");
 
 // This used to give "Undefined variable: r"
 li_std_string::test_const_reference_returning_void("foo");
diff --git a/Examples/test-suite/php/li_std_vector_member_var_runme.php b/Examples/test-suite/php/li_std_vector_member_var_runme.php
index f463efb..32ce484 100644
--- a/Examples/test-suite/php/li_std_vector_member_var_runme.php
+++ b/Examples/test-suite/php/li_std_vector_member_var_runme.php
@@ -1,7 +1,6 @@
 <?php
 
 require "tests.php";
-require "li_std_vector_member_var.php";
 
 $t = new Test();
 
diff --git a/Examples/test-suite/php/member_pointer_const_runme.php b/Examples/test-suite/php/member_pointer_const_runme.php
new file mode 100644
index 0000000..3f55549
--- /dev/null
+++ b/Examples/test-suite/php/member_pointer_const_runme.php
@@ -0,0 +1,59 @@
+<?php
+
+require "tests.php";
+
+// Check functions.
+check::functions(array('do_op','do_op_td','areapt','perimeterpt','perimeterpt_td','call1'));
+
+// Check classes.
+check::classes(array('member_pointer_const','Circle','Funktions','Shape','Square'));
+
+// Check global variables.
+check::globals(array('areavar','perimetervar','perimetervar_td'));
+
+# Get the pointers
+
+$area_pt = member_pointer_const::areapt();
+$perim_pt = member_pointer_const::perimeterpt();
+
+# Create some objects
+
+$s = new Square(10);
+
+# Do some calculations
+
+check::equal(100.0, member_pointer_const::do_op($s, $area_pt), "Square area");
+check::equal(40.0, member_pointer_const::do_op($s, $perim_pt), "Square perim");
+
+
+$memberPtr = member_pointer_const::areavar_get();
+$memberPtr = member_pointer_const::perimetervar_get();
+
+# Try the variables
+check::equal(100.0, member_pointer_const::do_op($s, member_pointer_const::areavar_get()), "Square area");
+check::equal(40.0, member_pointer_const::do_op($s, member_pointer_const::perimetervar_get()), "Square perim");
+
+# Modify one of the variables
+member_pointer_const::areavar_set($perim_pt);
+
+check::equal(40.0, member_pointer_const::do_op($s, member_pointer_const::areavar_get()), "Square perimeter");
+
+# Try the constants
+
+/*
+$memberPtr = member_pointer_const::AREAPT;
+$memberPtr = member_pointer_const::PERIMPT;
+$memberPtr = member_pointer_const::NULLPT;
+
+check::equal(100.0, member_pointer_const::do_op($s, member_pointer_const::AREAPT), "Square area");
+check::equal(40.0, member_pointer_const::do_op($s, member_pointer_const::PERIMPT), "Square perim");
+*/
+
+# Typedefs
+check::equal(40.0, member_pointer_const::do_op_td($s, $perim_pt), "Square perim");
+
+/*
+check::equal(3, member_pointer_const::call1(member_pointer_const::ADD_BY_VALUE, 1, 2), "Add by value");
+check::equal(7, member_pointer_const::call2(member_pointer_const::ADD_BY_VALUE, 3, 4), "Add by pointer");
+check::equal(11, member_pointer_const::call3(member_pointer_const::ADD_BY_VALUE, 5, 6), "Add by reference");
+ */
diff --git a/Examples/test-suite/php/multivalue_runme.php b/Examples/test-suite/php/multivalue_runme.php
index 2a5188d..77e1e1a 100644
--- a/Examples/test-suite/php/multivalue_runme.php
+++ b/Examples/test-suite/php/multivalue_runme.php
@@ -1,7 +1,6 @@
 <?php
 
 require "tests.php";
-require "multivalue.php";
 
 // New functions
 check::functions(array('divide_l','divide_v','divide_mv'));
diff --git a/Examples/test-suite/php/newobject1_runme.php b/Examples/test-suite/php/newobject1_runme.php
index 03bba0c..a495ab1 100644
--- a/Examples/test-suite/php/newobject1_runme.php
+++ b/Examples/test-suite/php/newobject1_runme.php
@@ -1,13 +1,12 @@
 <?php
 
 require "tests.php";
-require "newobject1.php";
 
 // No new functions
-check::functions(array('foo_makefoo','foo_makemore','foo_foocount'));
-// No new classes
+check::functions(array());
+// New classes
 check::classes(array('Foo'));
-// now new vars
+// No new vars
 check::globals(array());
 
 $foo = Foo::makeFoo();
diff --git a/Examples/test-suite/php/newobject3_runme.php b/Examples/test-suite/php/newobject3_runme.php
index 9b7e143..8efa989 100644
--- a/Examples/test-suite/php/newobject3_runme.php
+++ b/Examples/test-suite/php/newobject3_runme.php
@@ -1,7 +1,6 @@
 <?php
 
 require "tests.php";
-require "newobject3.php";
 
 $factory = new factory();
 
diff --git a/Examples/test-suite/php/overload_null_runme.php b/Examples/test-suite/php/overload_null_runme.php
index 03932dc..b4b28e4 100644
--- a/Examples/test-suite/php/overload_null_runme.php
+++ b/Examples/test-suite/php/overload_null_runme.php
@@ -1,7 +1,6 @@
 <?php
 
 require "tests.php";
-require "overload_null.php";
 
 $o = new Overload();
 $x = new X();
diff --git a/Examples/test-suite/php/overload_polymorphic_runme.php b/Examples/test-suite/php/overload_polymorphic_runme.php
index 6c20374..9089882 100644
--- a/Examples/test-suite/php/overload_polymorphic_runme.php
+++ b/Examples/test-suite/php/overload_polymorphic_runme.php
@@ -1,7 +1,6 @@
 <?php
 
 require "tests.php";
-require "overload_polymorphic.php";
 
 $t = new Derived();
 
diff --git a/Examples/test-suite/php/overload_rename_runme.php b/Examples/test-suite/php/overload_rename_runme.php
index 982db5c..c2df990 100644
--- a/Examples/test-suite/php/overload_rename_runme.php
+++ b/Examples/test-suite/php/overload_rename_runme.php
@@ -1,13 +1,12 @@
 <?php
 
 require "tests.php";
-require "overload_rename.php";
 
 // No new functions
 check::functions(array());
-// No new classes
+// New classes
 check::classes(array('Foo'));
-// now new vars
+// No new vars
 check::globals(array());
 
 $f = new Foo(1.0);
diff --git a/Examples/test-suite/php/overload_return_type_runme.php b/Examples/test-suite/php/overload_return_type_runme.php
index 2fadb7a..5f01bbe 100644
--- a/Examples/test-suite/php/overload_return_type_runme.php
+++ b/Examples/test-suite/php/overload_return_type_runme.php
@@ -1,7 +1,6 @@
 <?php
 
 require "tests.php";
-require "overload_return_type.php";
 
 $b = new B;
 check::equal($b->foo(1), 0, "");
diff --git a/Examples/test-suite/php/php_iterator_runme.php b/Examples/test-suite/php/php_iterator_runme.php
index 48021bf..76b4757 100644
--- a/Examples/test-suite/php/php_iterator_runme.php
+++ b/Examples/test-suite/php/php_iterator_runme.php
@@ -1,9 +1,9 @@
 <?php
 
 require "tests.php";
-require "php_iterator.php";
 
-check::functions(array('myiterator_rewind','myiterator_key','myiterator_current','myiterator_next','myiterator_valid'));
+// No new functions.
+check::functions(array());
 check::classes(array('MyIterator'));
 // No new global variables.
 check::globals(array());
diff --git a/Examples/test-suite/php/php_pragma_runme.php b/Examples/test-suite/php/php_pragma_runme.php
index 76359dc..e70f2ce 100644
--- a/Examples/test-suite/php/php_pragma_runme.php
+++ b/Examples/test-suite/php/php_pragma_runme.php
@@ -1,8 +1,6 @@
 <?php
 
 require "tests.php";
-require "php_pragma.php";
-
 
 check::equal('1.5',(new ReflectionExtension('php_pragma'))->getVersion(),"1.5==version(php_pragma)");
 
diff --git a/Examples/test-suite/php/pointer_reference_runme.php b/Examples/test-suite/php/pointer_reference_runme.php
index 3fa3c6a..a8a5112 100644
--- a/Examples/test-suite/php/pointer_reference_runme.php
+++ b/Examples/test-suite/php/pointer_reference_runme.php
@@ -1,7 +1,6 @@
 <?php
 
 require "tests.php";
-require "pointer_reference.php";
 
 $s  = pointer_reference::get();
 check::equal($s->value, 10, "pointer_reference::get() failed");
diff --git a/Examples/test-suite/php/prefix_runme.php b/Examples/test-suite/php/prefix_runme.php
index d8f77ec..0f56b04 100644
--- a/Examples/test-suite/php/prefix_runme.php
+++ b/Examples/test-suite/php/prefix_runme.php
@@ -1,13 +1,12 @@
 <?php
 
 require "tests.php";
-require "prefix.php";
 
 // No new functions
-check::functions(array('foo_get_self'));
-// No new classes
+check::functions(array());
+// New classes
 check::classes(array('ProjectFoo'));
-// now new vars
+// No new vars
 check::globals(array());
 
 $f = new ProjectFoo();
diff --git a/Examples/test-suite/php/preproc_constants_c_runme.php b/Examples/test-suite/php/preproc_constants_c_runme.php
index 5f52016..a26892b 100644
--- a/Examples/test-suite/php/preproc_constants_c_runme.php
+++ b/Examples/test-suite/php/preproc_constants_c_runme.php
@@ -1,7 +1,6 @@
 <?php
 
 require "tests.php";
-require "preproc_constants_c.php";
 
 // Same as preproc_constants.i testcase, but bool types are int instead
 check::equal(gettype(preproc_constants_c::CONST_INT1), "integer", "preproc_constants.CONST_INT1 has unexpected type");
diff --git a/Examples/test-suite/php/preproc_constants_runme.php b/Examples/test-suite/php/preproc_constants_runme.php
index 476eba8..7f45a1b 100644
--- a/Examples/test-suite/php/preproc_constants_runme.php
+++ b/Examples/test-suite/php/preproc_constants_runme.php
@@ -1,7 +1,6 @@
 <?php
 
 require "tests.php";
-require "preproc_constants.php";
 
 check::equal(gettype(preproc_constants::CONST_INT1), "integer", "preproc_constants.CONST_INT1 has unexpected type");
 check::equal(gettype(preproc_constants::CONST_INT2), "integer", "preproc_constants.CONST_INT2 has unexpected type");
diff --git a/Examples/test-suite/php/primitive_ref_runme.php b/Examples/test-suite/php/primitive_ref_runme.php
index efa9c52..9b281cf 100644
--- a/Examples/test-suite/php/primitive_ref_runme.php
+++ b/Examples/test-suite/php/primitive_ref_runme.php
@@ -1,7 +1,6 @@
 <?php
 
 require "tests.php";
-require "primitive_ref.php";
 
 # A large long long number is too big, so PHP makes treats it as a double, but SWIG opts to return it as a string.
 # The conversion to double can lose precision so this isn't an exact comparison.
diff --git a/Examples/test-suite/php/rename_scope_runme.php b/Examples/test-suite/php/rename_scope_runme.php
index 1fd85f8..e1db4f5 100644
--- a/Examples/test-suite/php/rename_scope_runme.php
+++ b/Examples/test-suite/php/rename_scope_runme.php
@@ -1,7 +1,6 @@
 <?php
 
 require "tests.php";
-require "rename_scope.php";
 
 check::classes(array("rename_scope","Interface_UP","Interface_BP","Natural_UP","Natural_BP","Bucket"));
 
diff --git a/Examples/test-suite/php/skel.php b/Examples/test-suite/php/skel.php
index 351cad7..7fdebc7 100644
--- a/Examples/test-suite/php/skel.php
+++ b/Examples/test-suite/php/skel.php
@@ -2,13 +2,12 @@
 // Sample test file
 
 require "tests.php";
-require "____.php";
 
 // No new functions
 check::functions(array());
 // No new classes
 check::classes(array());
-// now new vars
+// No new vars
 check::globals(array());
 
 check::done();
diff --git a/Examples/test-suite/php/smart_pointer_rename_runme.php b/Examples/test-suite/php/smart_pointer_rename_runme.php
index 8cc47dc..0948603 100644
--- a/Examples/test-suite/php/smart_pointer_rename_runme.php
+++ b/Examples/test-suite/php/smart_pointer_rename_runme.php
@@ -1,7 +1,6 @@
 <?php
 
 require "tests.php";
-require "smart_pointer_rename.php";
 
 check::classes(array("Foo","Bar"));
 check::classmethods("foo",array("ftest1","ftest2","__set","__isset","__get","__construct"));
diff --git a/Examples/test-suite/php/swig_exception_runme.php b/Examples/test-suite/php/swig_exception_runme.php
index 66758ed..37a7a59 100644
--- a/Examples/test-suite/php/swig_exception_runme.php
+++ b/Examples/test-suite/php/swig_exception_runme.php
@@ -1,6 +1,5 @@
 <?php
 
-require("swig_exception.php");
 require("tests.php");
 
 $c = new Circle(10);
diff --git a/Examples/test-suite/php/sym_runme.php b/Examples/test-suite/php/sym_runme.php
index f6a8cda..7e8f914 100644
--- a/Examples/test-suite/php/sym_runme.php
+++ b/Examples/test-suite/php/sym_runme.php
@@ -1,10 +1,9 @@
 <?php
 
 require "tests.php";
-require "sym.php";
 
-// New functions
-check::functions(array('flim_hulahoops','flim_jar','flam_jam','flam_jar'));
+// No new functions
+check::functions(array());
 // New classes
 check::classes(array('Flim','Flam'));
 // No new vars
diff --git a/Examples/test-suite/php/template_arg_typename_runme.php b/Examples/test-suite/php/template_arg_typename_runme.php
index fd1d5e5..c7fcbbd 100644
--- a/Examples/test-suite/php/template_arg_typename_runme.php
+++ b/Examples/test-suite/php/template_arg_typename_runme.php
@@ -1,7 +1,6 @@
 <?php
 
 require "tests.php";
-require "template_arg_typename.php";
 
 // No new functions
 check::functions(array());
diff --git a/Examples/test-suite/php/template_construct_runme.php b/Examples/test-suite/php/template_construct_runme.php
index a8e554b..d09bc4d 100644
--- a/Examples/test-suite/php/template_construct_runme.php
+++ b/Examples/test-suite/php/template_construct_runme.php
@@ -1,7 +1,6 @@
 <?php
 
 require "tests.php";
-require "template_construct.php";
 
 check::classes(array('Foo_int'));
 $foo_int=new foo_int(3);
diff --git a/Examples/test-suite/php/tests.php b/Examples/test-suite/php/tests.php
index 264d4d3..cec6e58 100644
--- a/Examples/test-suite/php/tests.php
+++ b/Examples/test-suite/php/tests.php
@@ -1,86 +1,24 @@
 <?php
 
-function die_on_error($errno, $errstr, $file, $line) {
-    if ($file !== Null) {
-        print $file;
-        if ($line !== Null) print ":$line";
-        print ": ";
-    }
-    print "$errstr\n";
-    exit(1);
-}
-set_error_handler("die_on_error", -1);
-
-$_original_functions=get_defined_functions();
-$_original_globals=1;
-$_original_classes=get_declared_classes();
-$_original_globals=array_keys($GLOBALS);
-
 class check {
   // do we have true global vars or just GETSET functions?
   // Used to filter out get/set global functions to fake vars...
   const GETSET = 1;
 
-  static function get_extra_classes($ref=FALSE) {
-    static $extra;
-    global $_original_classes;
-    if ($ref===FALSE) $f=$_original_classes;
-    if (! is_array($extra)) {
-      $df=array_flip(get_declared_classes());
-      foreach($_original_classes as $class) unset($df[$class]);
-      $extra=array_keys($df);
-    }
-    return $extra;
-  }
+  private static $_extension = null;
 
-  static function get_extra_functions($ref=FALSE,$gs=false) {
-    static $extra;
-    static $extrags; // for get/setters
-    global $_original_functions;
-    if ($ref===FALSE) $f=$_original_functions;
-    if (! is_array($extra) || $gs) {
-      $extra=array();
-      $extrags=array();
-      $df=get_defined_functions();
-      $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 ((self::GETSET && preg_match('/_[gs]et$/', $func)) ||
-            preg_match('/^new_/', $func) ||
-            preg_match('/_(alter|get)_newobject$/', $func))
-          $extrags[]=$func;
-        else $extra[]=$func;
-//      $extra=array_keys($df);
+  // This is called automatically at the end of this file.
+  static function init() {
+    foreach(get_included_files() as $f) {
+      $module_name = preg_filter('/.*\/([^\/]+)_runme\.php$/', '\1', $f);
+      if ($module_name !== null) break;
     }
-    if ($gs) return $extrags;
-    return $extra;
-  }
+    if ($module_name === null) {
+      print("Failed to determine module name from get_included_files()\n");
+      exit(1);
+    }
 
-  static function get_extra_globals($ref=FALSE) {
-    static $extra;
-    global $_original_globals;
-    if (! is_array($extra)) {
-      if (self::GETSET) {
-        $_extra=array();
-        foreach(check::get_extra_functions(false,1) as $global) {
-          if (preg_match('/^(.*)_[sg]et$/', $global, $match))
-            $_extra[$match[1]] = 1;
-        }
-        $extra=array_keys($_extra);
-      } else {
-        if ($ref===FALSE) $ref=$_original_globals;
-        if (! is_array($extra)) {
-          $df=array_flip(array_keys($GLOBALS));
-          foreach($_original_globals as $func) unset($df[$func]);
-          // MASK xxxx_LOADED__ variables
-          foreach(array_keys($df) as $func)
-            if (preg_match('/_LOADED__$/', $func)) unset($df[$func]);
-          $extra=array_keys($df);
-        }
-      }
-    }
-    return $extra;
+    self::$_extension = new ReflectionExtension($module_name);
   }
 
   static function classname($string,$object) {
@@ -148,7 +86,8 @@
     if (! is_array($classes)) $classes=array($classes);
     $message=array();
     $missing=array();
-    $extra=array_flip(check::get_extra_classes());
+    $extra = array_flip(array_filter(self::$_extension->getClassNames(),
+				     function ($e) { return !preg_match('/^SWIG\\\\/', $e); }));
     foreach($classes as $class) {
       if (! class_exists($class)) $missing[]=$class;
       else unset($extra[$class]);
@@ -164,15 +103,16 @@
     if (! is_array($functions)) $functions=array($functions);
     $message=array();
     $missing=array();
-    $extra=array_flip(check::get_extra_functions());
-
+    $extra = self::$_extension->getFunctions();
     foreach ($functions as $func) {
       if (! function_exists($func)) $missing[]=$func;
       else unset($extra[$func]);
     }
+    $extra = array_filter(array_keys($extra),
+			  function ($e) { return !preg_match('/_[gs]et$/', $e); });
     if ($missing) $message[]=sprintf("Functions missing: %s",join(",",$missing));
     if ($message) return check::fail(join("\n  ",$message));
-    if ($extra) $message[]=sprintf("These extra functions are defined: %s",join(",",array_keys($extra)));
+    if ($extra) $message[]=sprintf("These extra functions are defined: %s",join(",",$extra));
     if ($message) return check::warn(join("\n  ",$message));
     return TRUE;    
   }
@@ -181,19 +121,19 @@
     if (! is_array($globals)) $globals=array($globals);
     $message=array();
     $missing=array();
-    $extra=array_flip(check::get_extra_globals());
+    $extra = self::$_extension->getFunctions();
     foreach ($globals as $glob) {
-      if (self::GETSET) {
-        if (! isset($extra[$glob])) $missing[]=$glob;
-        else unset($extra[$glob]);
-      } else {
-        if (! isset($GLOBALS[$glob])) $missing[]=$glob;
-        else unset($extra[$glob]);
+      if (! function_exists($glob . "_get") && ! function_exists($glob . "_set")) $missing[]=$glob;
+      else {
+        unset($extra[$glob . "_get"]);
+        unset($extra[$glob . "_set"]);
       }
     }
+    $extra = array_filter(array_keys($extra),
+			  function ($e) { return preg_match('/_[gs]et$/', $e); });
     if ($missing) $message[]=sprintf("Globals missing: %s",join(",",$missing));
     if ($message) return check::fail(join("\n  ",$message));
-    if ($extra) $message[]=sprintf("These extra globals are defined: %s",join(",",array_keys($extra)));
+    if ($extra) $message[]=sprintf("These extra globals are defined: %s",join(",",$extra));
     if ($message) return check::warn(join("\n  ",$message));
     return TRUE;    
 
@@ -210,24 +150,13 @@
     return TRUE;
   }
 
-  static function resource($a,$b,$message) {
-    $resource=trim(check::var_dump($a));
-    if (! preg_match("/^resource\([0-9]+\) of type \($b\)/i", $resource))
-      return check::fail($message);
+  static function equivalent($a,$b,$message) {
+    if (! ($a==$b)) return check::fail($message . ": '$a'!='$b'");
     return TRUE;
   }
 
   static function isnull($a,$message) {
-    $value=trim(check::var_dump($a));
-    return check::equal($value,"NULL",$message);
-  }
-
-  static function var_dump($arg) {
-    ob_start();
-    var_dump($arg);
-    $result=ob_get_contents();
-    ob_end_clean();
-    return $result;
+    return check::equal($a,NULL,$message);
   }
 
   static function fail($pattern) {
@@ -246,3 +175,5 @@
 #    print $_SERVER[argv][0]." ok\n";
   }
 }
+
+check::init();
diff --git a/Examples/test-suite/php/threads_exception_runme.php b/Examples/test-suite/php/threads_exception_runme.php
index 80717eb..38873df 100644
--- a/Examples/test-suite/php/threads_exception_runme.php
+++ b/Examples/test-suite/php/threads_exception_runme.php
@@ -1,14 +1,13 @@
 <?php
 
 require "tests.php";
-require "threads_exception.php";
 
 // Check functions
-check::functions(array('test_simple','test_message','test_hosed','test_unknown','test_multi','is_python_builtin'));
+check::functions(array('is_python_builtin'));
 // Check classes.
 check::classes(array('Exc','Test','threads_exception'));
-// Check globals.
-check::globals(array('exc_code','exc_msg'));
+// No new vars
+check::globals(array());
 
 $t = new Test();
 try {
diff --git a/Examples/test-suite/php/typedef_reference_runme.php b/Examples/test-suite/php/typedef_reference_runme.php
index ee6cf48..8309c3c 100644
--- a/Examples/test-suite/php/typedef_reference_runme.php
+++ b/Examples/test-suite/php/typedef_reference_runme.php
@@ -1,7 +1,6 @@
 <?php
 
 require "tests.php";
-require "typedef_reference.php";
 
 check::functions(array('somefunc','otherfunc','new_intp','copy_intp','delete_intp','intp_assign','intp_value'));
 $int2=copy_intp(2);
diff --git a/Examples/test-suite/php/typemap_ns_using_runme.php b/Examples/test-suite/php/typemap_ns_using_runme.php
index d679af9..e154278 100644
--- a/Examples/test-suite/php/typemap_ns_using_runme.php
+++ b/Examples/test-suite/php/typemap_ns_using_runme.php
@@ -1,7 +1,7 @@
 <?php
 
 require "tests.php";
-require "typemap_ns_using.php";
+
 if (! class_exists("_fooimpl")) die("_fooimpl class not found\n");
 if (! 3==spam(3)) die("spam function not working right\n");
 
diff --git a/Examples/test-suite/php/using1_runme.php b/Examples/test-suite/php/using1_runme.php
index 0f3c915..e154278 100644
--- a/Examples/test-suite/php/using1_runme.php
+++ b/Examples/test-suite/php/using1_runme.php
@@ -1,7 +1,7 @@
 <?php
 
 require "tests.php";
-require "using1.php";
+
 if (! class_exists("_fooimpl")) die("_fooimpl class not found\n");
 if (! 3==spam(3)) die("spam function not working right\n");
 
diff --git a/Examples/test-suite/php/using2_runme.php b/Examples/test-suite/php/using2_runme.php
index fa79cb2..e154278 100644
--- a/Examples/test-suite/php/using2_runme.php
+++ b/Examples/test-suite/php/using2_runme.php
@@ -1,7 +1,7 @@
 <?php
 
 require "tests.php";
-require "using2.php";
+
 if (! class_exists("_fooimpl")) die("_fooimpl class not found\n");
 if (! 3==spam(3)) die("spam function not working right\n");
 
diff --git a/Examples/test-suite/php/valuewrapper_base_runme.php b/Examples/test-suite/php/valuewrapper_base_runme.php
index d20df4e..3f4c38e 100644
--- a/Examples/test-suite/php/valuewrapper_base_runme.php
+++ b/Examples/test-suite/php/valuewrapper_base_runme.php
@@ -1,10 +1,9 @@
 <?php
 
 require "tests.php";
-require "valuewrapper_base.php";
 
 check::classes(array("valuewrapper_base","Base","Interface_BP"));
-check::functions("make_interface_bp");
+check::functions("make_Interface_BP");
 
 $ibp=valuewrapper_base::make_interface_bp();
 check::classname("interface_bp",$ibp);
diff --git a/Examples/test-suite/php/virtual_vs_nonvirtual_base_runme.php b/Examples/test-suite/php/virtual_vs_nonvirtual_base_runme.php
index d83210e..abfb4d3 100644
--- a/Examples/test-suite/php/virtual_vs_nonvirtual_base_runme.php
+++ b/Examples/test-suite/php/virtual_vs_nonvirtual_base_runme.php
@@ -1,7 +1,6 @@
 <? 
 
 require "tests.php";
-require "virtual_vs_nonvirtual_base.php";
 
 $fail = new SimpleClassFail();
 $work = new SimpleClassWork();
diff --git a/Examples/test-suite/php/wrapmacro_runme.php b/Examples/test-suite/php/wrapmacro_runme.php
index f79ddf9..18b2ef1 100644
--- a/Examples/test-suite/php/wrapmacro_runme.php
+++ b/Examples/test-suite/php/wrapmacro_runme.php
@@ -1,9 +1,8 @@
 <?php
 
 require "tests.php";
-require "wrapmacro.php";
 
-check::functions(array('guint16_swap_le_be_constant', 'maximum'));
+check::functions(array('GUINT16_SWAP_LE_BE_CONSTANT', 'maximum'));
 
 check::equal(maximum(2.3, 2.4), 2.4, "maximum() doesn't work");
 check::equal(guint16_swap_le_be_constant(0x1234), 0x3412, "GUINT16_SWAP_LE_BE_CONSTANT() doesn't work");
diff --git a/Lib/php/const.i b/Lib/php/const.i
index 32b4b9b..79c6d24 100644
--- a/Lib/php/const.i
+++ b/Lib/php/const.i
@@ -3,6 +3,57 @@
  *
  * Typemaps for constants
  * ----------------------------------------------------------------------------- */
+%typemap(classconsttab) int,
+                        unsigned int,
+                        short,
+                        unsigned short,
+                        long,
+                        unsigned long,
+                        unsigned char,
+                        signed char,
+                        enum SWIGTYPE %{
+  zend_declare_class_constant_long(SWIGTYPE_$class_ce, "$const_name", sizeof("$const_name") - 1, ($1_type)$value);
+%}
+
+%typemap(classconsttab) bool %{
+  zend_declare_class_constant_bool(SWIGTYPE_$class_ce, "$const_name", sizeof("$const_name") - 1, ($1_type)$value);
+%}
+
+%typemap(classconsttab) float,
+                        double %{
+  zend_declare_class_constant_double(SWIGTYPE_$class_ce, "$const_name", sizeof("$const_name") - 1, $value);
+%}
+
+%typemap(classconsttab) char %{
+{
+  char swig_char = $value;
+  zend_declare_class_constant_stringl(SWIGTYPE_$class_ce, "$const_name", sizeof("$const_name") - 1, &swig_char, 1);
+}
+%}
+
+%typemap(classconsttab) char *,
+                        const char *,
+                        char [],
+                        const char [] %{
+  zend_declare_class_constant_string(SWIGTYPE_$class_ce, "$const_name", sizeof("$const_name") - 1, $value);
+%}
+
+// This creates a zend_object to wrap the pointer, and we can't do that
+// before the Zend runtime has been initialised so we delay it until
+// RINIT.  The downside is it then happens for every request.
+%typemap(classconsttab,rinit=1) SWIGTYPE *,
+                        SWIGTYPE &,
+                        SWIGTYPE &&,
+                        SWIGTYPE [] %{
+{
+  zval z;
+  SWIG_SetPointerZval(&z, (void*)$value, $1_descriptor, 0);
+  zval_copy_ctor(&z);
+  zend_declare_class_constant(SWIGTYPE_$class_ce, "$const_name", sizeof("$const_name") - 1, &z);
+}
+%}
+
+%typemap(classconsttab) SWIGTYPE (CLASS::*) "";
 
 %typemap(consttab) int,
                    unsigned int,
@@ -31,7 +82,10 @@
                    const char []
   "SWIG_STRING_CONSTANT($symname, $value);";
 
-%typemap(consttab) SWIGTYPE *,
+// This creates a zend_object to wrap the pointer, and we can't do that
+// before the Zend runtime has been initialised so we delay it until
+// RINIT.  The downside is it then happens for every request.
+%typemap(consttab,rinit=1) SWIGTYPE *,
                    SWIGTYPE &,
                    SWIGTYPE &&,
                    SWIGTYPE [] {
diff --git a/Lib/php/director.swg b/Lib/php/director.swg
index ea0eba8..ead731a 100644
--- a/Lib/php/director.swg
+++ b/Lib/php/director.swg
@@ -8,6 +8,8 @@
 #ifndef SWIG_DIRECTOR_PHP_HEADER_
 #define SWIG_DIRECTOR_PHP_HEADER_
 
+#define SWIG_DIRECTOR_CAST(ARG) dynamic_cast<Swig::Director *>(ARG)
+
 #include <string>
 #include <exception>
 #include <map>
@@ -76,31 +78,39 @@
   };
 
   class Director {
+    private:
+      /* flag indicating whether the object is owned by PHP or C++ */
+      mutable bool swig_disown_flag;
+
     protected:
       // "mutable" so we can get a non-const pointer to it in const methods.
       mutable zval swig_self;
       typedef std::map<void *, GCItem_var> swig_ownership_map;
       mutable swig_ownership_map swig_owner;
+
     public:
-      Director(zval *self) {
+      Director(zval *self) : swig_disown_flag(false) {
         ZVAL_COPY_VALUE(&swig_self, self);
       }
 
-      static bool swig_is_overridden_method(const char *cname, const char *lc_fname) {
-        bool result = false;
+      ~Director() {
+        if (swig_disown_flag) {
+          Z_DELREF(swig_self);
+	}
+      }
+
+      void swig_disown() const {
+        if (!swig_disown_flag) {
+          swig_disown_flag = true;
+          Z_ADDREF(swig_self);
+        }
+      }
+
+      static bool swig_is_overridden_method(const char *cname, zval *z) {
         zend_string * cname_str = zend_string_init(cname, strlen(cname), 0);
         zend_class_entry *ce = zend_lookup_class(cname_str);
-        if (ce) {
-          zval * mptr = zend_hash_str_find(&ce->function_table, lc_fname, strlen(lc_fname));
-          if (mptr) {
-            // common.scope points to zend_class_entry for the declaring class,
-            // and there's only one of those per class, so we can just use a
-            // pointer compare here.
-            result = Z_FUNC_P(mptr)->common.scope != ce;
-          }
-        }
         zend_string_release(cname_str);
-        return result;
+        return ce == Z_OBJCE_P(z);
       }
 
       template <typename Type>
@@ -109,6 +119,12 @@
           swig_owner[vptr] = new GCItem_T<Type>(vptr);
         }
       }
+
+      void swig_acquire_ownership_obj(void *vptr, int own) const {
+        if (vptr && own) {
+          swig_owner[vptr] = new GCItem_Object(own);
+        }
+      }
   };
 
   /* base class for director exceptions */
@@ -150,8 +166,7 @@
   };
 
   /* any php exception that occurs during a director method call */
-  class DirectorMethodException : public DirectorException
-  {
+  class DirectorMethodException : public DirectorException {
   public:
     DirectorMethodException()
       : DirectorException(E_ERROR, "SWIG director method error", NULL) {
diff --git a/Lib/php/factory.i b/Lib/php/factory.i
index b47b479..5a1f9dc 100644
--- a/Lib/php/factory.i
+++ b/Lib/php/factory.i
@@ -95,7 +95,7 @@
   Type *dobj = dynamic_cast<Type *>($1);
   if (dobj) {
     dcast = 1;
-    SWIG_SetPointerZval(return_value, SWIG_as_voidptr(dobj),$descriptor(Type *), $owner);
+    SWIG_SetPointerZval(return_value, SWIG_as_voidptr(dobj), $descriptor(Type *), $owner);
   }
 }%enddef
 
@@ -104,6 +104,6 @@
   int dcast = 0;
   %formacro(%_factory_dispatch, Types)
   if (!dcast) {
-    SWIG_SetPointerZval(return_value, SWIG_as_voidptr($1),$descriptor, $owner);
+    SWIG_SetPointerZval(return_value, SWIG_as_voidptr($1), $descriptor, $owner);
   }
 }%enddef
diff --git a/Lib/php/globalvar.i b/Lib/php/globalvar.i
deleted file mode 100644
index 6b31207..0000000
--- a/Lib/php/globalvar.i
+++ /dev/null
@@ -1,293 +0,0 @@
-/* -----------------------------------------------------------------------------
- * globalvar.i
- *
- * Global variables - add the variable to PHP
- * ----------------------------------------------------------------------------- */
-
-%typemap(varinit) char *
-{
-  zval z_var;
-  if ($1) {
-    ZVAL_STRING(&z_var, $1);
-  } else {
-    ZVAL_STR(&z_var, ZSTR_EMPTY_ALLOC());
-  }
-  zend_hash_str_add(&EG(symbol_table), "$1", sizeof("$1") - 1, &z_var);
-}
-
-%typemap(varinit) char []
-{
-  zval z_var;
-  ZVAL_STRING(&z_var, $1);
-  zend_hash_str_add(&EG(symbol_table), "$1", sizeof("$1") - 1, &z_var);
-}
-
-%typemap(varinit) int,
-	          unsigned int,
-                  unsigned short,
-                  short,
-                  unsigned short,
-                  long,
-                  unsigned long,
-                  signed char,
-                  unsigned char,
-                  enum SWIGTYPE
-{
-  zval z_var;
-  ZVAL_LONG(&z_var, (long)$1);
-  zend_hash_str_add(&EG(symbol_table), "$1", sizeof("$1") - 1, &z_var);
-}
-
-%typemap(varinit) bool
-{
-  zval z_var;
-  ZVAL_BOOL(&z_var, ($1)?1:0);
-  zend_hash_str_add(&EG(symbol_table), "$1", sizeof("$1") - 1, &z_var);
-}
-
-%typemap(varinit) float, double
-{
-  zval z_var;
-  ZVAL_DOUBLE(&z_var, (double)$1);
-  zend_hash_str_add(&EG(symbol_table), "$1", sizeof("$1") - 1, &z_var);
-}
-
-%typemap(varinit) char
-{
-  zval z_var;
-  char c = $1;
-  ZVAL_STRINGL(&z_var, &c, 1);
-  zend_hash_str_add(&EG(symbol_table), "$1", sizeof("$1") - 1, &z_var);
-}
-
-%typemap(varinit) SWIGTYPE *, SWIGTYPE []
-{
-  zval z_var;
-  SWIG_SetPointerZval(&z_var, (void*)$1, $1_descriptor, 0);
-  zend_hash_str_add(&EG(symbol_table), "$1", sizeof("$1") - 1, &z_var);
-}
-
-%typemap(varinit) SWIGTYPE, SWIGTYPE &, SWIGTYPE &&
-{
-  zval z_var;
-  SWIG_SetPointerZval(&z_var, (void*)&$1, $&1_descriptor, 0);
-  zend_hash_str_add(&EG(symbol_table), "$1", sizeof("$1") - 1, &z_var);
-}
-
-%typemap(varinit) char [ANY]
-{
-  zval z_var;
-  /* varinit char [ANY] */
-  ZVAL_STRINGL(&z_var, $1, $1_dim0);
-  zend_hash_str_add(&EG(symbol_table), "$1", sizeof("$1") - 1, &z_var);
-}
-
-%typemap(varinit, fragment="swig_php_init_member_ptr") SWIGTYPE (CLASS::*)
-{
-  zval resource;
-  void * p = emalloc(sizeof($1));
-  memcpy(p, &$1, sizeof($1));
-  ZVAL_RES(&resource, zend_register_resource(p, swig_member_ptr));
-  zend_hash_str_add(&EG(symbol_table), "$1", sizeof("$1") - 1, &resource);
-}
-
-%typemap(varin) int, unsigned int, short, unsigned short, long, unsigned long, signed char, unsigned char,  enum SWIGTYPE
-{
-  zval *z_var = zend_hash_str_find(&EG(symbol_table), "$1", sizeof("$1") - 1);
-  $1 = zval_get_long(z_var);
-}
-
-%typemap(varin) bool
-{
-  zval *z_var = zend_hash_str_find(&EG(symbol_table), "$1", sizeof("$1") - 1);
-  convert_to_boolean(z_var);
-  $1 = (Z_TYPE_P(z_var) == IS_TRUE);
-}
-
-%typemap(varin) double,float
-{
-  zval *z_var = zend_hash_str_find(&EG(symbol_table), "$1", sizeof("$1") - 1);
-  $1 = zval_get_double(z_var);
-}
-
-%typemap(varin) char
-{
-  zval *z_var = zend_hash_str_find(&EG(symbol_table), "$1", sizeof("$1") - 1);
-  convert_to_string(z_var);
-  if ($1 != Z_STRVAL_P(z_var)[0]) {
-    $1 = Z_STRVAL_P(z_var)[0];
-  }
-}
-
-%typemap(varin) char *
-{
-  zval *z_var = zend_hash_str_find(&EG(symbol_table), "$1", sizeof("$1") - 1);
-  char *s1;
-  convert_to_string(z_var);
-  s1 = Z_STRVAL_P(z_var);
-  if ((s1 == NULL) || ($1 == NULL) || strcmp(s1, $1)) {
-    if (s1)
-      $1 = estrdup(s1);
-    else
-      $1 = NULL;
-  }
-}
-
-
-%typemap(varin) SWIGTYPE []
-{
-  if ($1) {
-    zval *z_var = zend_hash_str_find(&EG(symbol_table), "$1", sizeof("$1") - 1);
-    SWIG_SetPointerZval(z_var, (void*)$1, $1_descriptor, $owner);
-  }
-}
-
-%typemap(varin) char [ANY]
-{
-  zval **z_var;
-  char *s1;
-
-  zend_hash_str_find(&EG(symbol_table), "$1", sizeof("$1") - 1, (void**)&z_var);
-  s1 = Z_STRVAL_P(z_var);
-  if ((s1 == NULL) || ($1 == NULL) || strcmp(s1, $1)) {
-    if (s1)
-      strncpy($1, s1, $1_dim0);
-  }
-}
-
-%typemap(varin) SWIGTYPE
-{
-  zval *z_var;
-  $&1_ltype _temp;
-
-  z_var = zend_hash_str_find(&EG(symbol_table), "$1", sizeof("$1") - 1);
-  if (SWIG_ConvertPtr(z_var, (void**)&_temp, $&1_descriptor, 0) < 0) {
-    SWIG_PHP_Error(E_ERROR,"Type error in value of $symname. Expected $&1_descriptor");
-  }
-
-  $1 = *($&1_ltype)_temp;
-}
-
-%typemap(varin) SWIGTYPE *, SWIGTYPE &, SWIGTYPE &&
-{
-  zval *z_var;
-  $1_ltype _temp;
-
-  z_var = zend_hash_str_find(&EG(symbol_table), "$1", sizeof("$1") - 1);
-  if (SWIG_ConvertPtr(z_var, (void **)&_temp, $1_descriptor, 0) < 0) {
-    SWIG_PHP_Error(E_ERROR,"Type error in value of $symname. Expected $&1_descriptor");
-  }
-
-  $1 = ($1_ltype)_temp;
-}
-
-%typemap(varin, fragment="swig_php_init_member_ptr") SWIGTYPE (CLASS::*)
-{
-  zval *z_var = zend_hash_str_find(&EG(symbol_table), "$1", sizeof("$1") - 1);
-  void * p = (void*)zend_fetch_resource_ex(z_var, SWIG_MEMBER_PTR, swig_member_ptr);
-  memcpy(&$1, p, sizeof($1));
-}
-
-%typemap(varout) int,
-                 unsigned int,
-                 unsigned short,
-                 short,
-                 long,
-                 unsigned long,
-                 signed char,
-                 unsigned char,
-                 enum SWIGTYPE
-{
-  zval *z_var = zend_hash_str_find(&EG(symbol_table), "$1", sizeof("$1") - 1);
-  if ($1 != ($1_ltype)Z_LVAL_P(z_var)) {
-    z_var->value.lval = (long)$1;
-  }
-}
-
-//SAMFIX need to cast zval->type, what if zend-hash_find fails? etc?
-%typemap(varout) bool
-{
-  zval *z_var = zend_hash_str_find(&EG(symbol_table), "$1", sizeof("$1") - 1);
-  if ($1 != ($1_ltype)Z_LVAL_P(z_var)) {
-    z_var->value.lval = (long)$1;
-  }
-}
-
-%typemap(varout) double, float
-{
-  zval *z_var = zend_hash_str_find(&EG(symbol_table), "$1", sizeof("$1") - 1);
-  if ($1 != ($1_ltype)Z_DVAL_P(z_var)) {
-    z_var->value.dval = (double)$1;
-  }
-}
-
-%typemap(varout) char
-{
-  zval *z_var = zend_hash_str_find(&EG(symbol_table), "$1", sizeof("$1") - 1);
-  char c = $1;
-  if ($1 != Z_STRVAL_P(z_val)[0]) {
-    ZVAL_STRING(z_var, &c);
-  }
-}
-
-%typemap(varout) char *
-{
-  zval *z_var = zend_hash_str_find(&EG(symbol_table), "$1", sizeof("$1") - 1);
-  const char *s1 = Z_STRVAL_P(z_var);
-  if ((s1 == NULL) || ($1 == NULL) || strcmp(s1, $1)) {
-    if (s1)
-      efree(s1);
-    if ($1) {
-      (z_var)->value.str.val = estrdup($1);
-      (z_var)->value.str.len = strlen($1) + 1;
-    } else {
-      (z_var)->value.str.val = 0;
-      (z_var)->value.str.len = 0;
-    }
-  }
-}
-
-%typemap(varout) SWIGTYPE
-{
-  zval *z_var = zend_hash_str_find(&EG(symbol_table), "$1", sizeof("$1") - 1);
-  SWIG_SetPointerZval(z_var, (void*)&$1, $&1_descriptor, 0);
-}
-
-%typemap(varout) SWIGTYPE []
-{
-  if($1) {
-    zval *z_var = zend_hash_str_find(&EG(symbol_table), "$1", sizeof("$1") - 1);
-    SWIG_SetPointerZval(z_var, (void*)$1, $1_descriptor, 0);
-  }
-}
-
-%typemap(varout) char [ANY]
-{
-  zval *z_var = zend_hash_str_find(&EG(symbol_table), "$1", sizeof("$1") - 1);
-  const char *s1 = Z_STRVAL_P(z_var);
-deliberate error cos this code looks bogus to me
-  if ((s1 == NULL) || strcmp(s1, $1)) {
-    if ($1) {
-      (z_var)->value.str.val = estrdup($1);
-      (z_var)->value.str.len = strlen($1) + 1;
-    } else {
-      (z_var)->value.str.val = 0;
-      (z_var)->value.str.len = 0;
-    }
-  }
-}
-
-%typemap(varout) SWIGTYPE *, SWIGTYPE &, SWIGTYPE &&
-{
-  zval *z_var = zend_hash_str_find(&EG(symbol_table), "$1", sizeof("$1") - 1);
-  SWIG_SetPointerZval(z_var, (void*)$1, $1_descriptor, 0);
-}
-
-%typemap(varout, fragment="swig_php_init_member_ptr") SWIGTYPE (CLASS::*)
-{
-  zval resource;
-  void * p = emalloc(sizeof($1));
-  memcpy(p, &$1, sizeof($1));
-  ZVAL_RES(&resource, zend_register_resource(p, swig_member_ptr));
-  zend_hash_str_add(&EG(symbol_table), "$1", sizeof("$1") - 1, &resource);
-}
diff --git a/Lib/php/php.swg b/Lib/php/php.swg
index 07213fa..468c7bb 100644
--- a/Lib/php/php.swg
+++ b/Lib/php/php.swg
@@ -10,7 +10,6 @@
 
 %include <phpinit.swg> // PHP initialization routine.
 
-%include <globalvar.i>	// Global variables.
 %include <const.i>
 
 // use %init %{ "/*code goes here*/ " %}
@@ -94,12 +93,10 @@
 
 %typemap(directorout) SWIGTYPE ($&1_ltype tmp)
 %{
-  /* If exit was via exception, PHP NULL is returned so skip the conversion. */
-  if (!EG(exception)) {
-    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;
+  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 *,
@@ -110,14 +107,32 @@
   }
 %}
 
+%typemap(directorout) SWIGTYPE * (swig_owntype own),
+		      SWIGTYPE [] (swig_owntype own)
+%{
+  if (SWIG_ConvertPtrAndOwn($input, (void **)&$result, $1_descriptor, SWIG_POINTER_DISOWN, &own) < 0) {
+    SWIG_PHP_Error(E_ERROR, "Type error in argument $argnum of $symname. Expected $1_descriptor");
+  }
+  swig_acquire_ownership_obj((void*)$result, own);
+%}
+
 %typemap(in) SWIGTYPE &,
-	     SWIGTYPE &&
+             SWIGTYPE &&
 %{
   if (SWIG_ConvertPtr(&$input, (void **) &$1, $1_descriptor, 0) < 0 || $1 == NULL) {
     SWIG_PHP_Error(E_ERROR, "Type error in argument $argnum of $symname. Expected $1_descriptor");
   }
 %}
 
+%typemap(directorout) SWIGTYPE & ($1_ltype tmp),
+		      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 *const& ($*ltype temp)
 %{
   if (SWIG_ConvertPtr(&$input, (void **) &temp, $*1_descriptor, 0) < 0) {
@@ -151,7 +166,7 @@
 
 /* Special case when void* is passed by reference so it can be made to point
    to opaque api structs */
-%typemap(in) void ** ($*1_ltype ptr, int force),
+%typemap(in, byref=1) void ** ($*1_ltype ptr, int force),
              void *& ($*1_ltype ptr, int force)
 {
   /* If they pass NULL by reference, make it into a void*
@@ -178,8 +193,8 @@
 %typemap(argout) void **,
                  void *&
 %{
-  if (force$argnum) {
-    SWIG_SetPointerZval(&$input, (void*) ptr$argnum, $*1_descriptor, 1);
+  if (force$argnum && Z_ISREF($input)) {
+    SWIG_SetPointerZval(Z_REFVAL($input), (void*) ptr$argnum, $*1_descriptor, 1);
   }
 %}
 
@@ -365,12 +380,12 @@
               SWIGTYPE &,
               SWIGTYPE &&
 %{
-  SWIG_SetPointerZval(return_value, (void *)$1, $1_descriptor, $owner);
+  SWIG_SetPointerZval($result, (void *)$1, $1_descriptor, $owner);
 %}
 
 %typemap(out) SWIGTYPE *const&
 %{
-  SWIG_SetPointerZval(return_value, (void *)*$1, $*1_descriptor, $owner);
+  SWIG_SetPointerZval($result, (void *)*$1, $*1_descriptor, $owner);
 %}
 
 %typemap(directorin) SWIGTYPE *,
@@ -378,19 +393,19 @@
                      SWIGTYPE &,
                      SWIGTYPE &&
 %{
-  SWIG_SetPointerZval($input, (void *)&$1, $1_descriptor, ($owner)|2);
+  SWIG_SetPointerZval($input, (void *)&$1, $1_descriptor, $owner);
 %}
 
-%typemap(out, fragment="swig_php_init_member_ptr") SWIGTYPE (CLASS::*)
+%typemap(out) SWIGTYPE (CLASS::*)
 {
   void * p = emalloc(sizeof($1));
   memcpy(p, &$1, sizeof($1));
-  RETVAL_RES(zend_register_resource(p, swig_member_ptr));
+  SWIG_SetPointerZval($result, (void *)p, $&1_descriptor, 1);
 }
 
-%typemap(in, fragment="swig_php_init_member_ptr") SWIGTYPE (CLASS::*)
+%typemap(in) SWIGTYPE (CLASS::*)
 {
-  void * p = (void*)zend_fetch_resource_ex(&$input, SWIG_MEMBER_PTR, swig_member_ptr);
+  void * p = SWIG_Z_FETCH_OBJ_P(&$input)->ptr;
   memcpy(&$1, p, sizeof($1));
 }
 
@@ -398,33 +413,30 @@
               SWIGTYPE &DYNAMIC
 {
   swig_type_info *ty = SWIG_TypeDynamicCast($1_descriptor, (void **) &$1);
-  SWIG_SetPointerZval(return_value, (void *)$1, ty, $owner);
+  SWIG_SetPointerZval($result, (void *)$1, ty, $owner);
 }
 
 %typemap(out) SWIGTYPE
+{
 #ifdef __cplusplus
-{
   $&1_ltype resultobj = new $1_ltype((const $1_ltype &) $1);
-  SWIG_SetPointerZval(return_value, (void *)resultobj, $&1_descriptor, 1);
-}
 #else
-{
-  $&1_ltype resultobj = ($&1_ltype) emalloc(sizeof($1_type));
+  $&1_ltype resultobj = ($&1_ltype) malloc(sizeof($1_type));
   memcpy(resultobj, &$1, sizeof($1_type));
-  SWIG_SetPointerZval(return_value, (void *)resultobj, $&1_descriptor, 1);
-}
 #endif
+  SWIG_SetPointerZval($result, (void *)resultobj, $&1_descriptor, 1);
+}
 
 %typemap(directorin) SWIGTYPE
 %{
-  SWIG_SetPointerZval($input, SWIG_as_voidptr(new $1_ltype((const $1_ltype &)$1)), $&1_descriptor, 1|2);
+  SWIG_SetPointerZval($input, SWIG_as_voidptr(new $1_ltype((const $1_ltype &)$1)), $&1_descriptor, 1);
 %}
 
 %typemap(out) void "";
 
 %typemap(out) char [ANY]
 {
-  int len = 0;
+  size_t len = 0;
   while (len < $1_dim0 && $1[len]) ++len;
   RETVAL_STRINGL($1, len);
 }
diff --git a/Lib/php/phpinit.swg b/Lib/php/phpinit.swg
index 1665f5d..40f7b07 100644
--- a/Lib/php/phpinit.swg
+++ b/Lib/php/phpinit.swg
@@ -7,19 +7,6 @@
 
 %init %{
 SWIG_php_minit {
+  zend_class_entry SWIGUNUSED internal_ce;
   SWIG_InitializeModule((void*)&module_number);
 %}
-
-%fragment("swig_php_init_member_ptr2", "header") %{
-#define SWIG_MEMBER_PTR "CLASS::*"
-
-static void swig_member_ptr_dtor(zend_resource *res) {
-  efree(res->ptr);
-}
-
-static int swig_member_ptr = 0;
-%}
-
-%fragment("swig_php_init_member_ptr", "init", fragment="swig_php_init_member_ptr2") %{
-  swig_member_ptr = zend_register_list_destructors_ex(swig_member_ptr_dtor, NULL, SWIG_MEMBER_PTR, module_number);
-%}
diff --git a/Lib/php/phppointers.i b/Lib/php/phppointers.i
index d79697b..1475683 100644
--- a/Lib/php/phppointers.i
+++ b/Lib/php/phppointers.i
@@ -2,15 +2,11 @@
 %typemap(in, byref=1) TYPE *REF ($*1_ltype tmp),
              TYPE &REF ($*1_ltype tmp)
 %{
-  /* First Check for SWIG wrapped type */
-  if (Z_ISNULL($input)) {
-      $1 = 0;
-  } else if (Z_ISREF($input)) {
-      /* Not swig wrapped type, so we check if it's a PHP reference type */
-      CONVERT_IN(tmp, $*1_ltype, $input);
-      $1 = &tmp;
+  if (Z_ISREF($input)) {
+    CONVERT_IN(tmp, $*1_ltype, $input);
+    $1 = &tmp;
   } else {
-      SWIG_PHP_Error(E_ERROR, SWIG_PHP_Arg_Error_Msg($argnum, Expected a reference));
+    SWIG_PHP_Error(E_ERROR, SWIG_PHP_Arg_Error_Msg($argnum, Expected a reference));
   }
 %}
 %typemap(argout) TYPE *REF,
diff --git a/Lib/php/phprun.swg b/Lib/php/phprun.swg
index 44f1087..252c152 100644
--- a/Lib/php/phprun.swg
+++ b/Lib/php/phprun.swg
@@ -4,6 +4,8 @@
  * PHP runtime library
  * ----------------------------------------------------------------------------- */
 
+#define swig_owntype                                    int
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -19,10 +21,6 @@
 #include "ext/standard/php_string.h"
 #include <stdlib.h> /* for abort(), used in generated code. */
 
-/* This indirection is to work around const correctness issues in older PHP.
- * FIXME: Remove for PHP7?  Or might user code be using it? */
-#define SWIG_ZEND_NAMED_FE(ZN, N, A) ZEND_NAMED_FE(ZN, N, A)
-
 #define SWIG_BOOL_CONSTANT(N, V) REGISTER_BOOL_CONSTANT(#N, V, CONST_CS | CONST_PERSISTENT)
 #define SWIG_LONG_CONSTANT(N, V) REGISTER_LONG_CONSTANT(#N, V, CONST_CS | CONST_PERSISTENT)
 #define SWIG_DOUBLE_CONSTANT(N, V) REGISTER_DOUBLE_CONSTANT(#N, V, CONST_CS | CONST_PERSISTENT)
@@ -32,19 +30,36 @@
     REGISTER_STRINGL_CONSTANT(#N, &swig_char, 1, CONST_CS | CONST_PERSISTENT);\
 } while (0)
 
-/* ZEND_CONSTANT_SET_FLAGS is new in PHP 7.3. */
+/* ZEND_CONSTANT_SET_FLAGS was new in PHP 7.3. */
 #ifdef ZEND_CONSTANT_SET_FLAGS
 # define SWIG_ZEND_CONSTANT_SET_FLAGS ZEND_CONSTANT_SET_FLAGS
 #else
 # define SWIG_ZEND_CONSTANT_SET_FLAGS(C, F, N) do { (C)->flags = (F); (C)->module_number = (N); } while (0)
 #endif
 
+/* zend_object_alloc was new in PHP 7.3. */
+#if PHP_MAJOR_VERSION == 7 && PHP_MINOR_VERSION < 3
+static zend_always_inline void *zend_object_alloc(size_t obj_size, zend_class_entry *ce) {
+    void *obj = emalloc(obj_size + zend_object_properties_size(ce));
+    memset(obj, 0, obj_size - sizeof(zval));
+    return obj;
+}
+#endif
+
+/* ZEND_THIS was new in PHP 7.4. */
+#ifndef ZEND_THIS
+# define ZEND_THIS &EX(This)
+#endif
+
 #ifdef __cplusplus
 }
 #endif
 
 #define SWIG_fail goto fail
 
+// If there's an active PHP exception, just return so it can propagate.
+#define SWIG_FAIL() do { if (!EG(exception)) zend_error_noreturn(SWIG_ErrorCode(), "%s", SWIG_ErrorMsg()); goto thrown; } while (0)
+
 static const char *default_error_msg = "Unknown error occurred";
 static int default_error_code = E_ERROR;
 
@@ -53,7 +68,7 @@
 #define SWIG_PHP_Error(code,msg) do { SWIG_ErrorCode() = code; SWIG_ErrorMsg() = msg; SWIG_fail; } while (0)
 
 #define SWIG_contract_assert(expr,msg) \
-  if (!(expr) ) { zend_printf("Contract Assert Failed %s\n",msg ); } else
+  do { if (!(expr)) zend_printf("Contract Assert Failed %s\n", msg); } while (0)
 
 /* Standard SWIG API */
 #define SWIG_GetModule(clientdata) SWIG_Php_GetModule()
@@ -64,70 +79,49 @@
 typedef struct {
   void * ptr;
   int newobject;
+  const swig_type_info * type;
+  zend_object std;
 } swig_object_wrapper;
 
+#define SWIG_Z_FETCH_OBJ_P(zv) php_fetch_object(Z_OBJ_P(zv))
+
+static inline
+swig_object_wrapper * php_fetch_object(zend_object *obj) {
+  return (swig_object_wrapper *)((char *)obj - XtOffsetOf(swig_object_wrapper, std));
+}
+
 #define SWIG_as_voidptr(a) const_cast< void * >(static_cast< const void * >(a))
 
 static void
 SWIG_SetPointerZval(zval *z, void *ptr, swig_type_info *type, int newobject) {
-  /*
-   * First test for Null pointers.  Return those as PHP native NULL
-   */
-  if (!ptr ) {
+  // Return PHP NULL for a C/C++ NULL pointer.
+  if (!ptr) {
     ZVAL_NULL(z);
     return;
   }
-  if (type->clientdata) {
-    swig_object_wrapper *value;
-    if (! (*(int *)(type->clientdata)))
-      zend_error(E_ERROR, "Type: %s failed to register with zend",type->name);
-    value=(swig_object_wrapper *)emalloc(sizeof(swig_object_wrapper));
-    value->ptr=ptr;
-    value->newobject=(newobject & 1);
-    if ((newobject & 2) == 0) {
-      /* Just register the pointer as a resource. */
-      ZVAL_RES(z, zend_register_resource(value, *(int *)(type->clientdata)));
-    } else {
-      /*
-       * Wrap the resource in an object, the resource will be accessible
-       * via the "_cPtr" property. This code path is currently only used by
-       * directorin typemaps.
-       */
-      zend_class_entry *ce = NULL;
-      const char *type_name = type->name+3; /* +3 so: _p_Foo -> Foo */
-      size_t type_name_len;
-      const char * p;
 
-      /* Namespace__Foo -> Foo */
-      /* FIXME: ugly and goes wrong for classes with __ in their names. */
-      while ((p = strstr(type_name, "__")) != NULL) {
-        type_name = p + 2;
-      }
-      type_name_len = strlen(type_name);
-
-      if (SWIG_PREFIX_LEN > 0) {
-        zend_string * classname = zend_string_alloc(SWIG_PREFIX_LEN + type_name_len, 0);
-        memcpy(classname->val, SWIG_PREFIX, SWIG_PREFIX_LEN);
-        memcpy(classname->val + SWIG_PREFIX_LEN, type_name, type_name_len);
-        ce = zend_lookup_class(classname);
-        zend_string_release(classname);
-      } else {
-        zend_string * classname = zend_string_init(type_name, type_name_len, 0);
-        ce = zend_lookup_class(classname);
-        zend_string_release(classname);
-      }
-      if (ce == NULL) {
-        /* class does not exist */
-        object_init(z);
-      } else {
-        object_init_ex(z, ce);
-      }
-
-      add_property_resource_ex(z, "_cPtr", sizeof("_cPtr") - 1, zend_register_resource(value, *(int *)(type->clientdata)));
-    }
+  if (!type->clientdata) {
+    zend_error(E_ERROR, "Type: %s not registered with zend", type->name);
     return;
   }
-  zend_error(E_ERROR, "Type: %s not registered with zend",type->name);
+
+  {
+    zend_object *obj;
+    swig_object_wrapper *value;
+    if (Z_TYPE_P(z) == IS_OBJECT) {
+      /* The PHP object is already initialised - this is the case when wrapping
+       * the return value from a PHP constructor. */
+      obj = Z_OBJ_P(z);
+    } else {
+      zend_class_entry *ce = (zend_class_entry*)(type->clientdata);
+      obj = ce->create_object(ce);
+      ZVAL_OBJ(z, obj);
+    }
+    value = php_fetch_object(obj);
+    value->ptr = ptr;
+    value->newobject = (newobject & 1);
+    value->type = type;
+  }
 }
 
 /* This pointer conversion routine takes the native pointer p (along with
@@ -135,15 +129,10 @@
    according to ty.  The resultant pointer is returned, or NULL is returned
    if the pointer can't be cast.
 
-   Sadly PHP has no API to find a type name from a type id, only from an
-   instance of a resource of the type id, so we have to pass type_name as well.
-
-   The two functions which might call this are:
-   SWIG_ConvertResourcePtr which gets the type name from the resource
-   and the registered zend destructors for which we have one per type each
-   with the type name hard wired in. */
+   This is called by SWIG_ConvertPtr which gets the type name from the
+   swig_object_wrapper. */
 static void *
-SWIG_ConvertResourceData(void * p, const char *type_name, swig_type_info *ty) {
+SWIG_ConvertPtrData(void * p, const char *type_name, swig_type_info *ty, int *own) {
   swig_cast_info *tc;
   void *result = 0;
 
@@ -162,66 +151,36 @@
   if (tc) {
     int newmemory = 0;
     result = SWIG_TypeCast(tc, p, &newmemory);
-    assert(!newmemory); /* newmemory handling not yet implemented */
+    if (newmemory == SWIG_CAST_NEW_MEMORY) {
+      assert(own); /* badly formed typemap which will lead to a memory leak - it must set and use own to delete *ptr */
+      if (own)
+        *own |= SWIG_CAST_NEW_MEMORY;
+    }
   }
   return result;
 }
 
-/* This function returns a pointer of type ty by extracting the pointer
-   and type info from the resource in z.  z must be a resource.
-   If it fails, NULL is returned.
-   It uses SWIG_ConvertResourceData to do the real work. */
-static void *
-SWIG_ConvertResourcePtr(zval *z, swig_type_info *ty, int flags) {
-  swig_object_wrapper *value;
-  void *p;
-  const char *type_name;
-
-  if (Z_RES_TYPE_P(z) == -1) return NULL;
-  value = (swig_object_wrapper *) Z_RES_VAL_P(z);
-  if (flags & SWIG_POINTER_DISOWN) {
-    value->newobject = 0;
-  }
-  p = value->ptr;
-
-  type_name=zend_rsrc_list_get_rsrc_type(Z_RES_P(z));
-
-  return SWIG_ConvertResourceData(p, type_name, ty);
-}
-
-/* We allow passing of a RESOURCE pointing to the object or an OBJECT whose
-   _cPtr is a resource pointing to the object */
+/* We wrap C/C++ pointers as PHP objects. */
 static int
-SWIG_ConvertPtr(zval *z, void **ptr, swig_type_info *ty, int flags) {
+SWIG_ConvertPtrAndOwn(zval *z, void **ptr, swig_type_info *ty, int flags, swig_owntype *own) {
+  if (own)
+    *own = 0;
+
   if (z == NULL) {
     *ptr = 0;
-    return 0;
+    return (flags & SWIG_POINTER_NO_NULL) ? SWIG_NullReferenceError : SWIG_OK;
   }
 
   switch (Z_TYPE_P(z)) {
     case IS_OBJECT: {
-#if PHP_MAJOR_VERSION < 8
-      HashTable * ht = Z_OBJ_HT_P(z)->get_properties(z);
-#else
-      HashTable * ht = Z_OBJ_HT_P(z)->get_properties(Z_OBJ_P(z));
-#endif
-      if (ht) {
-        zval * _cPtr = zend_hash_str_find(ht, "_cPtr", sizeof("_cPtr") - 1);
-        if (_cPtr) {
-          if (Z_TYPE_P(_cPtr) == IS_INDIRECT) {
-            _cPtr = Z_INDIRECT_P(_cPtr);
-          }
-          if (Z_TYPE_P(_cPtr) == IS_RESOURCE) {
-            *ptr = SWIG_ConvertResourcePtr(_cPtr, ty, flags);
-            return (*ptr == NULL ? -1 : 0);
-          }
-        }
+      swig_object_wrapper *value = SWIG_Z_FETCH_OBJ_P(z);
+      *ptr = SWIG_ConvertPtrData(value->ptr, value->type->name, ty, own);
+      if (*ptr == NULL) return SWIG_ERROR;
+      if (flags & SWIG_POINTER_DISOWN) {
+	value->newobject = 0;
       }
-      break;
+      return SWIG_OK;
     }
-    case IS_RESOURCE:
-      *ptr = SWIG_ConvertResourcePtr(z, ty, flags);
-      return (*ptr == NULL ? -1 : 0);
     case IS_NULL:
       *ptr = 0;
       return (flags & SWIG_POINTER_NO_NULL) ? SWIG_NullReferenceError : SWIG_OK;
@@ -230,6 +189,11 @@
   return -1;
 }
 
+static int
+SWIG_ConvertPtr(zval *z, void **ptr, swig_type_info *ty, int flags) {
+  return SWIG_ConvertPtrAndOwn(z, ptr, ty, flags, 0);
+}
+
 static const char const_name[] = "swig_runtime_data_type_pointer";
 static swig_module_info *SWIG_Php_GetModule() {
   zval *pointer = zend_get_constant_str(const_name, sizeof(const_name) - 1);
diff --git a/Lib/php/std_string.i b/Lib/php/std_string.i
index b55751f..082a32c 100644
--- a/Lib/php/std_string.i
+++ b/Lib/php/std_string.i
@@ -33,10 +33,8 @@
     %}
 
     %typemap(directorout) string %{
-      if (!EG(exception)) {
         convert_to_string($input);
         $result.assign(Z_STRVAL_P($input), Z_STRLEN_P($input));
-      }
     %}
 
     %typemap(out) string %{
@@ -74,12 +72,10 @@
     %}
 
     %typemap(directorout) string & ($*1_ltype *temp) %{
-      if (!EG(exception)) {
         convert_to_string($input);
         temp = new $*1_ltype(Z_STRVAL_P($input), Z_STRLEN_P($input));
         swig_acquire_ownership(temp);
         $result = temp;
-      }
     %}
 
     %typemap(argout) string & %{
diff --git a/Lib/php/utils.i b/Lib/php/utils.i
index ed6e08f..b8fd909 100644
--- a/Lib/php/utils.i
+++ b/Lib/php/utils.i
@@ -17,8 +17,8 @@
           errno = 0;
           lvar = (t) strtoll(Z_STRVAL(invar), &endptr, 10);
           if (*endptr && !errno) break;
-          /* FALL THRU */
       }
+      /* FALL THRU */
       default:
           lvar = (t) zval_get_long(&invar);
   }
@@ -34,8 +34,8 @@
           errno = 0;
           lvar = (t) strtoull(Z_STRVAL(invar), &endptr, 10);
           if (*endptr && !errno) break;
-          /* FALL THRU */
       }
+      /* FALL THRU */
       default:
           lvar = (t) zval_get_long(&invar);
   }
@@ -75,16 +75,21 @@
 %}
 %typemap(directorout) TYPE
 %{
-  if (!EG(exception)) {
-    CONVERT_IN($result, $1_ltype, *$input);
-  }
+  CONVERT_IN($result, $1_ltype, *$input);
 %}
-%typemap(directorout) const TYPE & ($*1_ltype temp)
+%typemap(directorout) const TYPE &
 %{
-  if (!EG(exception)) {
-    CONVERT_IN(temp, $*1_ltype, *$input);
+  $*1_ltype swig_val;
+  CONVERT_IN(swig_val, $*1_ltype, *$input);
+  $1_ltype temp = new $*1_ltype(($*1_ltype)swig_val);
+  swig_acquire_ownership(temp);
+  $result = temp;
+%}
+%typemap(directorfree) const TYPE &
+%{
+  if (director) {
+    director->swig_release_ownership(%as_voidptr($input));
   }
-  $result = &temp;
 %}
 %enddef
 
diff --git a/Source/Modules/php.cxx b/Source/Modules/php.cxx
index 2c68fcd..e85dd69 100644
--- a/Source/Modules/php.cxx
+++ b/Source/Modules/php.cxx
@@ -12,57 +12,34 @@
  * -----------------------------------------------------------------------------
  */
 
-/* FIXME: PHP OO wrapping TODO list:
- *
- * Medium term:
- *
- * Handle default parameters on overloaded methods in PHP where possible.
- *   (Mostly done - just need to handle cases of overloaded methods with
- *   default parameters...)
- *   This is an optimisation - we could handle this case using a PHP
- *   default value, but currently we treat it as we would for a default
- *   value which is a compound C++ expression (i.e. as if we had a
- *   method with two overloaded forms instead of a single method with
- *   a default parameter value).
- */
-
 #include "swigmod.h"
 
 #include <ctype.h>
 #include <errno.h>
 
 static const char *usage = "\
-PHP 7 Options (available with -php7)\n\
-     -noproxy         - Don't generate proxy classes.\n\
+PHP Options (available with -php7)\n\
      -prefix <prefix> - Prepend <prefix> to all class names in PHP wrappers\n\
 \n";
 
-/* The original class wrappers for PHP stored the pointer to the C++ class in
- * the object property _cPtr.  If we use the same name for the member variable
- * which we put the pointer to the C++ class in, then the flat function
- * wrappers will automatically pull it out without any changes being required.
- * FIXME: Isn't using a leading underscore a bit suspect here?
- */
-#define SWIG_PTR "_cPtr"
+// How to wrap non-class functions, variables and constants:
+// FIXME: Make this specifiable and also allow a real namespace.
 
-/* This is the name of the hash where the variables existing only in PHP
- * classes are stored.
- */
-#define SWIG_DATA "_pData"
+// Wrap as global PHP names.
+static bool wrap_nonclass_global = true;
 
-static String *NOTCLASS = NewString("Not a class");
-static Node *classnode = 0;
+// Wrap in a class to fake a namespace (for compatibility with SWIG's behaviour
+// before PHP added namespaces.
+static bool wrap_nonclass_fake_class = true;
+
 static String *module = 0;
 static String *cap_module = 0;
 static String *prefix = 0;
 
-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;
 
@@ -72,7 +49,6 @@
 static String *r_init;		// RINIT user code
 static String *s_shutdown;	// MSHUTDOWN user code
 static String *r_shutdown;	// RSHUTDOWN user code
-static String *s_vinit;		// varinit initialization code.
 static String *s_vdecl;
 static String *s_cinit;		// consttab initialization code.
 static String *s_oinit;
@@ -80,29 +56,48 @@
 static String *s_entry;
 static String *cs_entry;
 static String *all_cs_entry;
+static String *fake_cs_entry;
+static String *s_creation;
 static String *pragma_incl;
 static String *pragma_code;
 static String *pragma_phpinfo;
 static String *pragma_version;
-static String *s_oowrappers;
-static String *s_fakeoowrappers;
-static String *s_phpclasses;
+
+static String *class_name = NULL;
+static String *magic_set = NULL;
+static String *magic_get = NULL;
+static String *magic_isset = NULL;
+
+// Class used as pseudo-namespace for compatibility.
+static String *fake_class_name() {
+  static String *result = NULL;
+  if (!result) {
+    result = Len(prefix) ? prefix : module;
+    if (!s_creation) {
+      s_creation = NewStringEmpty();
+    }
+    if (!fake_cs_entry) {
+      fake_cs_entry = NewStringf("static zend_function_entry class_%s_functions[] = {\n", result);
+    }
+    Printf(s_creation, "/* class entry for %s */\n",result);
+    Printf(s_creation, "zend_class_entry *SWIGTYPE_%s_ce;\n\n",result);
+    Printf(s_oinit, "  INIT_CLASS_ENTRY(internal_ce, \"%s\", class_%s_functions);\n", result, result);
+    Printf(s_oinit, "  SWIGTYPE_%s_ce = zend_register_internal_class(&internal_ce);\n", result);
+    Printf(s_oinit, "\n");
+  }
+  return result;
+}
 
 /* To reduce code size (generated and compiled) we only want to emit each
  * different arginfo once, so we need to track which have been used.
  */
 static Hash *arginfo_used;
 
-/* Variables for using PHP classes */
-static Node *current_class = 0;
-
-static Hash *shadow_get_vars;
-static Hash *shadow_set_vars;
+/* Track non-class pointer types we need to to wrap */
 static Hash *zend_types = 0;
 
 static int shadow = 1;
 
-static bool class_has_ctor = false;
 static String *wrapping_member_constant = NULL;
 
 // These static variables are used to pass some state from Handlers into functionWrapper
@@ -113,67 +108,115 @@
   membervar,
   staticmembervar,
   constructor,
-  directorconstructor
+  directorconstructor,
+  directordisown
 } wrapperType = standard;
 
 extern "C" {
   static void (*r_prevtracefunc) (const SwigType *t, String *mangled, String *clientdata) = 0;
 }
 
-static void SwigPHP_emit_resource_registrations() {
-  Iterator ki;
-  bool emitted_default_dtor = false;
+static void print_creation_free_wrapper(Node *n) {
+  if (!s_creation) {
+    s_creation = NewStringEmpty();
+  }
 
+  String *s = s_creation;
+
+  Printf(s, "/* class entry for %s */\n",class_name);
+  Printf(s, "zend_class_entry *SWIGTYPE_%s_ce;\n\n",class_name);
+  Printf(s, "/* class object handlers for %s */\n",class_name);
+  Printf(s, "zend_object_handlers %s_object_handlers;\n\n",class_name);
+
+  if (Getattr(n, "has_destructor")) {
+    Printf(s, "/* Garbage Collection Method for class %s */\n",class_name);
+    Printf(s, "void %s_free_storage(zend_object *object) {\n",class_name);
+    Printf(s, "  swig_object_wrapper *obj = 0;\n");
+    Printf(s, "  if (!object)\n");
+    Printf(s, "    return;\n");
+    Printf(s, "  obj = php_fetch_object(object);\n");
+
+    Printf(s, "  zend_object_std_dtor(&obj->std);\n");
+
+    // expand %delete typemap instead of SWIG_remove?
+    Printf(s, "  if (obj->newobject)\n");
+    Printf(s, "    SWIG_remove((%s *)obj->ptr);\n", Getattr(n, "classtype"));
+    Printf(s, "}\n\n");
+  }
+
+  Printf(s, "/* Object Creation Method for class %s */\n",class_name);
+  Printf(s, "zend_object * %s_object_new(zend_class_entry *ce) {\n",class_name);
+  Printf(s, "  swig_object_wrapper *obj = (swig_object_wrapper*)zend_object_alloc(sizeof(swig_object_wrapper), ce);\n");
+  Printf(s, "  zend_object_std_init(&obj->std, ce);\n");
+  Printf(s, "  object_properties_init(&obj->std, ce);\n");
+  Printf(s, "  %s_object_handlers.offset = XtOffsetOf(swig_object_wrapper, std);\n", class_name);
+  if (Getattr(n, "has_destructor")) {
+    Printf(s, "  %s_object_handlers.free_obj = %s_free_storage;\n", class_name, class_name);
+  }
+  Printf(s, "  obj->std.handlers = &%s_object_handlers;\n  obj->newobject = 1;\n  return &obj->std;\n}\n\n\n",class_name);
+}
+
+static void SwigPHP_emit_pointer_type_registrations() {
   if (!zend_types)
     return;
 
-  ki = First(zend_types);
-  if (ki.key)
-    Printf(s_oinit, "\n  /* Register resource destructors for pointer types */\n");
+  Iterator ki = First(zend_types);
+  if (!ki.key)
+    return;
+
+  Printf(s_wrappers, "/* class object handlers for pointer wrappers */\n");
+  Printf(s_wrappers, "static zend_object_handlers swig_ptr_object_handlers;\n\n");
+
+  Printf(s_wrappers, "/* Object Creation Method for pointer wrapping class */\n");
+  Printf(s_wrappers, "static zend_object * swig_ptr_object_new(zend_class_entry *ce) {\n");
+  Printf(s_wrappers, "  swig_object_wrapper *obj = (swig_object_wrapper*)zend_object_alloc(sizeof(swig_object_wrapper), ce);\n");
+  Printf(s_wrappers, "  zend_object_std_init(&obj->std, ce);\n");
+  Printf(s_wrappers, "  object_properties_init(&obj->std, ce);\n");
+  Printf(s_wrappers, "  obj->std.handlers = &swig_ptr_object_handlers;\n");
+  Printf(s_wrappers, "  obj->newobject = 0;\n");
+  Printf(s_wrappers, "  return &obj->std;\n");
+  Printf(s_wrappers, "}\n\n");
+
+  Printf(s_wrappers, "/* Implement __toString equivalent, since that worked for the old-style resource wrapped pointers. */\n");
+  Append(s_wrappers, "#if PHP_MAJOR_VERSION < 8\n");
+  Printf(s_wrappers, "static int swig_ptr_cast_object(zval *z, zval *retval, int type) {\n");
+  Append(s_wrappers, "#else\n");
+  Printf(s_wrappers, "static int swig_ptr_cast_object(zend_object *zobj, zval *retval, int type) {\n");
+  Append(s_wrappers, "#endif\n");
+  Printf(s_wrappers, "  if (type == IS_STRING) {\n");
+  Printf(s_wrappers, "    char buf[80];\n");
+  Append(s_wrappers, "#if PHP_MAJOR_VERSION < 8\n");
+  Printf(s_wrappers, "    swig_object_wrapper *obj = SWIG_Z_FETCH_OBJ_P(z);\n");
+  Append(s_wrappers, "#else\n");
+  Printf(s_wrappers, "    swig_object_wrapper *obj = php_fetch_object(zobj);\n");
+  Append(s_wrappers, "#endif\n");
+  Printv(s_wrappers, "    sprintf(buf, \"SWIGPointer(%p,owned=%d)\", obj->ptr, obj->newobject);\n", NIL);
+  Printf(s_wrappers, "    ZVAL_STRING(retval, buf);\n");
+  Printf(s_wrappers, "    return SUCCESS;\n");
+  Printf(s_wrappers, "  }\n");
+  Printf(s_wrappers, "  return FAILURE;\n");
+  Printf(s_wrappers, "}\n\n");
+
+  Printf(s_oinit, "\n  /* Register classes to represent non-class pointer types */\n");
+  Printf(s_oinit, "  memcpy(&swig_ptr_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));\n");
+  Printf(s_oinit, "  swig_ptr_object_handlers.offset = XtOffsetOf(swig_object_wrapper, std);\n");
+  Printf(s_oinit, "  swig_ptr_object_handlers.cast_object = swig_ptr_cast_object;\n");
+
   while (ki.key) {
-    DOH *key = ki.key;
-    Node *class_node = ki.item;
-    String *human_name = key;
-    String *rsrc_dtor_name = NULL;
+    String *type = ki.key;
 
-    // write out body
-    if (class_node != NOTCLASS) {
-      String *destructor = Getattr(class_node, "destructor");
-      human_name = Getattr(class_node, "sym:name");
-      if (!human_name)
-        human_name = Getattr(class_node, "name");
-      // Do we have a known destructor for this type?
-      if (destructor) {
-	rsrc_dtor_name = NewStringf("_wrap_destroy%s", key);
-	// Write out custom destructor function
-	Printf(s_wrappers, "static ZEND_RSRC_DTOR_FUNC(%s) {\n", rsrc_dtor_name);
-        Printf(s_wrappers, "  %s(res, SWIGTYPE%s->name);\n", destructor, key);
-	Printf(s_wrappers, "}\n");
-      }
+    if (!s_creation) {
+      s_creation = NewStringEmpty();
     }
 
-    if (!rsrc_dtor_name) {
-      rsrc_dtor_name = NewString("_swig_default_rsrc_destroy");
-      if (!emitted_default_dtor) {
-	// Write out custom destructor function
-	Printf(s_wrappers, "static ZEND_RSRC_DTOR_FUNC(%s) {\n", rsrc_dtor_name);
-	Printf(s_wrappers, "  efree(res->ptr);\n");
-	Printf(s_wrappers, "}\n");
-	emitted_default_dtor = true;
-      }
-    }
+    Printf(s_creation, "/* class entry for pointer to %s */\n", type);
+    Printf(s_creation, "zend_class_entry *SWIGTYPE_%s_ce;\n\n", type);
 
-    // declare le_swig<mangled> to store php registration
-    Printf(s_vdecl, "static int le_swig%s=0; /* handle for %s */\n", key, human_name);
-
-    // register with php
-    Printf(s_oinit, "  le_swig%s=zend_register_list_destructors_ex"
-		    "(%s, NULL, SWIGTYPE%s->name, module_number);\n", key, rsrc_dtor_name, key);
-
-    // store php type in class struct
-    Printf(s_oinit, "  SWIG_TypeClientData(SWIGTYPE%s,&le_swig%s);\n", key, key);
-
-    Delete(rsrc_dtor_name);
+    Printf(s_oinit, "  INIT_CLASS_ENTRY(internal_ce, \"%s\\\\%s\", NULL);\n", "SWIG", type);
+    Printf(s_oinit, "  SWIGTYPE_%s_ce = zend_register_internal_class(&internal_ce);\n", type);
+    Printf(s_oinit, "  SWIGTYPE_%s_ce->create_object = swig_ptr_object_new;\n", type);
+    Printf(s_oinit, "  SWIG_TypeClientData(SWIGTYPE%s,SWIGTYPE_%s_ce);\n", type, type);
+    Printf(s_oinit, "\n");
 
     ki = Next(ki);
   }
@@ -202,7 +245,7 @@
 	} else {
 	  Swig_arg_error();
 	}
-      } else if ((strcmp(argv[i], "-noshadow") == 0) || (strcmp(argv[i], "-noproxy") == 0)) {
+      } else if ((strcmp(argv[i], "-noshadow") == 0)) {
 	shadow = 0;
 	Swig_mark_arg(i);
       } else if (strcmp(argv[i], "-help") == 0) {
@@ -257,12 +300,10 @@
     s_header = NewString("/* header section */\n");
     s_wrappers = NewString("/* wrapper section */\n");
     /* subsections of the init section */
-    s_vinit = NewStringEmpty();
     s_vdecl = NewString("/* vdecl subsection */\n");
     s_cinit = NewString("  /* cinit subsection */\n");
     s_oinit = NewString("  /* oinit subsection */\n");
     pragma_phpinfo = NewStringEmpty();
-    s_phpclasses = NewString("/* PHP Proxy Classes */\n");
     f_directors_h = NewStringEmpty();
     f_directors = NewStringEmpty();
 
@@ -300,9 +341,6 @@
     if (!prefix)
       prefix = NewStringEmpty();
 
-    Printf(f_runtime, "#define SWIG_PREFIX \"%s\"\n", prefix);
-    Printf(f_runtime, "#define SWIG_PREFIX_LEN %lu\n", (unsigned long)Len(prefix));
-
     if (directorsEnabled()) {
       Swig_banner(f_directors_h);
       Printf(f_directors_h, "\n");
@@ -314,35 +352,6 @@
       Delete(filename);
     }
 
-    /* PHP module file */
-    filen = NewStringEmpty();
-    Printv(filen, SWIG_output_directory(), module, ".php", NIL);
-
-    f_phpcode = NewFile(filen, "w", SWIG_output_files());
-    if (!f_phpcode) {
-      FileErrorDisplay(filen);
-      SWIG_exit(EXIT_FAILURE);
-    }
-
-    Printf(f_phpcode, "<?php\n\n");
-
-    Swig_banner(f_phpcode);
-
-    Printf(f_phpcode, "\n");
-    Printf(f_phpcode, "// Try to load our extension if it's not already loaded.\n");
-    Printf(f_phpcode, "if (!extension_loaded('%s')) {\n", module);
-    Printf(f_phpcode, "  if (strtolower(substr(PHP_OS, 0, 3)) === 'win') {\n");
-    Printf(f_phpcode, "    if (!dl('php_%s.dll')) return;\n", module);
-    Printf(f_phpcode, "  } else {\n");
-    Printf(f_phpcode, "    // PHP_SHLIB_SUFFIX gives 'dylib' on MacOS X but modules are 'so'.\n");
-    Printf(f_phpcode, "    if (PHP_SHLIB_SUFFIX === 'dylib') {\n");
-    Printf(f_phpcode, "      if (!dl('%s.so')) return;\n", module);
-    Printf(f_phpcode, "    } else {\n");
-    Printf(f_phpcode, "      if (!dl('%s.'.PHP_SHLIB_SUFFIX)) return;\n", module);
-    Printf(f_phpcode, "    }\n");
-    Printf(f_phpcode, "  }\n");
-    Printf(f_phpcode, "}\n\n");
-
     /* sub-sections of the php file */
     pragma_code = NewStringEmpty();
     pragma_incl = NewStringEmpty();
@@ -361,20 +370,6 @@
     Printf(s_header, "#define SWIG_ErrorMsg() ZEND_MODULE_GLOBALS_ACCESSOR(%s, error_msg)\n", module);
     Printf(s_header, "#define SWIG_ErrorCode() ZEND_MODULE_GLOBALS_ACCESSOR(%s, error_code)\n", module);
 
-    /* The following can't go in Lib/php/phprun.swg as it uses SWIG_ErrorMsg(), etc
-     * which has to be dynamically generated as it depends on the module name.
-     */
-    Append(s_header, "#ifdef __GNUC__\n");
-    Append(s_header, "static void SWIG_FAIL(void) __attribute__ ((__noreturn__));\n");
-    Append(s_header, "#endif\n\n");
-    Append(s_header, "static void SWIG_FAIL(void) {\n");
-    Append(s_header, "    zend_error(SWIG_ErrorCode(), \"%s\", SWIG_ErrorMsg());\n");
-    // zend_error() should never return with the parameters we pass, but if it
-    // does, we really don't want to let SWIG_FAIL() return.  This also avoids
-    // a warning about returning from a function marked as "__noreturn__".
-    Append(s_header, "    abort();\n");
-    Append(s_header, "}\n\n");
-
     Printf(s_header, "static void %s_init_globals(zend_%s_globals *globals ) {\n", module, module);
     Printf(s_header, "  globals->error_msg = default_error_msg;\n");
     Printf(s_header, "  globals->error_code = default_error_code;\n");
@@ -386,34 +381,6 @@
     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, "\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 *) Z_RES_VAL(args[0]);\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, "\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 *) Z_RES_VAL(args[0]);\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 __cplusplus\n");
@@ -427,6 +394,9 @@
     Printf(s_header, "}\n");
     Printf(s_header, "#endif\n\n");
 
+    Printf(s_header, "#ifdef __cplusplus\n#define SWIG_remove(PTR) delete PTR\n");
+    Printf(s_header, "#else\n#define SWIG_remove(PTR) free(PTR)\n#endif\n\n");
+
     if (directorsEnabled()) {
       // Insert director runtime
       Swig_insert_file("director_common.swg", s_header);
@@ -459,34 +429,26 @@
     s_arginfo = NewString("/* arginfo subsection */\n");
     arginfo_used = NewHash();
 
-    // Add arginfo we'll definitely need for *_alter_newobject and *_get_newobject.
-    SetFlag(arginfo_used, "1");
-    Append(s_arginfo,
-	   "ZEND_BEGIN_ARG_INFO_EX(swig_arginfo_1, 0, 0, 0)\n"
-	   " ZEND_ARG_INFO(0,arg1)\n"
-	   "ZEND_END_ARG_INFO()\n");
-
-    SetFlag(arginfo_used, "2");
-    Append(s_arginfo,
-	   "ZEND_BEGIN_ARG_INFO_EX(swig_arginfo_2, 0, 0, 0)\n"
-	   " ZEND_ARG_INFO(0,arg1)\n"
-	   " ZEND_ARG_INFO(0,arg2)\n"
-	   "ZEND_END_ARG_INFO()\n");
-
     /* start the function entry section */
     s_entry = NewString("/* entry subsection */\n");
 
     /* holds all the per-class function entry sections */
     all_cs_entry = NewString("/* class entry subsection */\n");
     cs_entry = NULL;
+    fake_cs_entry = NULL;
 
     Printf(s_entry, "/* Every non-class user visible function must have an entry here */\n");
-    Printf(s_entry, "static zend_function_entry %s_functions[] = {\n", module);
+    Printf(s_entry, "static zend_function_entry module_%s_functions[] = {\n", module);
 
     /* Emit all of the code */
     Language::top(n);
 
-    SwigPHP_emit_resource_registrations();
+    SwigPHP_emit_pointer_type_registrations();
+    if (s_creation) {
+      Dump(s_creation, s_header);
+      Delete(s_creation);
+      s_creation = NULL;
+    }
 
     /* start the init section */
     {
@@ -495,14 +457,14 @@
       Printv(s_init, "zend_module_entry ", module, "_module_entry = {\n", NIL);
       Printf(s_init, "    STANDARD_MODULE_HEADER,\n");
       Printf(s_init, "    \"%s\",\n", module);
-      Printf(s_init, "    %s_functions,\n", module);
+      Printf(s_init, "    module_%s_functions,\n", module);
       Printf(s_init, "    PHP_MINIT(%s),\n", module);
       if (Len(s_shutdown) > 0) {
 	Printf(s_init, "    PHP_MSHUTDOWN(%s),\n", module);
       } else {
 	Printf(s_init, "    NULL, /* No MSHUTDOWN code */\n");
       }
-      if (Len(r_init) > 0 || Len(s_vinit) > 0) {
+      if (Len(r_init) > 0) {
 	Printf(s_init, "    PHP_RINIT(%s),\n", module);
       } else {
 	Printf(s_init, "    NULL, /* No RINIT code */\n");
@@ -548,8 +510,6 @@
      */
 
     //    Printv(s_init,s_resourcetypes,NIL);
-    /* We need this after all classes written out by ::top */
-    Printf(s_oinit, "  CG(active_class_entry) = NULL;\n");
     Printf(s_oinit, "  /* end oinit subsection */\n");
     Printf(s_init, "%s\n", s_oinit);
 
@@ -563,27 +523,14 @@
     Printf(s_init, "}\n\n");
 
     // Now do REQUEST init which holds any user specified %rinit, and also vinit
-    if (Len(r_init) > 0 || Len(s_vinit) > 0) {
+    if (Len(r_init) > 0) {
       Printf(f_h, "PHP_RINIT_FUNCTION(%s);\n", module);
 
       Printf(s_init, "PHP_RINIT_FUNCTION(%s)\n{\n", module);
-      if (Len(r_init) > 0) {
-	Printv(s_init,
-	       "/* rinit section */\n",
-	       r_init, "\n",
-	       NIL);
-      }
-
-      if (Len(s_vinit) > 0) {
-	/* finish our init section which will have been used by class wrappers */
-	Printv(s_init,
-	       "  /* vinit subsection */\n",
-	       s_vinit, "\n"
-	       "  /* end vinit subsection */\n",
-	       NIL);
-	Clear(s_vinit);
-      }
-      Delete(s_vinit);
+      Printv(s_init,
+	     "/* rinit section */\n",
+	     r_init, "\n",
+	     NIL);
 
       Printf(s_init, "  return SUCCESS;\n");
       Printf(s_init, "}\n\n");
@@ -652,10 +599,13 @@
       Dump(f_directors, f_begin);
     }
     Printv(f_begin, s_vdecl, s_wrappers, NIL);
-    Printv(f_begin, all_cs_entry, "\n\n", s_arginfo, "\n\n", s_entry,
-	" SWIG_ZEND_NAMED_FE(swig_", module, "_alter_newobject,_wrap_swig_", module, "_alter_newobject,swig_arginfo_2)\n"
-	" SWIG_ZEND_NAMED_FE(swig_", module, "_get_newobject,_wrap_swig_", module, "_get_newobject,swig_arginfo_1)\n"
+    Printv(f_begin, s_arginfo, "\n\n", all_cs_entry, "\n\n", s_entry,
 	" ZEND_FE_END\n};\n\n", NIL);
+    if (fake_cs_entry) {
+      Printv(f_begin, fake_cs_entry, " ZEND_FE_END\n};\n\n", NIL);
+      Delete(fake_cs_entry);
+      fake_cs_entry = NULL;
+    }
     Printv(f_begin, s_init, NIL);
     Delete(s_header);
     Delete(s_wrappers);
@@ -668,25 +618,50 @@
     Delete(f_begin);
     Delete(arginfo_used);
 
-    Printf(f_phpcode, "%s\n%s\n", pragma_incl, pragma_code);
-    if (s_fakeoowrappers) {
-      Printf(f_phpcode, "abstract class %s {", Len(prefix) ? prefix : module);
-      Printf(f_phpcode, "%s", s_fakeoowrappers);
-      Printf(f_phpcode, "}\n\n");
-      Delete(s_fakeoowrappers);
-      s_fakeoowrappers = NULL;
+    if (Len(pragma_incl) > 0 || Len(pragma_code) > 0) {
+      /* PHP module file */
+      String *php_filename = NewStringEmpty();
+      Printv(php_filename, SWIG_output_directory(), module, ".php", NIL);
+
+      File *f_phpcode = NewFile(php_filename, "w", SWIG_output_files());
+      if (!f_phpcode) {
+	FileErrorDisplay(php_filename);
+	SWIG_exit(EXIT_FAILURE);
+      }
+
+      Printf(f_phpcode, "<?php\n\n");
+
+      if (Len(pragma_incl) > 0) {
+	Printv(f_phpcode, pragma_incl, "\n", NIL);
+      }
+
+      if (Len(pragma_code) > 0) {
+	Printv(f_phpcode, pragma_code, "\n", NIL);
+      }
+
+      Delete(f_phpcode);
+      Delete(php_filename);
     }
-    Printf(f_phpcode, "%s\n", s_phpclasses);
-    Delete(f_phpcode);
 
     return SWIG_OK;
   }
 
   /* Just need to append function names to function table to register with PHP. */
-  void create_command(String *cname, String *iname, Node *n) {
+  void create_command(String *cname, String *fname, Node *n, bool overload, String *modes = NULL) {
     // This is for the single main zend_function_entry record
-    Printf(f_h, "ZEND_NAMED_FUNCTION(%s);\n", iname);
-
+    bool has_this = false;
+    if (cname && Cmp(Getattr(n, "storage"), "friend") != 0) {
+      Printf(f_h, "PHP_METHOD(%s%s,%s);\n", prefix, cname, fname);
+      has_this = (wrapperType != staticmemberfn) &&
+		 (wrapperType != staticmembervar) &&
+		 (Cmp(fname, "__construct") != 0);
+    } else {
+      if (overload) {
+        Printf(f_h, "ZEND_NAMED_FUNCTION(%s);\n", fname);
+      } else {
+        Printf(f_h, "PHP_FUNCTION(%s);\n", fname);
+      }
+    }
     // We want to only emit each different arginfo once, as that reduces the
     // size of both the generated source code and the compiled extension
     // module.  The parameters at this level are just named arg1, arg2, etc
@@ -694,20 +669,31 @@
     // bitmap value saying which (if any) are passed by reference.
     ParmList *l = Getattr(n, "parms");
     unsigned long bitmap = 0, bit = 1;
-    int n_params = 0;
     bool overflowed = false;
+    bool skip_this = has_this;
     for (Parm *p = l; p; p = Getattr(p, "tmap:in:next")) {
-      /* Ignored parameters */
-      if (checkAttribute(p, "tmap:in:numinputs", "0")) {
+      if (skip_this) {
+	skip_this = false;
+        continue;
+      }
+      String* tmap_in_numinputs = Getattr(p, "tmap:in:numinputs");
+      // tmap:in:numinputs is unset for varargs, which we don't count here.
+      if (!tmap_in_numinputs || Equal(tmap_in_numinputs, "0")) {
+	/* Ignored parameter */
 	continue;
       }
-      ++n_params;
       if (GetFlag(p, "tmap:in:byref")) {
 	  bitmap |= bit;
 	  if (bit == 0) overflowed = true;
       }
       bit <<= 1;
     }
+    int num_arguments = emit_num_arguments(l);
+    int num_required = emit_num_required(l);
+    if (has_this) {
+      --num_arguments;
+      --num_required;
+    }
     String * arginfo_code;
     if (overflowed) {
       // We overflowed the bitmap so just generate a unique name - this only
@@ -718,49 +704,119 @@
       arginfo_code = NewStringf("z%d", ++overflowed_counter);
     } else if (bitmap == 0) {
       // No parameters passed by reference.
-      arginfo_code = NewStringf("%d", n_params);
+      if (num_required == num_arguments) {
+	  arginfo_code = NewStringf("%d", num_arguments);
+      } else {
+	  arginfo_code = NewStringf("%d_%d", num_required, num_arguments);
+      }
     } else {
-      arginfo_code = NewStringf("%d_%lx", n_params, bitmap);
+      if (num_required == num_arguments) {
+	  arginfo_code = NewStringf("%d_r%lx", num_arguments, bitmap);
+      } else {
+	  arginfo_code = NewStringf("%d_%d_r%lx", num_required, num_arguments, bitmap);
+      }
     }
 
     if (!GetFlag(arginfo_used, arginfo_code)) {
       // Not had this one before so emit it.
       SetFlag(arginfo_used, arginfo_code);
-      Printf(s_arginfo, "ZEND_BEGIN_ARG_INFO_EX(swig_arginfo_%s, 0, 0, 0)\n", arginfo_code);
+      Printf(s_arginfo, "ZEND_BEGIN_ARG_INFO_EX(swig_arginfo_%s, 0, 0, %d)\n", arginfo_code, num_required);
+      bool skip_this = has_this;
+      int param_count = 0;
       for (Parm *p = l; p; p = Getattr(p, "tmap:in:next")) {
-	Printf(s_arginfo, " ZEND_ARG_INFO(%d,%s)\n", GetFlag(p, "tmap:in:byref"), Getattr(p, "lname"));
+	if (skip_this) {
+	  skip_this = false;
+	  continue;
+	}
+	String* tmap_in_numinputs = Getattr(p, "tmap:in:numinputs");
+	// tmap:in:numinputs is unset for varargs, which we don't count here.
+	if (!tmap_in_numinputs || Equal(tmap_in_numinputs, "0")) {
+	  /* Ignored parameter */
+	  continue;
+	}
+	Printf(s_arginfo, " ZEND_ARG_INFO(%d,arg%d)\n", GetFlag(p, "tmap:in:byref"), ++param_count);
       }
       Printf(s_arginfo, "ZEND_END_ARG_INFO()\n");
     }
 
     String * s = cs_entry;
     if (!s) s = s_entry;
-    Printf(s, " SWIG_ZEND_NAMED_FE(%(lower)s,%s,swig_arginfo_%s)\n", cname, iname, arginfo_code);
+    if (cname && Cmp(Getattr(n, "storage"), "friend") != 0) {
+      Printf(all_cs_entry, " PHP_ME(%s%s,%s,swig_arginfo_%s,%s)\n", prefix, cname, fname, arginfo_code, modes);
+    } else {
+      if (overload) {
+	if (wrap_nonclass_global) {
+	  Printf(s, " ZEND_NAMED_FE(%(lower)s,%s,swig_arginfo_%s)\n", Getattr(n, "sym:name"), fname, arginfo_code);
+	}
+
+	if (wrap_nonclass_fake_class) {
+	  (void)fake_class_name();
+	  Printf(fake_cs_entry, " ZEND_NAMED_ME(%(lower)s,%s,swig_arginfo_%s,ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)\n", Getattr(n, "sym:name"), fname, arginfo_code);
+	}
+      } else {
+	if (wrap_nonclass_global) {
+	  Printf(s, " PHP_FE(%s,swig_arginfo_%s)\n", fname, arginfo_code);
+	}
+
+	if (wrap_nonclass_fake_class) {
+	  String *fake_class = fake_class_name();
+	  Printf(fake_cs_entry, " PHP_ME(%s,%s,swig_arginfo_%s,ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)\n", fake_class, fname, arginfo_code);
+	}
+      }
+    }
     Delete(arginfo_code);
   }
 
   /* ------------------------------------------------------------
    * dispatchFunction()
    * ------------------------------------------------------------ */
-  void dispatchFunction(Node *n) {
+  void dispatchFunction(Node *n, int constructor) {
     /* Last node in overloaded chain */
 
     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, "%s(INTERNAL_FUNCTION_PARAM_PASSTHRU); return;", &maxargs);
 
     /* Generate a dispatch wrapper for all overloaded functions */
 
     Wrapper *f = NewWrapper();
     String *symname = Getattr(n, "sym:name");
-    String *wname = Swig_name_wrapper(symname);
+    String *wname = NULL;
+    String *modes = NULL;
+    bool constructorRenameOverload = false;
 
-    create_command(symname, wname, n);
-    Printv(f->def, "ZEND_NAMED_FUNCTION(", wname, ") {\n", NIL);
+    if (constructor) {
+      // Renamed constructor - turn into static factory method
+      if (Cmp(class_name, Getattr(n, "constructorHandler:sym:name")) != 0) {
+        constructorRenameOverload = true;
+        wname = Copy(Getattr(n, "constructorHandler:sym:name"));
+      } else {
+        wname = NewString("__construct");
+      }
+    } else if (class_name) {
+      wname = Getattr(n, "wrapper:method:name");
+    } else {
+      wname = Swig_name_wrapper(symname);
+    }
+
+    if (constructor) {
+      modes = NewString("ZEND_ACC_PUBLIC | ZEND_ACC_CTOR");
+      if (constructorRenameOverload) {
+        Append(modes, " | ZEND_ACC_STATIC");
+      }
+    } else if (wrapperType == staticmemberfn || Cmp(Getattr(n, "storage"), "static") == 0) {
+      modes = NewString("ZEND_ACC_PUBLIC | ZEND_ACC_STATIC");
+    } else {
+      modes = NewString("ZEND_ACC_PUBLIC");
+    }
+
+    create_command(class_name, wname, n, true, modes);
+
+    if (class_name && Cmp(Getattr(n, "storage"), "friend") != 0) {
+      Printv(f->def, "PHP_METHOD(", prefix, class_name, ",", wname, ") {\n", NIL);
+    } else {
+      Printv(f->def, "ZEND_NAMED_FUNCTION(", wname, ") {\n", NIL);
+    }
 
     Wrapper_add_local(f, "argc", "int argc");
 
@@ -778,14 +834,14 @@
     Printf(f->code, "SWIG_ErrorCode() = E_ERROR;\n");
     Printf(f->code, "SWIG_ErrorMsg() = \"No matching function for overloaded '%s'\";\n", symname);
     Printv(f->code, "SWIG_FAIL();\n", NIL);
-
+    Printv(f->code, "thrown:\n", NIL);
+    Printv(f->code, "return;\n", NIL);
     Printv(f->code, "}\n", NIL);
     Wrapper_print(f, s_wrappers);
 
     DelWrapper(f);
     Delete(dispatch);
     Delete(tmp);
-    Delete(wname);
   }
 
   /* ------------------------------------------------------------
@@ -805,7 +861,218 @@
     return false;
   }
 
+  /* Helper method for PHP::functionWrapper to get class name for parameter*/
+  String *get_class_name(SwigType *t) {
+    Node *n = classLookup(t);
+    String *r = NULL;
+    if (n) {
+      r = Getattr(n, "php:proxy");      // Set by classDeclaration()
+      if (!r)
+        r = Getattr(n, "sym:name");     // Not seen by classDeclaration yet, but this is the name
+    }
+    return r;
+  }
+
+  /* Helper function to check if class is wrapped */
+  bool is_class_wrapped(String *className) {
+    if (!className)
+      return false;
+    Node * n = symbolLookup(className);
+    return n && Getattr(n, "classtype") != NULL;
+  }
+
+  /* Is special return type */
+  bool is_param_type_pointer(SwigType *t) {
+    
+    if (SwigType_ispointer(t) ||
+          SwigType_ismemberpointer(t) ||
+            SwigType_isreference(t) ||
+              SwigType_isarray(t))
+      return true;
+
+    return false;
+  }
+
+  void generate_magic_property_methods(Node *class_node, String *base_class) {
+    if (Equal(base_class, "Exception") || !is_class_wrapped(base_class)) {
+      base_class = NULL;
+    }
+
+    // Ensure arginfo_1 and arginfo_2 exist.
+    if (!GetFlag(arginfo_used, "1")) {
+      SetFlag(arginfo_used, "1");
+      Append(s_arginfo,
+	     "ZEND_BEGIN_ARG_INFO_EX(swig_arginfo_1, 0, 0, 1)\n"
+	     " ZEND_ARG_INFO(0,arg1)\n"
+	     "ZEND_END_ARG_INFO()\n");
+    }
+    if (!GetFlag(arginfo_used, "2")) {
+      SetFlag(arginfo_used, "2");
+      Append(s_arginfo,
+	     "ZEND_BEGIN_ARG_INFO_EX(swig_arginfo_2, 0, 0, 2)\n"
+	     " ZEND_ARG_INFO(0,arg1)\n"
+	     " ZEND_ARG_INFO(0,arg2)\n"
+	     "ZEND_END_ARG_INFO()\n");
+    }
+
+    Wrapper *f = NewWrapper();
+
+    Printf(f_h, "PHP_METHOD(%s%s,__set);\n", prefix, class_name);
+    Printf(all_cs_entry, " PHP_ME(%s%s,__set,swig_arginfo_2,ZEND_ACC_PUBLIC)\n", prefix, class_name);
+    Printf(f->code, "PHP_METHOD(%s%s,__set) {\n", prefix, class_name);
+
+    Printf(f->code, "  swig_object_wrapper *arg = SWIG_Z_FETCH_OBJ_P(ZEND_THIS);\n");
+    Printf(f->code, "  zval args[2];\n zval tempZval;\n  zend_string *arg2 = 0;\n\n");
+    Printf(f->code, "  if(ZEND_NUM_ARGS() != 2 || zend_get_parameters_array_ex(2, args) != SUCCESS) {\n");
+    Printf(f->code, "\tWRONG_PARAM_COUNT;\n}\n\n");
+    Printf(f->code, "  if(!arg) SWIG_PHP_Error(E_ERROR, \"this pointer is NULL\");\n\n");
+    Printf(f->code, "  arg2 = Z_STR(args[0]);\n\n");
+
+    Printf(f->code, "if (!arg2) {\n  RETVAL_NULL();\n}\n");
+    if (magic_set) {
+      Append(f->code, magic_set);
+    }
+    Printf(f->code, "\nelse if (strcmp(ZSTR_VAL(arg2),\"thisown\") == 0) {\n");
+    Printf(f->code, "arg->newobject = zval_get_long(&args[1]);\n");
+    if (Swig_directorclass(class_node)) {
+      Printv(f->code, "if (arg->newobject == 0) {\n",
+		      "  Swig::Director *director = SWIG_DIRECTOR_CAST((", Getattr(class_node, "classtype"), "*)(arg->ptr));\n",
+		      "  if (director) director->swig_disown();\n",
+		      "}\n", NIL);
+    }
+    Printf(f->code, "} else {\n");
+    if (base_class) {
+      Printf(f->code, "PHP_MN(%s___set)(INTERNAL_FUNCTION_PARAM_PASSTHRU);\n}\n", base_class);
+    } else {
+      Printf(f->code, "add_property_zval_ex(ZEND_THIS, ZSTR_VAL(arg2), ZSTR_LEN(arg2), &args[1]);\n}\n");
+    }
+
+    Printf(f->code, "thrown:\n");
+    Printf(f->code, "return;\n");
+
+    /* Error handling code */
+    Printf(f->code, "fail:\n");
+    Append(f->code, "SWIG_FAIL();\n");
+    Printf(f->code, "}\n\n\n");
+
+
+    Printf(f_h, "PHP_METHOD(%s%s,__get);\n", prefix, class_name);
+    Printf(all_cs_entry, " PHP_ME(%s%s,__get,swig_arginfo_1,ZEND_ACC_PUBLIC)\n", prefix, class_name);
+    Printf(f->code, "PHP_METHOD(%s%s,__get) {\n",prefix, class_name);
+
+    Printf(f->code, "  swig_object_wrapper *arg = SWIG_Z_FETCH_OBJ_P(ZEND_THIS);\n", class_name);
+    Printf(f->code, "  zval args[1];\n zval tempZval;\n  zend_string *arg2 = 0;\n\n");
+    Printf(f->code, "  if(ZEND_NUM_ARGS() != 1 || zend_get_parameters_array_ex(1, args) != SUCCESS) {\n");
+    Printf(f->code, "\tWRONG_PARAM_COUNT;\n}\n\n");
+    Printf(f->code, "  if(!arg) SWIG_PHP_Error(E_ERROR, \"this pointer is NULL\");\n\n");
+    Printf(f->code, "  arg2 = Z_STR(args[0]);\n\n");
+
+    Printf(f->code, "if (!arg2) {\n  RETVAL_NULL();\n}\n");
+    if (magic_get) {
+      Append(f->code, magic_get);
+    }
+    Printf(f->code, "\nelse if (strcmp(ZSTR_VAL(arg2),\"thisown\") == 0) {\n");
+    Printf(f->code, "if(arg->newobject) {\nRETVAL_LONG(1);\n}\nelse {\nRETVAL_LONG(0);\n}\n}\n\n");
+    Printf(f->code, "else {\n");
+    if (base_class) {
+      Printf(f->code, "PHP_MN(%s___get)(INTERNAL_FUNCTION_PARAM_PASSTHRU);\n}\n", base_class);
+    } else {
+      // __get is only called if the property isn't set on the zend_object.
+      Printf(f->code, "RETVAL_NULL();\n}\n");
+    }
+
+    Printf(f->code, "thrown:\n");
+    Printf(f->code, "return;\n");
+
+    /* Error handling code */
+    Printf(f->code, "fail:\n");
+    Append(f->code, "SWIG_FAIL();\n");
+    Printf(f->code, "}\n\n\n");
+
+
+    Printf(f_h, "PHP_METHOD(%s%s,__isset);\n", prefix, class_name);
+    Printf(all_cs_entry, " PHP_ME(%s%s,__isset,swig_arginfo_1,ZEND_ACC_PUBLIC)\n", prefix, class_name);
+    Printf(f->code, "PHP_METHOD(%s%s,__isset) {\n",prefix, class_name);
+
+    Printf(f->code, "  swig_object_wrapper *arg = SWIG_Z_FETCH_OBJ_P(ZEND_THIS);\n", class_name);
+    Printf(f->code, "  zval args[1];\n  zend_string *arg2 = 0;\n\n");
+    Printf(f->code, "  if(ZEND_NUM_ARGS() != 1 || zend_get_parameters_array_ex(1, args) != SUCCESS) {\n");
+    Printf(f->code, "\tWRONG_PARAM_COUNT;\n}\n\n");
+    Printf(f->code, "  if(!arg) SWIG_PHP_Error(E_ERROR, \"this pointer is NULL\");\n\n");
+    Printf(f->code, "  arg2 = Z_STR(args[0]);\n\n");
+
+    Printf(f->code, "if (!arg2) {\n  RETVAL_FALSE;\n}\n");
+    Printf(f->code, "\nelse if (strcmp(ZSTR_VAL(arg2),\"thisown\") == 0) {\n");
+    Printf(f->code, "RETVAL_TRUE;\n}\n\n");
+    if (magic_isset) {
+      Append(f->code, magic_isset);
+    }
+    Printf(f->code, "else {\n");
+    if (base_class) {
+      Printf(f->code, "PHP_MN(%s___isset)(INTERNAL_FUNCTION_PARAM_PASSTHRU);\n}\n", base_class);
+    } else {
+      // __isset is only called if the property isn't set on the zend_object.
+      Printf(f->code, "RETVAL_FALSE;\n}\n");
+    }
+
+    Printf(f->code, "thrown:\n");
+    Printf(f->code, "return;\n");
+
+    /* Error handling code */
+    Printf(f->code, "fail:\n");
+    Append(f->code, "SWIG_FAIL();\n");
+    Printf(f->code, "}\n\n\n");
+
+    Wrapper_print(f, s_wrappers);
+    DelWrapper(f);
+    f = NULL;
+
+    Delete(magic_set);
+    Delete(magic_get);
+    Delete(magic_isset);
+    magic_set = NULL;
+    magic_get = NULL;
+    magic_isset = NULL;
+  }
+
+  String *getAccessMode(String *access) {
+    if (Cmp(access, "protected") == 0) {
+      return NewString("ZEND_ACC_PROTECTED");
+    } else if (Cmp(access, "private") == 0) {
+      return NewString("ZEND_ACC_PRIVATE");
+    }
+    return NewString("ZEND_ACC_PUBLIC");
+  }
+
+  bool is_setter_method(Node *n) {
+
+    const char *p = GetChar(n, "sym:name");
+      if (strlen(p) > 4) {
+        p += strlen(p) - 4;
+        if (strcmp(p, "_set") == 0) {
+          return true;
+        }
+      }
+      return false;
+  }
+
+  bool is_getter_method(Node *n) {
+
+    const char *p = GetChar(n, "sym:name");
+      if (strlen(p) > 4) {
+        p += strlen(p) - 4;
+        if (strcmp(p, "_get") == 0) {
+          return true;
+        }
+      }
+      return false;
+  }
+
   virtual int functionWrapper(Node *n) {
+    if (wrapperType == directordisown) {
+      // Handled via __set magic method - no explicit wrapper method wanted.
+      return SWIG_OK;
+    }
     String *name = GetChar(n, "name");
     String *iname = GetChar(n, "sym:name");
     SwigType *d = Getattr(n, "type");
@@ -820,17 +1087,25 @@
     String *tm;
     Wrapper *f;
 
-    String *wname;
+    String *wname = NewStringEmpty();
+    String *overloadwname = NULL;
     int overloaded = 0;
     String *overname = 0;
+    String *modes = NULL;
+    bool static_setter = false;
+    bool static_getter = false;
 
-    if (Cmp(nodeType, "destructor") == 0) {
-      // We just generate the Zend List Destructor and let Zend manage the
-      // reference counting.  There's no explicit destructor, but the user can
-      // just do `$obj = null;' to remove a reference to an object.
-      return CreateZendListDestructor(n);
+    modes = getAccessMode(Getattr(n, "access"));
+
+    if (constructor) {
+      Append(modes, " | ZEND_ACC_CTOR");
+    } 
+    if (wrapperType == staticmemberfn || Cmp(Getattr(n, "storage"), "static") == 0) {
+      Append(modes, " | ZEND_ACC_STATIC");
     }
-    // Test for overloading;
+    if (GetFlag(n, "abstract") && Swig_directorclass(Swig_methodclass(n)) && !is_member_director(n))
+      Append(modes, " | ZEND_ACC_ABSTRACT");
+
     if (Getattr(n, "sym:overloaded")) {
       overloaded = 1;
       overname = Getattr(n, "sym:overname");
@@ -839,25 +1114,106 @@
 	return SWIG_ERROR;
     }
 
-    wname = Swig_name_wrapper(iname);
     if (overname) {
-      Printf(wname, "%s", overname);
+      // Test for overloading
+      overloadwname = NewString(Swig_name_wrapper(iname));
+      Printf(overloadwname, "%s", overname);
+    }
+
+    if (constructor) {
+      wname = NewString("__construct");
+    } else if (wrapperType == membervar) {
+      wname = Copy(Getattr(n, "membervariableHandler:sym:name"));
+      if (is_setter_method(n)) {
+        Append(wname, "_set");
+      } else if (is_getter_method(n)) {
+        Append(wname, "_get");
+      }
+    } else if (wrapperType == memberfn) {
+      wname = Getattr(n, "memberfunctionHandler:sym:name");
+    } else if (wrapperType == staticmembervar) {
+      // Shape::nshapes -> nshapes
+      wname = Getattr(n, "staticmembervariableHandler:sym:name");
+
+      /* We get called twice for getter and setter methods. But to maintain
+         compatibility, Shape::nshapes() is being used for both setter and 
+         getter methods. So using static_setter and static_getter variables
+         to generate half of the code each time.
+       */
+      static_setter = is_setter_method(n);
+
+      if (is_getter_method(n)) {
+        // This is to overcome types that can't be set and hence no setter.
+        if (Cmp(Getattr(n, "feature:immutable"), "1") != 0)
+          static_getter = true;
+      }
+    } else if (wrapperType == staticmemberfn) {
+      wname = Getattr(n, "staticmemberfunctionHandler:sym:name");
+    } else {
+      if (class_name) {
+        if (Cmp(Getattr(n, "storage"), "friend") == 0 && Cmp(Getattr(n, "view"), "globalfunctionHandler") == 0) {
+          wname = iname;
+        } else {
+          wname = Getattr(n, "destructorHandler:sym:name");
+        }
+      } else {
+        wname = iname;
+      }
+    }
+
+    if (Cmp(nodeType, "destructor") == 0) {
+      // We don't explicitly wrap the destructor for PHP - Zend manages the
+      // reference counting, and the user can just do `$obj = null;' or similar
+      // to remove a reference to an object.
+      return SWIG_OK;
     }
 
     f = NewWrapper();
 
+    if (static_getter) {
+      Printf(f->def, "{\n");
+    }
+
     String *outarg = NewStringEmpty();
     String *cleanup = NewStringEmpty();
 
-    Printv(f->def, "ZEND_NAMED_FUNCTION(", wname, ") {\n", NIL);
+    if (!overloaded) {
+      if (!static_getter) {
+        if (class_name && Cmp(Getattr(n, "storage"), "friend") != 0) {
+          Printv(f->def, "PHP_METHOD(", prefix, class_name, ",", wname, ") {\n", NIL);
+        } else {
+	  if (wrap_nonclass_global) {
+	    Printv(f->def, "PHP_METHOD(", fake_class_name(), ",", wname, ") {\n",
+			   "  PHP_FN(", wname, ")(INTERNAL_FUNCTION_PARAM_PASSTHRU);\n",
+			   "}\n\n", NIL);
+	  }
+
+	  if (wrap_nonclass_fake_class) {
+	    Printv(f->def, "PHP_FUNCTION(", wname, ") {\n", NIL);
+	  }
+        }
+      }
+    } else {
+      if (class_name && Cmp(Getattr(n, "storage"), "friend") != 0) {
+        Printv(f->def, "PHP_METHOD(", prefix, class_name, ",", overloadwname, ") {\n", NIL);
+      } else {
+        Printv(f->def, "ZEND_NAMED_FUNCTION(", overloadwname, ") {\n", NIL);
+      }
+    }
 
     emit_parameter_variables(l, f);
     /* Attach standard typemaps */
 
     emit_attach_parmmaps(l, f);
     // Not issued for overloaded functions.
-    if (!overloaded) {
-      create_command(iname, wname, n);
+    if (!overloaded && !static_getter) {
+      create_command(class_name, wname, n, false, modes);
+    }
+
+    if (wrapperType == memberfn || wrapperType == membervar) {
+      // Assign "this" to arg1 and remove first entry from ParmList l.
+      Printf(f->code, "arg1 = (%s)SWIG_Z_FETCH_OBJ_P(ZEND_THIS)->ptr;\n", SwigType_lstr(Getattr(l, "type"), ""));
+      l = nextSibling(l);
     }
 
     // wrap:parms is used by overload resolution.
@@ -867,18 +1223,16 @@
     int num_required = emit_num_required(l);
     numopt = num_arguments - num_required;
 
-    if (wrapperType == directorconstructor)
-      num_arguments++;
-
     if (num_arguments > 0) {
       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 (wrapperType == directorconstructor) {
+      Wrapper_add_local(f, "arg0", "zval *arg0 = ZEND_THIS");
+    }
 
     // This generated code may be called:
     // 1) as an object method, or
@@ -896,6 +1250,12 @@
       Printf(f->code, "if(arg_count<%d || arg_count>%d ||\n", num_required, num_arguments);
       Printf(f->code, "   zend_get_parameters_array_ex(arg_count,args)!=SUCCESS)\n");
       Printf(f->code, "\tWRONG_PARAM_COUNT;\n\n");
+    } else if (static_setter || static_getter) {
+      if (num_arguments == 0) {
+        Printf(f->code, "if(ZEND_NUM_ARGS() == 0) {\n");
+      } else {
+        Printf(f->code, "if(ZEND_NUM_ARGS() == %d && zend_get_parameters_array_ex(%d, args) == SUCCESS) {\n", num_arguments, num_arguments);
+      }
     } else {
       if (num_arguments == 0) {
 	Printf(f->code, "if(ZEND_NUM_ARGS() != 0) {\n");
@@ -904,8 +1264,6 @@
       }
       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
@@ -917,10 +1275,7 @@
     // _this and not the first argument.
     // This may mean looking at Language::memberfunctionHandler
 
-    int limit = num_arguments;
-    if (wrapperType == directorconstructor)
-      limit--;
-    for (i = 0, p = l; i < limit; i++) {
+    for (i = 0, p = l; i < num_arguments; i++) {
       String *source;
 
       /* Skip ignored arguments */
@@ -931,17 +1286,21 @@
 
       SwigType *pt = Getattr(p, "type");
 
-      if (wrapperType == directorconstructor) {
-	source = NewStringf("args[%d]", i+1);
-      } else {
-	source = NewStringf("args[%d]", i);
-      }
+      source = NewStringf("args[%d]", i);
 
       /* Check if optional */
       if (i >= num_required) {
 	Printf(f->code, "\tif(arg_count > %d) {\n", i);
       }
 
+      String *paramType_class = NULL;
+      bool paramType_valid = is_class(pt);
+
+      if (paramType_valid) {
+        paramType_class = get_class_name(pt);
+        Chop(paramType_class);
+      }
+
       if ((tm = Getattr(p, "tmap:in"))) {
 	Replaceall(tm, "$input", source);
 	Setattr(p, "emit:input", source);
@@ -965,8 +1324,8 @@
 
     if (is_member_director(n)) {
       Wrapper_add_local(f, "upcall", "bool upcall = false");
-      Printf(f->code, "upcall = !Swig::Director::swig_is_overridden_method(\"%s%s\", \"%s\");\n",
-	  prefix, Swig_class_name(Swig_methodclass(n)), name);
+      Printf(f->code, "upcall = !Swig::Director::swig_is_overridden_method(\"%s%s\", ZEND_THIS);\n",
+	  prefix, Swig_class_name(Swig_methodclass(n)));
     }
 
     Swig_director_emit_dynamic_cast(n, f);
@@ -992,11 +1351,8 @@
     }
 
     /* Insert argument output code */
-    bool hasargout = false;
     for (i = 0, p = l; p; i++) {
       if ((tm = Getattr(p, "tmap:argout")) && Len(tm)) {
-	hasargout = true;
-	//      Replaceall(tm,"$input",Getattr(p,"lname"));
 	Replaceall(tm, "$result", "return_value");
 	Replaceall(tm, "$arg", Getattr(p, "emit:input"));
 	Replaceall(tm, "$input", Getattr(p, "emit:input"));
@@ -1007,14 +1363,27 @@
       }
     }
 
-    Setattr(n, "wrap:name", wname);
+    if (!overloaded) {
+      Setattr(n, "wrap:name", wname);
+    } else {
+      if (class_name && Cmp(Getattr(n, "storage"), "friend") != 0) {
+        String *m_call = NewStringEmpty();
+        Printf(m_call, "ZEND_MN(%s_%s)", class_name, overloadwname);
+        Setattr(n, "wrap:name", m_call);
+      } else {
+        Setattr(n, "wrap:name", overloadwname);
+      }
+    }
+    Setattr(n, "wrapper:method:name", wname);
+
+    bool php_constructor = (constructor && Cmp(class_name, Getattr(n, "constructorHandler:sym:name")) == 0);
 
     /* emit function call */
     String *actioncode = emit_action(n);
 
     if ((tm = Swig_typemap_lookup_out("out", n, Swig_cresult_name(), f, actioncode))) {
       Replaceall(tm, "$input", Swig_cresult_name());
-      Replaceall(tm, "$result", "return_value");
+      Replaceall(tm, "$result", php_constructor ? "ZEND_THIS" : "return_value");
       Replaceall(tm, "$owner", newobject ? "1" : "0");
       Printf(f->code, "%s\n", tm);
     } else {
@@ -1044,15 +1413,25 @@
       Delete(tm);
     }
 
-    Printf(f->code, "thrown:\n");
-    Printf(f->code, "return;\n");
+    if (static_getter) {
+      Printf(f->code, "}\n");
+    }
 
-    /* Error handling code */
-    Printf(f->code, "fail:\n");
-    Printv(f->code, cleanup, NIL);
-    Append(f->code, "SWIG_FAIL();\n");
+    if (static_setter || static_getter) {
+      Printf(f->code, "}\n");
+    }
 
-    Printf(f->code, "}\n");
+    if (!static_setter) {
+      Printf(f->code, "thrown:\n");
+      Printf(f->code, "return;\n");
+
+      /* Error handling code */
+      Printf(f->code, "fail:\n");
+      Printv(f->code, cleanup, NIL);
+      Append(f->code, "SWIG_FAIL();\n");
+
+      Printf(f->code, "}\n");
+    }
 
     Replaceall(f->code, "$cleanup", cleanup);
     Replaceall(f->code, "$symname", iname);
@@ -1060,823 +1439,10 @@
     Wrapper_print(f, s_wrappers);
     DelWrapper(f);
     f = NULL;
-
-    if (overloaded && !Getattr(n, "sym:nextSibling")) {
-      dispatchFunction(n);
-    }
-
-    Delete(wname);
     wname = NULL;
 
-    if (!shadow) {
-      return SWIG_OK;
-    }
-
-    // Handle getters and setters.
-    if (wrapperType == membervar) {
-      const char *p = Char(iname);
-      if (strlen(p) > 4) {
-	p += strlen(p) - 4;
-	String *varname = Getattr(n, "membervariableHandler:sym:name");
-	if (strcmp(p, "_get") == 0) {
-	  Setattr(shadow_get_vars, varname, Getattr(n, "type"));
-	} else if (strcmp(p, "_set") == 0) {
-	  Setattr(shadow_set_vars, varname, iname);
-	}
-      }
-      return SWIG_OK;
-    }
-
-    // Only look at non-overloaded methods and the last entry in each overload
-    // chain (we check the last so that wrap:parms and wrap:name have been set
-    // for them all).
-    if (overloaded && Getattr(n, "sym:nextSibling") != 0)
-      return SWIG_OK;
-
-    if (!s_oowrappers)
-      s_oowrappers = NewStringEmpty();
-
-    if (newobject || wrapperType == memberfn || wrapperType == staticmemberfn || wrapperType == standard || wrapperType == staticmembervar) {
-      bool handle_as_overload = false;
-      String **arg_names;
-      String **arg_values;
-      unsigned char * byref;
-      // Method or static method or plain function.
-      const char *methodname = 0;
-      String *output = s_oowrappers;
-      if (constructor) {
-	class_has_ctor = true;
-	// Skip the Foo:: prefix.
-	char *ptr = strrchr(GetChar(current_class, "sym:name"), ':');
-	if (ptr) {
-	  ptr++;
-	} else {
-	  ptr = GetChar(current_class, "sym:name");
-	}
-	if (strcmp(ptr, GetChar(n, "constructorHandler:sym:name")) == 0) {
-	  methodname = "__construct";
-	} else {
-	  // The class has multiple constructors and this one is
-	  // renamed, so this will be a static factory function
-	  methodname = GetChar(n, "constructorHandler:sym:name");
-	}
-      } else if (wrapperType == memberfn) {
-	methodname = Char(Getattr(n, "memberfunctionHandler:sym:name"));
-      } else if (wrapperType == staticmemberfn) {
-	methodname = Char(Getattr(n, "staticmemberfunctionHandler:sym:name"));
-      } else if (wrapperType == staticmembervar) {
-	// Static member variable, wrapped as a function due to PHP limitations.
-	methodname = Char(Getattr(n, "staticmembervariableHandler:sym:name"));
-      } else {			// wrapperType == standard
-	methodname = Char(iname);
-	if (!s_fakeoowrappers)
-	  s_fakeoowrappers = NewStringEmpty();
-	output = s_fakeoowrappers;
-      }
-
-      bool really_overloaded = overloaded ? true : false;
-      int min_num_of_arguments = emit_num_required(l);
-      int max_num_of_arguments = emit_num_arguments(l);
-
-      Hash *ret_types = NewHash();
-      Setattr(ret_types, d, d);
-
-      bool non_void_return = (Cmp(d, "void") != 0);
-
-      if (overloaded) {
-	// Look at all the overloaded versions of this method in turn to
-	// decide if it's really an overloaded method, or just one where some
-	// parameters have default values.
-	Node *o = Getattr(n, "sym:overloaded");
-	while (o) {
-	  if (o == n) {
-	    o = Getattr(o, "sym:nextSibling");
-	    continue;
-	  }
-
-	  SwigType *d2 = Getattr(o, "type");
-	  if (!d2) {
-	    assert(constructor);
-	  } else if (!Getattr(ret_types, d2)) {
-	    Setattr(ret_types, d2, d2);
-	    non_void_return = non_void_return || (Cmp(d2, "void") != 0);
-	  }
-
-	  ParmList *l2 = Getattr(o, "wrap:parms");
-	  int num_arguments = emit_num_arguments(l2);
-	  int num_required = emit_num_required(l2);
-	  if (num_required < min_num_of_arguments)
-	    min_num_of_arguments = num_required;
-
-	  if (num_arguments > max_num_of_arguments) {
-	    max_num_of_arguments = num_arguments;
-	  }
-	  o = Getattr(o, "sym:nextSibling");
-	}
-
-	o = Getattr(n, "sym:overloaded");
-	while (o) {
-	  if (o == n) {
-	    o = Getattr(o, "sym:nextSibling");
-	    continue;
-	  }
-
-	  ParmList *l2 = Getattr(o, "wrap:parms");
-	  Parm *p = l, *p2 = l2;
-	  if (wrapperType == memberfn) {
-	    p = nextSibling(p);
-	    p2 = nextSibling(p2);
-	  }
-	  while (p && p2) {
-	    if (Cmp(Getattr(p, "type"), Getattr(p2, "type")) != 0)
-	      break;
-	    if (Cmp(Getattr(p, "name"), Getattr(p2, "name")) != 0)
-	      break;
-	    String *value = Getattr(p, "value");
-	    String *value2 = Getattr(p2, "value");
-	    if (value && !value2)
-	      break;
-	    if (!value && value2)
-	      break;
-	    if (value) {
-	      if (Cmp(value, value2) != 0)
-		break;
-	    }
-	    p = nextSibling(p);
-	    p2 = nextSibling(p2);
-	  }
-	  if (p && p2)
-	    break;
-	  // One parameter list is a prefix of the other, so check that all
-	  // remaining parameters of the longer list are optional.
-	  if (p2)
-	    p = p2;
-	  while (p && Getattr(p, "value"))
-	    p = nextSibling(p);
-	  if (p)
-	    break;
-	  o = Getattr(o, "sym:nextSibling");
-	}
-	if (!o) {
-	  // This "overloaded method" is really just one with default args.
-	  really_overloaded = false;
-	}
-      }
-
-      if (wrapperType == memberfn) {
-	// Allow for the "this" pointer.
-	--min_num_of_arguments;
-	--max_num_of_arguments;
-      }
-
-      arg_names = (String **) malloc(max_num_of_arguments * sizeof(String *));
-      if (!arg_names) {
-	fprintf(stderr, "Malloc failed!\n");
-	SWIG_exit(EXIT_FAILURE);
-      }
-      for (i = 0; i < max_num_of_arguments; ++i) {
-	arg_names[i] = NULL;
-      }
-
-      arg_values = (String **) malloc(max_num_of_arguments * sizeof(String *));
-      byref = (unsigned char *) malloc(max_num_of_arguments);
-      if (!arg_values || !byref) {
-	fprintf(stderr, "Malloc failed!\n");
-	SWIG_exit(EXIT_FAILURE);
-      }
-      for (i = 0; i < max_num_of_arguments; ++i) {
-	arg_values[i] = NULL;
-	byref[i] = false;
-      }
-
-      Node *o;
-      if (overloaded) {
-	o = Getattr(n, "sym:overloaded");
-      } else {
-	o = n;
-      }
-      while (o) {
-	int argno = 0;
-	Parm *p = Getattr(o, "wrap:parms");
-	if (wrapperType == memberfn)
-	  p = nextSibling(p);
-	while (p) {
-	  if (GetInt(p, "tmap:in:numinputs") == 0) {
-	    p = nextSibling(p);
-	    continue;
-	  }
-	  assert(0 <= argno && argno < max_num_of_arguments);
-	  byref[argno] = GetFlag(p, "tmap:in:byref");
-	  String *&pname = arg_names[argno];
-	  const char *pname_cstr = GetChar(p, "name");
-	  // Just get rid of the C++ namespace part for now.
-	  const char *ptr = NULL;
-	  if (pname_cstr && (ptr = strrchr(pname_cstr, ':'))) {
-	    pname_cstr = ptr + 1;
-	  }
-	  if (!pname_cstr) {
-	    // Unnamed parameter, e.g. int foo(int);
-	  } else if (!pname) {
-	    pname = NewString(pname_cstr);
-	  } else {
-	    size_t len = strlen(pname_cstr);
-	    size_t spc = 0;
-	    size_t len_pname = strlen(Char(pname));
-	    while (spc + len <= len_pname) {
-	      if (strncmp(pname_cstr, Char(pname) + spc, len) == 0) {
-		char ch = ((char *) Char(pname))[spc + len];
-		if (ch == '\0' || ch == ' ') {
-		  // Already have this pname_cstr.
-		  pname_cstr = NULL;
-		  break;
-		}
-	      }
-	      char *p = strchr(Char(pname) + spc, ' ');
-	      if (!p)
-		break;
-	      spc = (p + 4) - Char(pname);
-	    }
-	    if (pname_cstr) {
-	      Printf(pname, " or_%s", pname_cstr);
-	    }
-	  }
-	  String *value = NewString(Getattr(p, "value"));
-	  if (Len(value)) {
-	    /* Check that value is a valid constant in PHP (and adjust it if
-	     * necessary, or replace it with "?" if it's just not valid). */
-	    SwigType *type = Getattr(p, "type");
-	    switch (SwigType_type(type)) {
-	      case T_BOOL: {
-		if (Strcmp(value, "true") == 0 || Strcmp(value, "false") == 0)
-		  break;
-		char *p;
-		errno = 0;
-		long n = strtol(Char(value), &p, 0);
-	        Clear(value);
-		if (errno || *p) {
-		  Append(value, "?");
-		} else if (n) {
-		  Append(value, "true");
-		} else {
-		  Append(value, "false");
-		}
-		break;
-	      }
-	      case T_CHAR:
-	      case T_SCHAR:
-	      case T_SHORT:
-	      case T_INT:
-	      case T_LONG:
-	      case T_LONGLONG: {
-		char *p;
-		errno = 0;
-		long n = strtol(Char(value), &p, 0);
-		(void) n;
-		if (errno || *p) {
-		  Clear(value);
-		  Append(value, "?");
-		}
-		break;
-	      }
-	      case T_UCHAR:
-	      case T_USHORT:
-	      case T_UINT:
-	      case T_ULONG:
-	      case T_ULONGLONG: {
-		char *p;
-		errno = 0;
-		unsigned int n = strtoul(Char(value), &p, 0);
-		(void) n;
-		if (errno || *p) {
-		  Clear(value);
-		  Append(value, "?");
-		}
-		break;
-	      }
-	      case T_FLOAT:
-	      case T_DOUBLE:
-	      case T_LONGDOUBLE: {
-		char *p;
-		errno = 0;
-		double val = strtod(Char(value), &p);
-		if (errno || *p) {
-		  Clear(value);
-		  Append(value, "?");
-		} else if (strchr(Char(value), '.') == 0) {
-		  // Ensure value is a double constant, not an integer one.
-		  Append(value, ".0");
-		  double val2 = strtod(Char(value), &p);
-		  if (errno || *p || val != val2) {
-		    Clear(value);
-		    Append(value, "?");
-		  }
-		}
-		break;
-	      }
-	      case T_STRING:
-		if (Len(value) < 2) {
-		  // How can a string (including "" be less than 2 characters?)
-		  Clear(value);
-		  Append(value, "?");
-		} else {
-		  const char *v = Char(value);
-		  if (v[0] != '"' || v[Len(value) - 1] != '"') {
-		    Clear(value);
-		    Append(value, "?");
-		  }
-		  // Strings containing "$" require special handling, but we do
-		  // that later.
-		}
-		break;
-	      case T_VOID:
-		assert(false);
-		break;
-	      case T_POINTER: {
-		const char *v = Char(value);
-		if (v[0] == '(') {
-		  // Handle "(void*)0", "(TYPE*)0", "(char*)NULL", etc.
-		  v += strcspn(v + 1, "*()") + 1;
-		  if (*v == '*') {
-		    do {
-		      v++;
-		      v += strspn(v, " \t");
-		    } while (*v == '*');
-		    if (*v++ == ')') {
-		      v += strspn(v, " \t");
-		      String * old = value;
-		      value = NewString(v);
-		      Delete(old);
-		    }
-		  }
-		}
-		if (Strcmp(value, "NULL") == 0 ||
-		    Strcmp(value, "nullptr") == 0 ||
-		    Strcmp(value, "0") == 0 ||
-		    Strcmp(value, "0L") == 0) {
-		  Clear(value);
-		  Append(value, "null");
-		} else {
-		  Clear(value);
-		  Append(value, "?");
-		}
-		break;
-	      }
-	      default:
-		/* Safe default */
-		Clear(value);
-		Append(value, "?");
-		break;
-	    }
-
-	    if (!arg_values[argno]) {
-	      arg_values[argno] = value;
-	      value = NULL;
-	    } else if (Cmp(arg_values[argno], value) != 0) {
-	      // If a parameter has two different default values in
-	      // different overloaded forms of the function, we can't
-	      // set its default in PHP.  Flag this by setting its
-	      // default to `?'.
-	      Delete(arg_values[argno]);
-	      arg_values[argno] = NewString("?");
-	    }
-	  } else if (arg_values[argno]) {
-	    // This argument already has a default value in another overloaded
-	    // form, but doesn't in this form.  So don't try to do anything
-	    // clever, just let the C wrappers resolve the overload and set the
-	    // default values.
-	    //
-	    // This handling is safe, but I'm wondering if it may be overly
-	    // conservative (FIXME) in some cases.  It seems it's only bad when
-	    // there's an overloaded form with the appropriate number of
-	    // parameters which doesn't want the default value, but I need to
-	    // think about this more.
-	    Delete(arg_values[argno]);
-	    arg_values[argno] = NewString("?");
-	  }
-	  Delete(value);
-	  p = nextSibling(p);
-	  ++argno;
-	}
-	if (!really_overloaded)
-	  break;
-	o = Getattr(o, "sym:nextSibling");
-      }
-
-      /* Clean up any parameters which haven't yet got names, or whose
-       * names clash. */
-      Hash *seen = NewHash();
-      /* We need $this to refer to the current class, so can't allow it
-       * to be used as a parameter. */
-      Setattr(seen, "this", seen);
-
-      for (int argno = 0; argno < max_num_of_arguments; ++argno) {
-	String *&pname = arg_names[argno];
-	if (pname) {
-	  Replaceall(pname, " ", "_");
-	} else {
-	  /* We get here if the SWIG .i file has "int foo(int);" */
-	  pname = NewStringEmpty();
-	  Printf(pname, "arg%d", argno + 1);
-	}
-	// Check if we've already used this parameter name.
-	while (Getattr(seen, pname)) {
-	  // Append "_" to clashing names until they stop clashing...
-	  Printf(pname, "_");
-	}
-	Setattr(seen, Char(pname), seen);
-
-	if (arg_values[argno] && Cmp(arg_values[argno], "?") == 0) {
-	  handle_as_overload = true;
-	}
-      }
-      Delete(seen);
-      seen = NULL;
-
-      String *invoke = NewStringEmpty();
-      String *prepare = NewStringEmpty();
-      String *args = NewStringEmpty();
-
-      if (!handle_as_overload && !(really_overloaded && max_num_of_arguments > min_num_of_arguments)) {
-	Printf(invoke, "%s(", iname);
-	if (wrapperType == memberfn) {
-	  Printf(invoke, "$this->%s", SWIG_PTR);
-	}
-	for (int i = 0; i < max_num_of_arguments; ++i) {
-	  if (i)
-	    Printf(args, ",");
-	  if (i || wrapperType == memberfn)
-	    Printf(invoke, ",");
-	  if (byref[i]) Printf(args, "&");
-	  String *value = arg_values[i];
-	  if (value) {
-	    const char *v = Char(value);
-	    if (v[0] == '"') {
-	      /* In a PHP double quoted string, $ needs to be escaped as \$. */
-	      Replaceall(value, "$", "\\$");
-	    }
-	    Printf(args, "$%s=%s", arg_names[i], value);
-	  } else if (constructor && strcmp(methodname, "__construct") == 0 && i >= 1 && i < min_num_of_arguments) {
-	    // We need to be able to call __construct($resource).
-	    Printf(args, "$%s=null", arg_names[i]);
-	  } else {
-	    Printf(args, "$%s", arg_names[i]);
-	  }
-	  Printf(invoke, "$%s", arg_names[i]);
-	}
-	Printf(invoke, ")");
-      } else {
-	int i;
-	for (i = 0; i < min_num_of_arguments; ++i) {
-	  if (i)
-	    Printf(args, ",");
-	  Printf(args, "$%s", arg_names[i]);
-	}
-	String *invoke_args = NewStringEmpty();
-	if (wrapperType == memberfn) {
-	  Printf(invoke_args, "$this->%s", SWIG_PTR);
-	  if (min_num_of_arguments > 0)
-	    Printf(invoke_args, ",");
-	}
-	Printf(invoke_args, "%s", args);
-	if (constructor && min_num_of_arguments > 1) {
-	  // We need to be able to call __construct($resource).
-	  Clear(args);
-	  Printf(args, "$%s", arg_names[0]);
-	  for (i = 1; i < min_num_of_arguments; ++i) {
-	    Printf(args, ",");
-	    Printf(args, "$%s=null", arg_names[i]);
-	  }
-	}
-	bool had_a_case = false;
-	int last_handled_i = i - 1;
-	for (; i < max_num_of_arguments; ++i) {
-	  if (i)
-	    Printf(args, ",");
-	  const char *value = Char(arg_values[i]);
-	  // FIXME: (really_overloaded && handle_as_overload) is perhaps a
-	  // little conservative, but it doesn't hit any cases that it
-	  // shouldn't for Xapian at least (and we need it to handle
-	  // "Enquire::get_mset()" correctly).
-	  bool non_php_default = ((really_overloaded && handle_as_overload) ||
-				  !value || strcmp(value, "?") == 0);
-	  if (non_php_default)
-	    value = "null";
-	  Printf(args, "$%s=%s", arg_names[i], value);
-	  if (non_php_default) {
-	    if (!had_a_case) {
-	      Printf(prepare, "\t\tswitch (func_num_args()) {\n");
-	      had_a_case = true;
-	    }
-	    Printf(prepare, "\t\t");
-	    while (last_handled_i < i) {
-	      Printf(prepare, "case %d: ", ++last_handled_i);
-	    }
-	    if (non_void_return) {
-	      if (!constructor) {
-		Append(prepare, "$r=");
-	      } else if (wrapperType == staticmemberfn || wrapperType == staticmembervar) {
-		Append(prepare, "$r=");
-	      } else {
-		Printf(prepare, "$this->%s=", SWIG_PTR);
-	      }
-	    }
-	    if (!directorsEnabled() || !Swig_directorclass(n) || !constructor) {
-	      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, ",");
-	  Printf(invoke_args, "$%s", arg_names[i]);
-	}
-	Printf(prepare, "\t\t");
-	if (had_a_case)
-	  Printf(prepare, "default: ");
-	if (non_void_return) {
-	  if (!constructor) {
-	    Append(prepare, "$r=");
-	  } else if (wrapperType == staticmemberfn || wrapperType == staticmembervar) {
-	    Append(prepare, "$r=");
-	  } else {
-	    Printf(prepare, "$this->%s=", SWIG_PTR);
-	  }
-	}
-
-	if (!directorsEnabled() || !Swig_directorclass(n) || !constructor) {
-	  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);
-	Printf(invoke, "$r");
-      }
-
-      Printf(output, "\n");
-      // If it's a member function or a class constructor...
-      if (wrapperType == memberfn || (constructor && current_class)) {
-	String *acc = NewString(Getattr(n, "access"));
-	// If a base has the same method with public access, then PHP
-	// requires to have it here as public as well
-	Node *bases = Getattr(Swig_methodclass(n), "bases");
-	if (bases && Strcmp(acc, "public") != 0) {
-	  String *warnmsg = 0;
-	  int haspublicbase = 0;
-	  Iterator i = First(bases);
-	  while (i.item) {
-	    Node *j = firstChild(i.item);
-	    while (j) {
-	      String *jname = Getattr(j, "name");
-	      if (!jname || Strcmp(jname, Getattr(n, "name")) != 0) {
-		j = nextSibling(j);
-		continue;
-	      }
-	      if (Strcmp(nodeType(j), "cdecl") == 0) {
-		if (!Getattr(j, "access") || checkAttribute(j, "access", "public")) {
-		  haspublicbase = 1;
-		}
-	      } else if (Strcmp(nodeType(j), "using") == 0 && firstChild(j) && Strcmp(nodeType(firstChild(j)), "cdecl") == 0) {
-		if (!Getattr(firstChild(j), "access") || checkAttribute(firstChild(j), "access", "public")) {
-		  haspublicbase = 1;
-		}
-	      }
-	      if (haspublicbase) {
-		  warnmsg = NewStringf("Modifying the access of '%s::%s' to public, as the base '%s' has it as public as well.\n", Getattr(current_class, "classtype"), Getattr(n, "name"), Getattr(i.item, "classtype"));
-		  break;
-	      }
-	      j = nextSibling(j);
-	    }
-	    i = Next(i);
-	    if (haspublicbase) {
-	      break;
-	    }
-	  }
-	  if (Getattr(n, "access") && haspublicbase) {
-	    Delete(acc);
-	    acc = NewStringEmpty(); // implicitly public
-	    Swig_warning(WARN_PHP_PUBLIC_BASE, input_file, line_number, Char(warnmsg));
-	    Delete(warnmsg);
-	  }
-	}
-
-	if (Cmp(acc, "public") == 0) {
-	  // The default visibility for methods is public, so don't specify
-	  // that explicitly to keep the wrapper size down.
-	  Delete(acc);
-	  acc = NewStringEmpty();
-	} else if (Cmp(acc, "") != 0) {
-	  Append(acc, " ");
-	}
-
-	if (constructor) {
-	  // Discriminate between the PHP constructor and a C++ constructor
-	  // renamed to become a factory function in PHP.
-	  bool php_constructor = (strcmp(methodname, "__construct") == 0);
-	  const char * arg0 = NULL;
-	  if (max_num_of_arguments > 0) {
-	    arg0 = Char(arg_names[0]);
-	  } else if (php_constructor) {
-	    // The PHP constructor needs to be able to wrap a resource, but a
-	    // renamed constructor doesn't.
-	    arg0 = "res";
-	    Delete(args);
-	    args = NewString("$res=null");
-	  }
-	  String *mangled_type = SwigType_manglestr(Getattr(n, "type"));
-	  if (!php_constructor) {
-	    // A renamed constructor should be a static method.
-	    Append(acc, "static ");
-	  }
-	  Printf(output, "\t%sfunction %s(%s) {\n", acc, methodname, args);
-	  if (php_constructor) {
-	    // The PHP constructor needs to be able to wrap a resource, but a
-	    // renamed constructor doesn't.
-	    Printf(output, "\t\tif (is_resource($%s) && get_resource_type($%s) === '%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, "\t%sfunction %s(%s) {\n", acc, methodname, args);
-	}
-	Delete(acc);
-      } else if (wrapperType == staticmembervar) {
-	// We're called twice for a writable static member variable - first
-	// with "foo_set" and then with "foo_get" - so generate half the
-	// wrapper function each time.
-	//
-	// For a const static member, we only get called once.
-	static bool started = false;
-	if (!started) {
-	  Printf(output, "\tstatic function %s() {\n", methodname);
-	  if (max_num_of_arguments) {
-	    // Setter.
-	    Printf(output, "\t\tif (func_num_args()) {\n");
-	    Printf(output, "\t\t\t%s(func_get_arg(0));\n", iname);
-	    Printf(output, "\t\t\treturn;\n");
-	    Printf(output, "\t\t}\n");
-	    started = true;
-	    goto done;
-	  }
-	}
-	started = false;
-      } else {
-	Printf(output, "\tstatic function %s(%s) {\n", methodname, args);
-      }
-
-      if (!constructor)
-	Printf(output, "%s", prepare);
-      if (constructor) {
-	if (!directorsEnabled() || !Swig_directorclass(n)) {
-	  if (!Len(prepare)) {
-	    if (strcmp(methodname, "__construct") == 0) {
-	      Printf(output, "\t\t$this->%s=%s;\n", SWIG_PTR, invoke);
-	    } else {
-	      String *classname = Swig_class_name(current_class);
-	      Printf(output, "\t\treturn new %s%s(%s);\n", prefix, classname, invoke);
-	    }
-	  }
-	} else {
-	  Node *parent = Swig_methodclass(n);
-	  String *classname = Swig_class_name(parent);
-	  Printf(output, "\t\tif (get_class($this) === '%s%s') {\n", prefix, 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 (!non_void_return && !hasargout) {
-	if (Cmp(invoke, "$r") != 0)
-	  Printf(output, "\t\t%s;\n", invoke);
-      } else if (is_class(d)) {
-	if (Cmp(invoke, "$r") != 0)
-	  Printf(output, "\t\t$r=%s;\n", invoke);
-	if (Len(ret_types) == 1) {
-	  /* If d is abstract we can't create a new wrapper type d. */
-	  Node *d_class = classLookup(d);
-	  int is_abstract = 0;
-	  if (Getattr(d_class, "abstracts")) {
-	    is_abstract = 1;
-	  }
-	  if (newobject || !is_abstract) {
-	    Printf(output, "\t\tif (is_resource($r)) {\n");
-	    if (Getattr(classLookup(Getattr(n, "type")), "module")) {
-	      /*
-	       * _p_Foo -> Foo, _p_ns__Bar -> Bar
-	       * TODO: do this in a more elegant way
-	       */
-	      if (Len(prefix) == 0) {
-		Printf(output, "\t\t\t$c=substr(get_resource_type($r), (strpos(get_resource_type($r), '__') ? strpos(get_resource_type($r), '__') + 2 : 3));\n");
-	      } else {
-		Printf(output, "\t\t\t$c='%s'.substr(get_resource_type($r), (strpos(get_resource_type($r), '__') ? strpos(get_resource_type($r), '__') + 2 : 3));\n", prefix);
-	      }
-	      Printf(output, "\t\t\tif (class_exists($c)) return new $c($r);\n");
-	      Printf(output, "\t\t\treturn new %s%s($r);\n", prefix, Getattr(classLookup(d), "sym:name"));
-	    } else {
-	      Printf(output, "\t\t\t$c = new stdClass();\n");
-	      Printf(output, "\t\t\t$c->" SWIG_PTR " = $r;\n");
-	      Printf(output, "\t\t\treturn $c;\n");
-	    }
-	    Printf(output, "\t\t}\n\t\treturn $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");
-	  String *wrapobj = NULL;
-	  String *common = NULL;
-	  Iterator i = First(ret_types);
-	  while (i.item) {
-	    SwigType *ret_type = i.item;
-	    i = Next(i);
-	    String *mangled = NewString("_p");
-	    Printf(mangled, "%s", SwigType_manglestr(ret_type));
-	    Node *class_node = Getattr(zend_types, mangled);
-	    if (!class_node) {
-	      /* This is needed when we're returning a pointer to a type
-	       * rather than returning the type by value or reference. */
-	      Delete(mangled);
-	      mangled = NewString(SwigType_manglestr(ret_type));
-	      class_node = Getattr(zend_types, mangled);
-	      if (!class_node) {
-		// Return type isn't an object, so will be handled by the
-		// !is_resource() check before the switch.
-		continue;
-	      }
-	    }
-	    const char *classname = GetChar(class_node, "sym:name");
-	    if (!classname)
-	      classname = GetChar(class_node, "name");
-	    String * action = NewStringEmpty();
-	    if (classname)
-	      Printf(action, "return new %s%s($r);\n", prefix, classname);
-            else
-	      Printf(action, "return $r;\n");
-	    if (!wrapobj) {
-		wrapobj = NewString("\t\tswitch (get_resource_type($r)) {\n");
-		common = action;
-	    } else {
-		if (common && Cmp(common, action) != 0) {
-		    Delete(common);
-		    common = NULL;
-		}
-	    }
-	    Printf(wrapobj, "\t\t");
-	    if (i.item) {
-	      Printf(wrapobj, "case '%s': ", mangled);
-	    } else {
-	      Printf(wrapobj, "default: ");
-	    }
-	    Printv(wrapobj, action, NIL);
-	    if (action != common) Delete(action);
-	    Delete(mangled);
-	  }
-	  Printf(wrapobj, "\t\t}\n");
-	  if (common) {
-	      // All cases have the same action, so eliminate the switch
-	      // wrapper.
-	      Printf(output, "\t\t%s", common);
-	      Delete(common);
-	  } else {
-	      Printv(output, wrapobj, NIL);
-	  }
-	  Delete(wrapobj);
-	}
-      } else {
-	if (non_void_return || hasargout) {
-	  Printf(output, "\t\treturn %s;\n", invoke);
-	} else if (Cmp(invoke, "$r") != 0) {
-	  Printf(output, "\t\t%s;\n", invoke);
-	}
-      }
-      Printf(output, "\t}\n");
-
-done:
-      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;
+    if (overloaded && !Getattr(n, "sym:nextSibling")) {
+      dispatchFunction(n, constructor);
     }
 
     return SWIG_OK;
@@ -1886,56 +1452,13 @@
    * globalvariableHandler()
    * ------------------------------------------------------------ */
 
-  virtual int globalvariableHandler(Node *n) {
-    char *name = GetChar(n, "name");
-    char *iname = GetChar(n, "sym:name");
-    SwigType *t = Getattr(n, "type");
-    String *tm;
-
-    /* First do the wrappers such as name_set(), name_get()
-     * as provided by the baseclass's implementation of variableWrapper
-     */
-    if (Language::globalvariableHandler(n) == SWIG_NOWRAP) {
-      return SWIG_NOWRAP;
-    }
-
-    if (!addSymbol(iname, n))
-      return SWIG_ERROR;
-
-    /* First link C variables to PHP */
-
-    tm = Swig_typemap_lookup("varinit", n, name, 0);
-    if (tm) {
-      Printf(s_vinit, "%s\n", tm);
-    } else {
-      Swig_error(input_file, line_number, "Unable to link with type %s\n", SwigType_str(t, 0));
-    }
-
-    /* Now generate PHP -> C sync blocks */
-    /*
-       tm = Swig_typemap_lookup("varin", n, name, 0);
-       if(tm) {
-       Replaceall(tm, "$symname", iname);
-       Printf(f_c->code, "%s\n", tm);
-       } else {
-       Swig_error(input_file, line_number, "Unable to link with type %s\n", SwigType_str(t, 0));
-       }
-     */
-    /* Now generate C -> PHP sync blocks */
-    /*
-       if(!GetFlag(n,"feature:immutable")) {
-
-       tm = Swig_typemap_lookup("varout", n, name, 0);
-       if(tm) {
-       Replaceall(tm, "$symname", iname);
-       Printf(f_php->code, "%s\n", tm);
-       } else {
-       Swig_error(input_file, line_number, "Unable to link with type %s\n", SwigType_str(t, 0));
-       }
-       }
-     */
-    return SWIG_OK;
-  }
+  /* PHP doesn't support intercepting reads and writes to global variables
+   * (nor static property reads and writes so we can't wrap them as static
+   * properties on a dummy class) so just let SWIG do its default thing and
+   * wrap them as name_get() and name_set().
+   */
+  //virtual int globalvariableHandler(Node *n) {
+  //}
 
   /* ------------------------------------------------------------
    * constantWrapper()
@@ -1954,46 +1477,42 @@
 
     SwigType_remember(type);
 
-    if ((tm = Swig_typemap_lookup("consttab", n, name, 0))) {
+    if (!wrapping_member_constant) {
+      {
+	tm = Swig_typemap_lookup("consttab", n, name, 0);
+	Replaceall(tm, "$value", value);
+        if (Getattr(n, "tmap:consttab:rinit")) {
+          Printf(r_init, "%s\n", tm);
+        } else {
+          Printf(s_cinit, "%s\n", tm);
+        }
+      }
+
+      {
+        tm = Swig_typemap_lookup("classconsttab", n, name, 0);
+
+        Replaceall(tm, "$class", fake_class_name());
+        Replaceall(tm, "$const_name", iname);
+	Replaceall(tm, "$value", value);
+        if (Getattr(n, "tmap:classconsttab:rinit")) {
+          Printf(r_init, "%s\n", tm);
+        } else {
+          Printf(s_cinit, "%s\n", tm);
+        }
+      }
+    } else {
+      tm = Swig_typemap_lookup("classconsttab", n, name, 0);
+      Replaceall(tm, "$class", class_name);
+      Replaceall(tm, "$const_name", wrapping_member_constant);
       Replaceall(tm, "$value", value);
-      Printf(s_cinit, "%s\n", tm);
-    }
-
-    if (shadow) {
-      String *enumvalue = GetChar(n, "enumvalue");
-      String *set_to = iname;
-
-      if (!enumvalue) {
-	enumvalue = GetChar(n, "enumvalueex");
-      }
-
-      if (enumvalue && *Char(enumvalue)) {
-	// Check for a simple constant expression which is valid in PHP.
-	// If we find one, initialise the const member with it; otherwise
-	// we initialise it using the C/C++ wrapped constant.
-	const char *p;
-	for (p = Char(enumvalue); *p; ++p) {
-	  if (!isdigit((unsigned char)*p) && !strchr(" +-", *p)) {
-	    // FIXME: enhance to handle `<previous_enum> + 1' which is what
-	    // we get for enums that don't have an explicit value set.
-	    break;
-	  }
-	}
-	if (!*p)
-	  set_to = enumvalue;
-      }
-
-      if (wrapping_member_constant) {
-	if (!s_oowrappers)
-	  s_oowrappers = NewStringEmpty();
-	Printf(s_oowrappers, "\n\tconst %s = %s;\n", wrapping_member_constant, set_to);
+      if (Getattr(n, "tmap:classconsttab:rinit")) {
+        Printf(r_init, "%s\n", tm);
       } else {
-	if (!s_fakeoowrappers)
-	  s_fakeoowrappers = NewStringEmpty();
-	Printf(s_fakeoowrappers, "\n\tconst %s = %s;\n", iname, set_to);
+        Printf(s_cinit, "%s\n", tm);
       }
     }
 
+    wrapperType = standard;
     return SWIG_OK;
   }
 
@@ -2055,218 +1574,116 @@
    * ------------------------------------------------------------ */
 
   virtual int classHandler(Node *n) {
-    current_class = n;
+    String *symname = Getattr(n, "sym:name");
+    String *base_class = NULL;
+
+    class_name = symname;
+
+    Printf(all_cs_entry, "static zend_function_entry class_%s_functions[] = {\n", class_name);
+
+    // namespace code to introduce namespaces into wrapper classes.
+    //if (nameSpace != NULL)
+      //Printf(s_oinit, "INIT_CLASS_ENTRY(internal_ce, \"%s\\\\%s\", class_%s_functions);\n", nameSpace, class_name, class_name);
+    //else
+    Printf(s_oinit, "  INIT_CLASS_ENTRY(internal_ce, \"%s%s\", class_%s_functions);\n", prefix, class_name, class_name);
 
     if (shadow) {
       char *rename = GetChar(n, "sym:name");
 
       if (!addSymbol(rename, n))
 	return SWIG_ERROR;
-      shadow_classname = NewString(rename);
-
-      shadow_get_vars = NewHash();
-      shadow_set_vars = NewHash();
 
       /* Deal with inheritance */
       List *baselist = Getattr(n, "bases");
       if (baselist) {
 	Iterator base = First(baselist);
-	while (base.item && GetFlag(base.item, "feature:ignore")) {
-	  base = Next(base);
-	}
-	base = Next(base);
-	if (base.item) {
-	  /* Warn about multiple inheritance for additional base class(es) */
-	  while (base.item) {
-	    if (GetFlag(base.item, "feature:ignore")) {
-	      base = Next(base);
-	      continue;
+	while (base.item) {
+	  if (!GetFlag(base.item, "feature:ignore")) {
+	    if (!base_class) {
+	      base_class = Getattr(base.item, "sym:name");
+	    } else {
+	      /* Warn about multiple inheritance for additional base class(es) */
+	      String *proxyclassname = SwigType_str(Getattr(n, "classtypeobj"), 0);
+	      String *baseclassname = SwigType_str(Getattr(base.item, "name"), 0);
+	      Swig_warning(WARN_PHP_MULTIPLE_INHERITANCE, input_file, line_number,
+			   "Warning for %s, base %s ignored. Multiple inheritance is not supported in PHP.\n", proxyclassname, baseclassname);
 	    }
-	    String *proxyclassname = SwigType_str(Getattr(n, "classtypeobj"), 0);
-	    String *baseclassname = SwigType_str(Getattr(base.item, "name"), 0);
-	    Swig_warning(WARN_PHP_MULTIPLE_INHERITANCE, input_file, line_number,
-			 "Warning for %s, base %s ignored. Multiple inheritance is not supported in PHP.\n", proxyclassname, baseclassname);
-	    base = Next(base);
 	  }
-	}
-      }
-    }
-
-    classnode = n;
-    Language::classHandler(n);
-    classnode = 0;
-
-    if (shadow) {
-      List *baselist = Getattr(n, "bases");
-      Iterator ki, base;
-
-      if (baselist) {
-	base = First(baselist);
-	while (base.item && GetFlag(base.item, "feature:ignore")) {
 	  base = Next(base);
 	}
-      } else {
-	base.item = NULL;
       }
-
-      if (Getattr(n, "abstracts") && !GetFlag(n, "feature:notabstract")) {
-	Printf(s_phpclasses, "abstract ");
-      }
-
-      Printf(s_phpclasses, "class %s%s ", prefix, shadow_classname);
-      String *baseclass = NULL;
-      if (base.item && Getattr(base.item, "module")) {
-	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 ");
-      }
-      {
-	Node *node = NewHash();
-	Setattr(node, "type", Getattr(n, "name"));
-	Setfile(node, Getfile(n));
-	Setline(node, Getline(n));
-	String * interfaces = Swig_typemap_lookup("phpinterfaces", node, "", 0);
-	if (interfaces) {
-	  Printf(s_phpclasses, "implements %s ", interfaces);
-	}
-	Delete(node);
-      }
-      Printf(s_phpclasses, "{\n\tpublic $%s=null;\n", SWIG_PTR);
-      if (!baseclass) {
-	// Only store this in the base class (NB !baseclass means we *are*
-	// a base class...)
-	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.
-	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.
-	  for (; ki.key; ki = Next(ki)) {
-	    DOH *key = ki.key;
-	    String *iname = ki.item;
-	    Printf(s_phpclasses, "\t\tif ($var === '%s') return %s($this->%s,$value);\n", key, iname, SWIG_PTR);
-	  }
-	} else {
-	  Printf(s_phpclasses, "\t\t$func = '%s_'.$var.'_set';\n", shadow_classname);
-	  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);
-	if (baseclass) {
-	  Printf(s_phpclasses, "\t\t%s%s::__set($var,$value);\n", prefix, baseclass);
-	} else {
-	  Printf(s_phpclasses, "\t\t$this->%s[$var] = $value;\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);
-	if (baseclass) {
-	  Printf(s_phpclasses, "\t\t%s%s::__set($var,$value);\n", prefix, baseclass);
-	} else {
-	  Printf(s_phpclasses, "\t\t$this->%s[$var] = $value;\n", SWIG_DATA);
-	}
-	Printf(s_phpclasses, "\t}\n");
-      }
-
-      // Write property GET handlers
-      ki = First(shadow_get_vars);
-      if (ki.key) {
-	// This class has getters.
-	Printf(s_phpclasses, "\n\tfunction __get($var) {\n");
-	int non_class_getters = 0;
-	for (; ki.key; ki = Next(ki)) {
-	  DOH *key = ki.key;
-	  SwigType *d = ki.item;
-	  if (!is_class(d)) {
-	    ++non_class_getters;
-	    continue;
-	  }
-	  Printv(s_phpclasses, "\t\tif ($var === '", key, "') return new ", prefix, Getattr(classLookup(d), "sym:name"), "(", shadow_classname, "_", key, "_get($this->", SWIG_PTR, "));\n", NIL);
-	}
-	// FIXME: tune this threshold...
-	if (non_class_getters <= 2) {
-	  // Not many non-class getters, so avoid call_user_func.
-	  for (ki = First(shadow_get_vars); non_class_getters && ki.key;  ki = Next(ki)) {
-	    DOH *key = ki.key;
-	    SwigType *d = ki.item;
-	    if (is_class(d)) continue;
-	    Printv(s_phpclasses, "\t\tif ($var === '", key, "') return ", shadow_classname, "_", key, "_get($this->", SWIG_PTR, ");\n", NIL);
-	    --non_class_getters;
-	  }
-	} 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\tif ($var === 'thisown') return swig_%s_get_newobject($this->%s);\n", module, SWIG_PTR);
-	if (baseclass) {
-	  Printf(s_phpclasses, "\t\treturn %s%s::__get($var);\n", prefix, baseclass);
-	} else {
-	  // Reading an unknown property name gives null in PHP.
-	  Printf(s_phpclasses, "\t\treturn $this->%s[$var];\n", SWIG_DATA);
-	}
-	Printf(s_phpclasses, "\t}\n");
-
-	/* __isset() should return true for read-only properties, so check for
-	 * *_get() not *_set(). */
-	Printf(s_phpclasses, "\n\tfunction __isset($var) {\n");
-	Printf(s_phpclasses, "\t\tif (function_exists('%s_'.$var.'_get')) return true;\n", shadow_classname);
-	Printf(s_phpclasses, "\t\tif ($var === 'thisown') return true;\n");
-	if (baseclass) {
-	  Printf(s_phpclasses, "\t\treturn %s%s::__isset($var);\n", prefix, baseclass);
-	} else {
-	  Printf(s_phpclasses, "\t\treturn array_key_exists($var, $this->%s);\n", SWIG_DATA);
-	}
-	Printf(s_phpclasses, "\t}\n");
-      } else {
-	Printf(s_phpclasses, "\n\tfunction __get($var) {\n");
-	Printf(s_phpclasses, "\t\tif ($var === 'thisown') return swig_%s_get_newobject($this->%s);\n", module, SWIG_PTR);
-	if (baseclass) {
-	  Printf(s_phpclasses, "\t\treturn %s%s::__get($var);\n", prefix, baseclass);
-	} else {
-	  Printf(s_phpclasses, "\t\treturn $this->%s[$var];\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");
-	if (baseclass) {
-	  Printf(s_phpclasses, "\t\treturn %s%s::__isset($var);\n", prefix, baseclass);
-	} else {
-	  Printf(s_phpclasses, "\t\treturn array_key_exists($var, $this->%s);\n", SWIG_DATA);
-	}
-	Printf(s_phpclasses, "\t}\n");
-      }
-
-      if (!class_has_ctor) {
-	Printf(s_phpclasses, "\tfunction __construct($h) {\n");
-	Printf(s_phpclasses, "\t\t$this->%s=$h;\n", SWIG_PTR);
-	Printf(s_phpclasses, "\t}\n");
-      }
-
-      if (s_oowrappers) {
-	Printf(s_phpclasses, "%s", s_oowrappers);
-	Delete(s_oowrappers);
-	s_oowrappers = NULL;
-      }
-      class_has_ctor = false;
-
-      Printf(s_phpclasses, "}\n\n");
-
-      Delete(shadow_classname);
-      shadow_classname = NULL;
-
-      Delete(shadow_set_vars);
-      shadow_set_vars = NULL;
-      Delete(shadow_get_vars);
-      shadow_get_vars = NULL;
     }
+
+    if (GetFlag(n, "feature:exceptionclass") && Getattr(n, "feature:except")) {
+      /* PHP requires thrown objects to be instances of or derived from
+       * Exception, so that really needs to take priority over any
+       * explicit base class.
+       */
+      if (base_class) {
+	String *proxyclassname = SwigType_str(Getattr(n, "classtypeobj"), 0);
+	Swig_warning(WARN_PHP_MULTIPLE_INHERITANCE, input_file, line_number,
+		     "Warning for %s, base %s ignored. Multiple inheritance is not supported in PHP.\n", proxyclassname, base_class);
+      }
+      base_class = NewString("Exception");
+    }
+
+    if (Equal(base_class, "Exception")) {
+      Printf(s_oinit, "  SWIGTYPE_%s_ce = zend_register_internal_class_ex(&internal_ce, zend_ce_exception);\n", class_name);
+    } else if (is_class_wrapped(base_class)) {
+      Printf(s_oinit, "  SWIGTYPE_%s_ce = zend_register_internal_class_ex(&internal_ce, SWIGTYPE_%s_ce);\n", class_name, base_class);
+    } else {
+      Printf(s_oinit, "  SWIGTYPE_%s_ce = zend_register_internal_class(&internal_ce);\n", class_name);
+    }
+
+    if (Getattr(n, "abstracts") && !GetFlag(n, "feature:notabstract")) {
+      Printf(s_oinit, "  SWIGTYPE_%s_ce->ce_flags |= ZEND_ACC_EXPLICIT_ABSTRACT_CLASS;\n", class_name);
+    }
+
+    {
+      Node *node = NewHash();
+      Setattr(node, "type", Getattr(n, "name"));
+      Setfile(node, Getfile(n));
+      Setline(node, Getline(n));
+      String *interfaces = Swig_typemap_lookup("phpinterfaces", node, "", 0);
+      Replaceall(interfaces, " ", "");
+      if (interfaces) {
+	// It seems we need to wait until RINIT time to look up classes.
+	// The downside is that this then happens for every request.
+	Printf(r_init, "{\n");
+        List *interface_list = Split(interfaces, ',', -1);
+        int num_interfaces = Len(interface_list);
+        String *append_interface = NewStringEmpty();
+        for(int Iterator = 1; Iterator <= num_interfaces; Iterator++) {
+          String *interface = Getitem(interface_list, Iterator-1);
+          String *interface_ce = NewStringEmpty();
+          Printf(interface_ce, "php_%s_interface_ce_%d" , class_name , Iterator);
+          Printf(r_init, "  zend_class_entry *%s = zend_lookup_class(zend_string_init(\"%s\", sizeof(\"%s\") - 1, 0));\n", interface_ce, interface, interface);
+          Append(append_interface, interface_ce);
+          Append(append_interface, " ");
+        }
+        Chop(append_interface);
+        Replaceall(append_interface, " ", ",");
+        Printf(r_init, "  zend_class_implements(SWIGTYPE_%s_ce, %d, %s);\n", class_name, num_interfaces, append_interface);
+	Printf(r_init, "}\n");
+      }
+    }
+
+    Printf(s_oinit, "  SWIGTYPE_%s_ce->create_object = %s_object_new;\n", class_name, class_name);
+    Printf(s_oinit, "  memcpy(&%s_object_handlers,zend_get_std_object_handlers(), sizeof(zend_object_handlers));\n", class_name);
+    Printf(s_oinit, "  %s_object_handlers.clone_obj = NULL;\n", class_name);
+    // If not defined we aren't wrapping this type being passed or returned.
+    Printf(s_oinit, "#ifdef SWIGTYPE_p%s\n", SwigType_manglestr(Getattr(n, "classtypeobj")));
+    Printf(s_oinit, "  SWIG_TypeClientData(SWIGTYPE_p%s,SWIGTYPE_%s_ce);\n", SwigType_manglestr(Getattr(n, "classtypeobj")), class_name);
+    Printf(s_oinit, "#endif\n");
+    Printf(s_oinit, "\n");
+
+    Language::classHandler(n);
+
+    print_creation_free_wrapper(n);
+    generate_magic_property_methods(n, base_class);
+    Printf(all_cs_entry, " ZEND_FE_END\n};\n\n");
+
+    class_name = NULL;
     return SWIG_OK;
   }
 
@@ -2287,6 +1704,25 @@
    * ------------------------------------------------------------ */
 
   virtual int membervariableHandler(Node *n) {
+    if (magic_set == NULL) {
+      magic_set = NewStringEmpty();
+      magic_get = NewStringEmpty();
+      magic_isset = NewStringEmpty();
+    }
+
+    String *v_name = GetChar(n, "name");
+
+    Printf(magic_set, "\nelse if (strcmp(ZSTR_VAL(arg2),\"%s\") == 0) {\n", v_name);
+    Printf(magic_set, "ZVAL_STRING(&tempZval, \"%s_set\");\n", v_name);
+    Printf(magic_set, "call_user_function(EG(function_table),ZEND_THIS,&tempZval,return_value,1,&args[1]);\n}\n");
+
+    Printf(magic_get, "\nelse if (strcmp(ZSTR_VAL(arg2),\"%s\") == 0) {\n", v_name);
+    Printf(magic_get, "ZVAL_STRING(&tempZval, \"%s_get\");\n", v_name);
+    Printf(magic_get, "call_user_function(EG(function_table),ZEND_THIS,&tempZval,return_value,0,NULL);\n}\n");
+
+    Printf(magic_isset, "\nelse if (strcmp(ZSTR_VAL(arg2),\"%s\") == 0) {\n", v_name);
+    Printf(magic_isset, "RETVAL_TRUE;\n}\n");
+
     wrapperType = membervar;
     Language::membervariableHandler(n);
     wrapperType = standard;
@@ -2352,9 +1788,9 @@
       Delete(director_ctor_code);
       director_ctor_code = NewStringEmpty();
       director_prot_ctor_code = NewStringEmpty();
-      Printf(director_ctor_code, "if (Z_TYPE_P(arg0) == IS_NULL) { /* not subclassed */\n");
-      Printf(director_prot_ctor_code, "if (Z_TYPE_P(arg0) == IS_NULL) { /* not subclassed */\n");
-      Printf(director_ctor_code, "  %s = (%s *)new %s(%s);\n", Swig_cresult_name(), ctype, ctype, args);
+      Printf(director_ctor_code, "if (Swig::Director::swig_is_overridden_method(\"%s\", arg0)) { /* not subclassed */\n", class_name);
+      Printf(director_prot_ctor_code, "if (Swig::Director::swig_is_overridden_method(\"%s\", arg0)) { /* not subclassed */\n", class_name);
+      Printf(director_ctor_code, "  %s = new %s(%s);\n", Swig_cresult_name(), ctype, args);
       Printf(director_prot_ctor_code, "  SWIG_PHP_Error(E_ERROR, \"accessing abstract class or protected constructor\");\n");
       if (i) {
 	Insert(args, 0, ", ");
@@ -2374,62 +1810,10 @@
   }
 
   /* ------------------------------------------------------------
-   * CreateZendListDestructor()
+   * destructorHandler()
    * ------------------------------------------------------------ */
   //virtual int destructorHandler(Node *n) {
   //}
-  int CreateZendListDestructor(Node *n) {
-    String *name = GetChar(Swig_methodclass(n), "name");
-    String *iname = GetChar(n, "sym:name");
-    ParmList *l = Getattr(n, "parms");
-
-    String *destructorname = NewStringEmpty();
-    Printf(destructorname, "_%s", Swig_name_wrapper(iname));
-    Setattr(classnode, "destructor", destructorname);
-
-    Wrapper *f = NewWrapper();
-    Printf(f->def, "/* This function is designed to be called by the zend list destructors */\n");
-    Printf(f->def, "/* to typecast and do the actual destruction */\n");
-    Printf(f->def, "static void %s(zend_resource *res, const char *type_name) {\n", destructorname);
-
-    Wrapper_add_localv(f, "value", "swig_object_wrapper *value=(swig_object_wrapper *) res->ptr", NIL);
-    Wrapper_add_localv(f, "ptr", "void *ptr=value->ptr", NIL);
-    Wrapper_add_localv(f, "newobject", "int newobject=value->newobject", NIL);
-
-    emit_parameter_variables(l, f);
-    emit_attach_parmmaps(l, f);
-
-    // Get type of first arg, thing to be destructed
-    // Skip ignored arguments
-    Parm *p = l;
-    //while (Getattr(p,"tmap:ignore")) {p = Getattr(p,"tmap:ignore:next");}
-    while (checkAttribute(p, "tmap:in:numinputs", "0")) {
-      p = Getattr(p, "tmap:in:next");
-    }
-    SwigType *pt = Getattr(p, "type");
-
-    Printf(f->code, "  efree(value);\n");
-    Printf(f->code, "  if (! newobject) return; /* can't delete it! */\n");
-    Printf(f->code, "  arg1 = (%s)SWIG_ConvertResourceData(ptr, type_name, SWIGTYPE%s);\n", SwigType_lstr(pt, 0), SwigType_manglestr(pt));
-    Printf(f->code, "  if (! arg1) zend_error(E_ERROR, \"%s resource already free'd\");\n", Char(name));
-
-    Setattr(n, "wrap:name", destructorname);
-
-    String *actioncode = emit_action(n);
-    Append(f->code, actioncode);
-    Delete(actioncode);
-
-    Printf(f->code, "thrown:\n");
-    Append(f->code, "return;\n");
-    Append(f->code, "fail:\n");
-    Append(f->code, "SWIG_FAIL();\n");
-    Printf(f->code, "}\n");
-
-    Wrapper_print(f, s_wrappers);
-    DelWrapper(f);
-
-    return SWIG_OK;
-  }
 
   /* ------------------------------------------------------------
    * memberconstantHandler()
@@ -2612,12 +1996,10 @@
 	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));
+	       SwigType_namestr(name));
       }
     } else {
       /* attach typemaps to arguments (C/C++ -> PHP) */
-      String *parse_args = NewStringEmpty();
-
       Swig_director_parms_fixup(l);
 
       /* remove the wrapper 'w' since it was producing spurious temps */
@@ -2655,9 +2037,7 @@
 	    Delete(input);
 	    Replaceall(tm, "$owner", "0");
 	    Printv(wrap_args, tm, "\n", NIL);
-	    Putc('O', parse_args);
 	  } else {
-	    Append(parse_args, parse);
 	    Setattr(p, "emit:directorinput", pname);
 	    Replaceall(tm, "$input", pname);
 	    Replaceall(tm, "$owner", "0");
@@ -2676,25 +2056,6 @@
 	p = nextSibling(p);
       }
 
-      /* exception handling */
-      bool error_used_in_typemap = false;
-      tm = Swig_typemap_lookup("director:except", n, Swig_cresult_name(), 0);
-      if (!tm) {
-	tm = Getattr(n, "feature:director:except");
-	if (tm)
-	  tm = Copy(tm);
-      }
-      if ((tm) && Len(tm) && (Strcmp(tm, "1") != 0)) {
-	if (Replaceall(tm, "$error", "error")) {
-	  /* Only declare error if it is used by the typemap. */
-	  error_used_in_typemap = true;
-	  Append(w->code, "int error;\n");
-	}
-      } else {
-	Delete(tm);
-	tm = NULL;
-      }
-
       if (!idx) {
 	Printf(w->code, "zval *args = NULL;\n");
       } else {
@@ -2703,24 +2064,38 @@
       // typemap_directorout testcase requires that 0 can be assigned to the
       // variable named after the result of Swig_cresult_name(), so that can't
       // be a zval - make it a pointer to one instead.
-      Printf(w->code, "zval swig_zval_result, swig_funcname;\n");
+      Printf(w->code, "zval swig_zval_result;\n");
       Printf(w->code, "zval * SWIGUNUSED %s = &swig_zval_result;\n", Swig_cresult_name());
-      const char * funcname = GetChar(n, "sym:name");
-      Printf(w->code, "ZVAL_STRINGL(&swig_funcname, \"%s\", %d);\n", funcname, strlen(funcname));
 
       /* wrap complex arguments to zvals */
-      Printv(w->code, wrap_args, NIL);
+      Append(w->code, wrap_args);
 
-      if (error_used_in_typemap) {
-	Append(w->code, "error = ");
+      const char * funcname = GetChar(n, "sym:name");
+      Append(w->code, "{\n");
+      Append(w->code, "#if PHP_MAJOR_VERSION < 8\n");
+      Printf(w->code, "zval swig_funcname;\n");
+      Printf(w->code, "ZVAL_STRINGL(&swig_funcname, \"%s\", %d);\n", funcname, strlen(funcname));
+      Printf(w->code, "call_user_function(EG(function_table), &swig_self, &swig_funcname, &swig_zval_result, %d, args);\n", idx);
+      Append(w->code, "#else\n");
+      Printf(w->code, "zend_string *swig_funcname = zend_string_init(\"%s\", %d, 0);\n", funcname, strlen(funcname));
+      Append(w->code, "zend_function *swig_zend_func = zend_std_get_method(&Z_OBJ(swig_self), swig_funcname, NULL);\n");
+      Append(w->code, "zend_string_release(swig_funcname);\n");
+      Printf(w->code, "if (swig_zend_func) zend_call_known_instance_method(swig_zend_func, Z_OBJ(swig_self), &swig_zval_result, %d, args);\n", idx);
+      Append(w->code, "#endif\n");
+
+      /* exception handling */
+      tm = Swig_typemap_lookup("director:except", n, Swig_cresult_name(), 0);
+      if (!tm) {
+	tm = Getattr(n, "feature:director:except");
+	if (tm)
+	  tm = Copy(tm);
       }
-      Append(w->code, "call_user_function(EG(function_table), &swig_self, &swig_funcname,");
-      Printf(w->code, " &swig_zval_result, %d, args);\n", idx);
-
-      if (tm) {
+      if ((tm) && Len(tm) && (Strcmp(tm, "1") != 0)) {
+	Replaceall(tm, "$error", "EG(exception)");
 	Printv(w->code, Str(tm), "\n", NIL);
-	Delete(tm);
       }
+      Append(w->code, "}\n");
+      Delete(tm);
 
       /* marshal return value from PHP to C/C++ type */
 
@@ -2749,8 +2124,8 @@
 	  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(returntype, 0), SwigType_namestr(c_classname),
-	      SwigType_namestr(name));
+		       "Unable to use return type %s in director method %s::%s (skipping method).\n", SwigType_str(returntype, 0),
+		       SwigType_namestr(c_classname), SwigType_namestr(name));
 	  status = SWIG_ERROR;
 	}
       }
@@ -2767,7 +2142,6 @@
 	}
       }
 
-      Delete(parse_args);
       Delete(cleanup);
       Delete(outarg);
     }
@@ -2823,36 +2197,31 @@
     return status;
   }
 
-  int classDirectorDisown(Node *) {
-    return SWIG_OK;
+  int classDirectorDisown(Node *n) {
+    wrapperType = directordisown;
+    int result = Language::classDirectorDisown(n);
+    wrapperType = standard;
+    return result;
   }
 };				/* class PHP */
 
 static PHP *maininstance = 0;
 
-// We use this function to be able to write out zend_register_list_destructor_ex
-// lines for most things in the type table
+// Collect non-class pointer types from the type table so we can set up PHP
+// resource types for them later.
+//
 // NOTE: it's a function NOT A PHP::METHOD
 extern "C" {
 static void typetrace(const SwigType *ty, String *mangled, String *clientdata) {
-  Node *class_node;
-  if (!zend_types) {
-    zend_types = NewHash();
-  }
-  // we want to know if the type which reduced to this has a constructor
-  if ((class_node = maininstance->classLookup(ty))) {
-    if (!Getattr(zend_types, mangled)) {
-      // OK it may have been set before by a different SwigType but it would
-      // have had the same underlying class node I think
-      // - it is certainly required not to have different originating class
-      // nodes for the same SwigType
-      Setattr(zend_types, mangled, class_node);
+  if (maininstance->classLookup(ty) == NULL) {
+    // a non-class pointer
+    if (!zend_types) {
+      zend_types = NewHash();
     }
-  } else {			// a non-class pointer
-    Setattr(zend_types, mangled, NOTCLASS);
+    Setattr(zend_types, mangled, mangled);
   }
   if (r_prevtracefunc)
-    (*r_prevtracefunc) (ty, mangled, (String *) clientdata);
+    (*r_prevtracefunc) (ty, mangled, clientdata);
 }
 }
 
diff --git a/appveyor.yml b/appveyor.yml
index fad8f0b..6044e19 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -1,5 +1,4 @@
 platform:
-- x86
 - x64
 
 environment:
@@ -7,38 +6,11 @@
     MAKEJOBS: 2
 
   matrix:
-  - SWIGLANG: csharp
-    VSVER: 12
-  - SWIGLANG: csharp
-    VSVER: 14
-  - SWIGLANG: java
-    VSVER: 14
-  - SWIGLANG: python
-    VSVER: 14
-    VER: 27
-  - SWIGLANG: python
-    VSVER: 15
-    VER: 38
-    PY3: 3
-    APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
   - SWIGLANG: python
     VSVER: 16
     VER: 39
     PY3: 3
     APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
-  - SWIGLANG: python
-    OSVARIANT: cygwin
-  - SWIGLANG: java
-    OSVARIANT: mingw
-    APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
-  - SWIGLANG: python
-    OSVARIANT: mingw
-    WITHLANG: python
-    VER: 37
-    PY3: 3
-    APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
-  - BUILDSYSTEM: cmake
-    VSVER: 14
 
 matrix:
   allow_failures: