Merge branch 'master' into gsoc2017-php7-classes-via-c-api
diff --git a/.travis.yml b/.travis.yml
index 7f78609..c434c2e 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,454 +1,30 @@
 language: cpp
 matrix:
   include:
-    - compiler: clang
-      os: linux
-      env: SWIGLANG=
-      dist: xenial
     - compiler: gcc
       os: linux
-      env: SWIGLANG=
-      dist: xenial
+      env: SWIGLANG=php VER=7.0 CONFIGOPTS=--enable-cpp11-testing CPPSTD=c++11
+      dist: bionic
     - compiler: gcc
       os: linux
-      env: SWIGLANG= BUILDSYSTEM=cmake
-      dist: xenial
-    - os: linux
-      env: SWIGLANG= GCC=4.4
-      dist: xenial
-    - os: linux
-      env: SWIGLANG= GCC=4.6
-      dist: xenial
-    - os: linux
-      env: SWIGLANG= GCC=4.7
-      dist: xenial
-    - os: linux
-      env: SWIGLANG= GCC=4.8
-      dist: xenial
-    - os: linux
-      env: SWIGLANG= GCC=4.9
-      dist: xenial
-    - os: linux
-      env: SWIGLANG= GCC=6
-      dist: xenial
-    - os: linux
-      env: SWIGLANG= GCC=7
-      dist: xenial
-    - os: linux
-      env: SWIGLANG= GCC=8
-      dist: xenial
-    - os: linux
-      env: SWIGLANG= GCC=9
-      dist: xenial
-    - os: linux
-      env: SWIGLANG= GCC=10
-      dist: focal
+      env: SWIGLANG=php VER=7.1 CONFIGOPTS=--enable-cpp11-testing CPPSTD=c++11
+      dist: bionic
     - compiler: gcc
       os: linux
-      env: SWIGLANG=csharp
-      dist: xenial
+      env: SWIGLANG=php VER=7.2 CONFIGOPTS=--enable-cpp11-testing CPPSTD=c++11
+      dist: bionic
     - compiler: gcc
       os: linux
-      env: SWIGLANG=d VER=2.066.0
-      dist: xenial
+      env: SWIGLANG=php VER=7.3 CONFIGOPTS=--enable-cpp11-testing CPPSTD=c++11
+      dist: bionic
     - compiler: gcc
       os: linux
-      env: SWIGLANG=d VER=2.086.1
-      dist: xenial
+      env: SWIGLANG=php VER=7.4 CONFIGOPTS=--enable-cpp11-testing CPPSTD=c++11
+      dist: bionic
     - compiler: gcc
       os: linux
-      env: SWIGLANG=go VER=1.3
-      dist: xenial
-    - compiler: gcc
-      os: linux
-      env: SWIGLANG=go VER=1.8
-      dist: xenial
-    - compiler: gcc
-      os: linux
-      env: SWIGLANG=go VER=1.12 CSTD=gnu99
-      dist: xenial
-    - compiler: gcc
-      os: linux
-      env: SWIGLANG=go VER=1.16 CSTD=gnu99
-      dist: xenial
-    - compiler: gcc
-      os: linux
-      env: SWIGLANG=guile
-      dist: xenial
-    - compiler: gcc
-      os: linux
-      env: SWIGLANG=java
-      dist: xenial
-    - compiler: gcc
-      os: linux
-      env: SWIGLANG=javascript ENGINE=node VER=6 CPP11=1
-      dist: xenial
-    - compiler: gcc
-      os: linux
-      env: SWIGLANG=javascript ENGINE=node VER=8 CPP11=1
-      dist: xenial
-    - compiler: gcc
-      os: linux
-      env: SWIGLANG=javascript ENGINE=node VER=10 CPP11=1
-      dist: xenial
-    - compiler: gcc
-      os: linux
-      env: SWIGLANG=javascript ENGINE=node VER=12 CPP11=1
-      dist: xenial
-    - compiler: gcc
-      os: linux
-      env: SWIGLANG=javascript ENGINE=node VER=14 CPP11=1
-      dist: xenial
-    - compiler: gcc
-      os: linux
-      env: SWIGLANG=javascript ENGINE=node VER=16 CPP14=1
-      dist: xenial
-    - compiler: gcc
-      os: linux
-      env: SWIGLANG=javascript ENGINE=jsc
-      dist: xenial
-    - compiler: gcc
-      os: linux
-      env: SWIGLANG=javascript ENGINE=v8
-      dist: xenial
-    - compiler: gcc
-      os: linux
-      env: SWIGLANG=lua
-      dist: xenial
-    - compiler: gcc
-      os: linux
-      env: SWIGLANG=lua VER=5.3
-      dist: xenial
-    - compiler: gcc
-      os: linux
-      env: SWIGLANG=mzscheme
-      dist: xenial
-    - compiler: gcc
-      os: linux
-      env: SWIGLANG=ocaml
-      dist: xenial
-    - compiler: gcc
-      os: linux
-      env: SWIGLANG=octave SWIGJOBS=-j2
-      dist: xenial   # Octave v4.0.0
-    - compiler: gcc
-      os: linux
-      env: SWIGLANG=octave SWIGJOBS=-j2 CPP11=1
-      dist: bionic   # Octave v4.2.2
-    - compiler: gcc
-      os: linux
-      env: SWIGLANG=perl5
-      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
-      os: linux
-      env: SWIGLANG=php VER=8.0
-      dist: xenial
-    - compiler: gcc
-      os: linux
-      env: SWIGLANG=python # 2.7
-      dist: xenial
-    - compiler: gcc
-      os: linux
-      env: SWIGLANG=python PY3=3 VER=3.2
-      dist: xenial
-    - compiler: gcc
-      os: linux
-      env: SWIGLANG=python PY3=3 VER=3.3
-      dist: xenial
-    - compiler: gcc
-      os: linux
-      env: SWIGLANG=python PY3=3 VER=3.4
-      dist: xenial
-    - compiler: gcc
-      os: linux
-      env: SWIGLANG=python PY3=3 VER=3.5
-      dist: xenial
-    - compiler: gcc
-      os: linux
-      env: SWIGLANG=python PY3=3 VER=3.6
-      dist: xenial
-    - compiler: gcc
-      os: linux
-      env: SWIGLANG=python PY3=3 VER=3.7
-      dist: xenial
-    - compiler: gcc
-      os: linux
-      env: SWIGLANG=python PY3=3 VER=3.8
-      dist: xenial
-    - compiler: gcc
-      os: linux
-      env: SWIGLANG=python PY3=3 VER=3.9
-      dist: xenial
-    - compiler: gcc
-      os: linux
-      env: SWIGLANG=python SWIG_FEATURES=-builtin
-      dist: xenial
-    - compiler: gcc
-      os: linux
-      env: SWIGLANG=python SWIG_FEATURES="-builtin -O"
-      dist: xenial
-    - os: linux
-      env: SWIGLANG=python SWIG_FEATURES=-builtin GCC=6 CPP11=1
-      dist: xenial
-    - os: linux
-      env: SWIGLANG=python SWIG_FEATURES=-builtin GCC=6 CPP11=1 PY3=3 VER=3.9
-      dist: xenial
-    - compiler: gcc
-      os: linux
-      env: SWIGLANG=python SWIG_FEATURES=-builtin PY3=3 VER=3.4
-      dist: xenial
-    - compiler: gcc
-      os: linux
-      env: SWIGLANG=python SWIG_FEATURES=-builtin PY3=3 VER=3.5
-      dist: xenial
-    - compiler: gcc
-      os: linux
-      env: SWIGLANG=python SWIG_FEATURES=-builtin PY3=3 VER=3.7
-      dist: xenial
-    - compiler: gcc
-      os: linux
-      env: SWIGLANG=python SWIG_FEATURES=-builtin PY3=3 VER=3.8
-      dist: xenial
-    - compiler: gcc
-      os: linux
-      env: SWIGLANG=python SWIG_FEATURES=-builtin PY3=3 VER=3.9
-      dist: xenial
-    - compiler: gcc
-      os: linux
-      env: SWIGLANG=python SWIG_FEATURES="-builtin -O" PY3=3 VER=3.9
-      dist: xenial
-    - compiler: gcc
-      os: linux
-      env: SWIGLANG=python SWIG_FEATURES=-builtin PY3=3 VER=3.9 SWIGOPTPY3=
-      dist: xenial
-    - compiler: gcc
-      os: linux
-      env: SWIGLANG=python SWIG_FEATURES=-O
-      dist: xenial
-    - compiler: gcc
-      os: linux
-      env: SWIGLANG=python SWIG_FEATURES=-O PY3=3 VER=3.9
-      dist: xenial
-    - compiler: gcc
-      os: linux
-      env: SWIGLANG=r
-      dist: xenial
-    - compiler: gcc
-      os: linux
-      env: SWIGLANG=ruby VER=1.9
-      dist: xenial
-    - compiler: gcc
-      os: linux
-      env: SWIGLANG=ruby VER=2.0
-      dist: xenial
-    - compiler: gcc
-      os: linux
-      env: SWIGLANG=ruby VER=2.1
-      dist: xenial
-    - compiler: gcc
-      os: linux
-      env: SWIGLANG=ruby VER=2.2
-      dist: xenial
-    - compiler: gcc
-      os: linux
-      env: SWIGLANG=ruby VER=2.3
-      dist: xenial
-    - compiler: gcc
-      os: linux
-      env: SWIGLANG=ruby VER=2.4
-      dist: xenial
-    - compiler: gcc
-      os: linux
-      env: SWIGLANG=ruby VER=2.5
-      dist: xenial
-    - compiler: gcc
-      os: linux
-      env: SWIGLANG=ruby VER=2.6
-      dist: xenial
-    - compiler: gcc
-      os: linux
-      env: SWIGLANG=ruby VER=2.7
-      dist: xenial
-    - compiler: gcc
-      os: linux
-      env: SWIGLANG=ruby VER=3.0 CSTD=c99
-      dist: xenial
-    - compiler: gcc
-      os: linux
-      env: SWIGLANG=scilab
-      dist: xenial
-    - compiler: gcc
-      os: linux
-      env: SWIGLANG=tcl
-      dist: xenial
-    - os: linux
-      env: SWIGLANG=csharp CPP11=1
-      dist: xenial
-    - os: linux
-      env: SWIGLANG=go VER=1.6 CPP11=1
-      dist: xenial
-    - os: linux
-      env: SWIGLANG=java CPP11=1
-      dist: xenial
-    - os: linux
-      env: SWIGLANG=python CPP11=1
-      dist: xenial
-    - os: linux
-      env: SWIGLANG=r CPP11=1 # Note: making 'R CMD SHLIB' use a different compiler is non-trivial
-      dist: xenial
-    - os: linux
-      env: SWIGLANG=ruby CPP11=1
-      dist: xenial
-    - os: linux
-      env: SWIGLANG=tcl CPP11=1
-      dist: xenial
-    - os: linux
-      env: SWIGLANG=csharp GCC=6 CPP14=1
-      dist: xenial
-    - os: linux
-      env: SWIGLANG=go VER=1.6 GCC=6 CPP14=1
-      dist: xenial
-    - os: linux
-      env: SWIGLANG=java GCC=6 CPP14=1
-      dist: xenial
-    - os: linux
-      env: SWIGLANG=python GCC=6 CPP14=1
-      dist: xenial
-    - os: linux
-      env: SWIGLANG=ruby GCC=6 CPP14=1
-      dist: xenial
-    - os: linux
-      env: SWIGLANG=tcl GCC=6 CPP14=1
-      dist: xenial
-    - os: linux
-      env: SWIGLANG=java GCC=7 CPP14=1
-      dist: xenial
-    - os: linux
-      env: SWIGLANG=python GCC=7 CPP14=1
-      dist: xenial
-    - os: linux
-      env: SWIGLANG=csharp GCC=8 CPP17=1
-      dist: xenial
-    - os: linux
-      env: SWIGLANG=java GCC=8 CPP17=1
-      dist: xenial
-    - os: linux
-      env: SWIGLANG=python GCC=8 CPP17=1 PY3=3 VER=3.9
-      dist: xenial
-    - os: linux
-      env: SWIGLANG=csharp GCC=9 CPP17=1
-      dist: xenial
-    - os: linux
-      env: SWIGLANG=java GCC=9 CPP17=1
-      dist: xenial
-    - os: linux
-      env: SWIGLANG=python GCC=9 CPP17=1 PY3=3 VER=3.9
-      dist: xenial
-    - os: linux
-      arch: s390x
-      env: SWIGLANG=ruby CPP11=1
-      dist: xenial
-    - compiler: gcc
-      os: osx
-      osx_image: xcode12.2
-      env: SWIGLANG=
-    - compiler: clang
-      os: osx
-      osx_image: xcode12.2
-      env: SWIGLANG= BUILDSYSTEM=cmake
-    - compiler: clang
-      os: osx
-      osx_image: xcode12.2
-      env: SWIGLANG=
-    - compiler: clang
-      os: osx
-      osx_image: xcode12.2
-      env: SWIGLANG=csharp
-    - compiler: clang
-      os: osx
-      osx_image: xcode12.2
-      env: SWIGLANG=go CSTD=gnu99
-    - compiler: clang
-      os: osx
-      osx_image: xcode12.2
-      env: SWIGLANG=guile CSTD=c11
-    - compiler: clang
-      os: osx
-      osx_image: xcode12.2
-      env: SWIGLANG=java
-    - compiler: clang
-      os: osx
-      osx_image: xcode12.2
-      env: SWIGLANG=lua
-# octave-6.1 not working
-#    - compiler: clang
-#      os: osx
-#      osx_image: xcode12.2
-#      env: SWIGLANG=octave SWIGJOBS=-j2 CPP11=1
-    - compiler: clang
-      os: osx
-      osx_image: xcode12.2
-      env: SWIGLANG=perl5
-    - compiler: clang
-      os: osx
-      osx_image: xcode12.2
-      env: SWIGLANG=python
-    - compiler: clang
-      os: osx
-      osx_image: xcode12.2
-      env: SWIGLANG=python PY3=3
-    - compiler: clang
-      os: osx
-      osx_image: xcode12.2
-      env: SWIGLANG=ruby
-    - compiler: clang
-      os: osx
-      osx_image: xcode12.2
-      env: SWIGLANG=tcl
-    - compiler: clang
-      os: osx
-      osx_image: xcode12.2
-      env: SWIGLANG=java CPP17=1
-    - compiler: clang
-      os: osx
-      osx_image: xcode12.2
-      env: SWIGLANG=python PY3=3 CPP17=1
-
-  allow_failures:
-    # Newer version of D not yet working/supported
-    - compiler: gcc
-      os: linux
-      env: SWIGLANG=d VER=2.086.1
-      dist: xenial
-    # Experimental languages
-    - compiler: gcc
-      os: linux
-      env: SWIGLANG=mzscheme
-      dist: xenial
-    - compiler: gcc
-      os: linux
-      env: SWIGLANG=ocaml
-      dist: xenial
+      env: SWIGLANG=php VER=8.0 CONFIGOPTS=--enable-cpp11-testing CPPSTD=c++11
+      dist: bionic
 
 before_install:
   - date -u
diff --git a/Doc/Manual/Php.html b/Doc/Manual/Php.html
index 1752168..ad9773c 100644
--- a/Doc/Manual/Php.html
+++ b/Doc/Manual/Php.html
@@ -306,8 +306,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>
@@ -1008,8 +1010,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 +1066,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 +1159,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 +1172,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..2e719a6 100644
--- a/Examples/Makefile.in
+++ b/Examples/Makefile.in
@@ -1077,7 +1077,7 @@
 # -----------------------------------------------------------------
 
 php_run:
-	$(RUNTOOL) $(PHP) -n -d extension_dir=. -d display_errors=stderr $(PHP_SCRIPT) $(RUNPIPE)
+	$(RUNTOOL) $(PHP) -n -d extension_dir=. -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/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..597b2fc 100644
--- a/Examples/php/sync/runme.php
+++ b/Examples/php/sync/runme.php
@@ -1,12 +1,31 @@
 <?
 
-// Load module and PHP classes.
 include("example.php");
 
-echo "Got new object\n";
-echo "Got string $s and value $x \n";
+echo "PHP reading globals: string is '", s_get(), "' and value is ", x_get(), "\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/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/add_link_runme.php b/Examples/test-suite/php/add_link_runme.php
index 92c76ac..5fe585b 100644
--- a/Examples/test-suite/php/add_link_runme.php
+++ b/Examples/test-suite/php/add_link_runme.php
@@ -3,8 +3,8 @@
 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..81fc01c 100644
--- a/Examples/test-suite/php/argout_runme.php
+++ b/Examples/test-suite/php/argout_runme.php
@@ -22,16 +22,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..af892a7 100644
--- a/Examples/test-suite/php/arrays_global_runme.php
+++ b/Examples/test-suite/php/arrays_global_runme.php
@@ -3,9 +3,10 @@
 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..c36ebff 100644
--- a/Examples/test-suite/php/arrays_global_twodim_runme.php
+++ b/Examples/test-suite/php/arrays_global_twodim_runme.php
@@ -3,9 +3,10 @@
 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..4119555 100644
--- a/Examples/test-suite/php/arrays_runme.php
+++ b/Examples/test-suite/php/arrays_runme.php
@@ -4,7 +4,7 @@
 
 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..7e0da61 100644
--- a/Examples/test-suite/php/arrays_scope_runme.php
+++ b/Examples/test-suite/php/arrays_scope_runme.php
@@ -3,12 +3,12 @@
 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..a0dc69f 100644
--- a/Examples/test-suite/php/callback_runme.php
+++ b/Examples/test-suite/php/callback_runme.php
@@ -2,7 +2,10 @@
 
 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..748e72c 100644
--- a/Examples/test-suite/php/casts_runme.php
+++ b/Examples/test-suite/php/casts_runme.php
@@ -4,10 +4,10 @@
 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/class_ignore_runme.php b/Examples/test-suite/php/class_ignore_runme.php
index 7025fcc..5f33374 100644
--- a/Examples/test-suite/php/class_ignore_runme.php
+++ b/Examples/test-suite/php/class_ignore_runme.php
@@ -3,7 +3,7 @@
 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/cpp_basic_runme.php b/Examples/test-suite/php/cpp_basic_runme.php
index 8e145ab..2f96c64 100644
--- a/Examples/test-suite/php/cpp_basic_runme.php
+++ b/Examples/test-suite/php/cpp_basic_runme.php
@@ -4,11 +4,11 @@
 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 +16,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..419417e 100644
--- a/Examples/test-suite/php/cpp_static_runme.php
+++ b/Examples/test-suite/php/cpp_static_runme.php
@@ -4,10 +4,10 @@
 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..65ae667 100644
--- a/Examples/test-suite/php/director_abstract_runme.php
+++ b/Examples/test-suite/php/director_abstract_runme.php
@@ -3,11 +3,11 @@
 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..2174cf4 100644
--- a/Examples/test-suite/php/director_basic_runme.php
+++ b/Examples/test-suite/php/director_basic_runme.php
@@ -3,9 +3,11 @@
 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..aa4ed59 100644
--- a/Examples/test-suite/php/director_classic_runme.php
+++ b/Examples/test-suite/php/director_classic_runme.php
@@ -4,10 +4,10 @@
 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 +81,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..41b15d3 100644
--- a/Examples/test-suite/php/director_default_runme.php
+++ b/Examples/test-suite/php/director_default_runme.php
@@ -4,10 +4,10 @@
 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..fcceff9 100644
--- a/Examples/test-suite/php/director_detect_runme.php
+++ b/Examples/test-suite/php/director_detect_runme.php
@@ -4,10 +4,10 @@
 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..9b185e9 100644
--- a/Examples/test-suite/php/director_enum_runme.php
+++ b/Examples/test-suite/php/director_enum_runme.php
@@ -4,10 +4,10 @@
 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..d7f22ea 100644
--- a/Examples/test-suite/php/director_exception_runme.php
+++ b/Examples/test-suite/php/director_exception_runme.php
@@ -3,11 +3,11 @@
 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 +38,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..5555ccc 100644
--- a/Examples/test-suite/php/director_extend_runme.php
+++ b/Examples/test-suite/php/director_extend_runme.php
@@ -4,10 +4,10 @@
 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..c149fd3 100644
--- a/Examples/test-suite/php/director_finalizer_runme.php
+++ b/Examples/test-suite/php/director_finalizer_runme.php
@@ -3,16 +3,21 @@
 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 +46,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..c93c395 100644
--- a/Examples/test-suite/php/director_frob_runme.php
+++ b/Examples/test-suite/php/director_frob_runme.php
@@ -4,11 +4,11 @@
 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..8d67379 100644
--- a/Examples/test-suite/php/director_nested_runme.php
+++ b/Examples/test-suite/php/director_nested_runme.php
@@ -4,10 +4,10 @@
 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 +60,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..45ea6d5 100644
--- a/Examples/test-suite/php/director_overload_runme.php
+++ b/Examples/test-suite/php/director_overload_runme.php
@@ -3,8 +3,7 @@
 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_profile_runme.php b/Examples/test-suite/php/director_profile_runme.php
index e1ae5a3..1aa0556 100644
--- a/Examples/test-suite/php/director_profile_runme.php
+++ b/Examples/test-suite/php/director_profile_runme.php
@@ -3,8 +3,8 @@
 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..4f6cd30 100644
--- a/Examples/test-suite/php/director_protected_runme.php
+++ b/Examples/test-suite/php/director_protected_runme.php
@@ -3,9 +3,11 @@
 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..71881fd 100644
--- a/Examples/test-suite/php/director_stl_runme.php
+++ b/Examples/test-suite/php/director_stl_runme.php
@@ -4,10 +4,10 @@
 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..748e289 100644
--- a/Examples/test-suite/php/director_string_runme.php
+++ b/Examples/test-suite/php/director_string_runme.php
@@ -4,11 +4,11 @@
 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..dd1233d 100644
--- a/Examples/test-suite/php/director_thread_runme.php
+++ b/Examples/test-suite/php/director_thread_runme.php
@@ -7,12 +7,12 @@
 # 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..11cd68d 100644
--- a/Examples/test-suite/php/director_unroll_runme.php
+++ b/Examples/test-suite/php/director_unroll_runme.php
@@ -4,11 +4,11 @@
 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 +23,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..571dc6a 100644
--- a/Examples/test-suite/php/enum_scope_template_runme.php
+++ b/Examples/test-suite/php/enum_scope_template_runme.php
@@ -2,13 +2,16 @@
 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_prop_runme.php b/Examples/test-suite/php/evil_diamond_prop_runme.php
index dd572fa..221cf27 100644
--- a/Examples/test-suite/php/evil_diamond_prop_runme.php
+++ b/Examples/test-suite/php/evil_diamond_prop_runme.php
@@ -31,9 +31,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/exception_order_runme.php b/Examples/test-suite/php/exception_order_runme.php
index 854cd3a..0f8139f 100644
--- a/Examples/test-suite/php/exception_order_runme.php
+++ b/Examples/test-suite/php/exception_order_runme.php
@@ -2,9 +2,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/global_vars_runme.php b/Examples/test-suite/php/global_vars_runme.php
new file mode 100644
index 0000000..77fdec8
--- /dev/null
+++ b/Examples/test-suite/php/global_vars_runme.php
@@ -0,0 +1,28 @@
+<?php
+
+require "tests.php";
+require "global_vars.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..4b425e1 100644
--- a/Examples/test-suite/php/grouping_runme.php
+++ b/Examples/test-suite/php/grouping_runme.php
@@ -5,7 +5,7 @@
 
 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..7db71fa 100644
--- a/Examples/test-suite/php/ignore_parameter_runme.php
+++ b/Examples/test-suite/php/ignore_parameter_runme.php
@@ -4,7 +4,7 @@
 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..c9a6fb4 100644
--- a/Examples/test-suite/php/import_nomodule_runme.php
+++ b/Examples/test-suite/php/import_nomodule_runme.php
@@ -2,13 +2,15 @@
 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
+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..597011e 100644
--- a/Examples/test-suite/php/li_carrays_cpp_runme.php
+++ b/Examples/test-suite/php/li_carrays_cpp_runme.php
@@ -3,14 +3,14 @@
 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..27e97b3 100644
--- a/Examples/test-suite/php/li_carrays_runme.php
+++ b/Examples/test-suite/php/li_carrays_runme.php
@@ -3,14 +3,14 @@
 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..c4f3253 100644
--- a/Examples/test-suite/php/li_factory_runme.php
+++ b/Examples/test-suite/php/li_factory_runme.php
@@ -4,10 +4,10 @@
 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..f1229ba 100644
--- a/Examples/test-suite/php/li_std_string_runme.php
+++ b/Examples/test-suite/php/li_std_string_runme.php
@@ -3,9 +3,33 @@
 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 +48,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/newobject1_runme.php b/Examples/test-suite/php/newobject1_runme.php
index 03bba0c..6912104 100644
--- a/Examples/test-suite/php/newobject1_runme.php
+++ b/Examples/test-suite/php/newobject1_runme.php
@@ -4,10 +4,10 @@
 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/overload_rename_runme.php b/Examples/test-suite/php/overload_rename_runme.php
index 982db5c..83259ba 100644
--- a/Examples/test-suite/php/overload_rename_runme.php
+++ b/Examples/test-suite/php/overload_rename_runme.php
@@ -5,9 +5,9 @@
 
 // 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/php_iterator_runme.php b/Examples/test-suite/php/php_iterator_runme.php
index 48021bf..edaaf2b 100644
--- a/Examples/test-suite/php/php_iterator_runme.php
+++ b/Examples/test-suite/php/php_iterator_runme.php
@@ -3,7 +3,8 @@
 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/prefix_runme.php b/Examples/test-suite/php/prefix_runme.php
index d8f77ec..eaabcf3 100644
--- a/Examples/test-suite/php/prefix_runme.php
+++ b/Examples/test-suite/php/prefix_runme.php
@@ -4,10 +4,10 @@
 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/skel.php b/Examples/test-suite/php/skel.php
index 351cad7..53a717c 100644
--- a/Examples/test-suite/php/skel.php
+++ b/Examples/test-suite/php/skel.php
@@ -8,7 +8,7 @@
 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/sym_runme.php b/Examples/test-suite/php/sym_runme.php
index f6a8cda..21d71df 100644
--- a/Examples/test-suite/php/sym_runme.php
+++ b/Examples/test-suite/php/sym_runme.php
@@ -3,8 +3,8 @@
 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/tests.php b/Examples/test-suite/php/tests.php
index 264d4d3..cbdb9e2 100644
--- a/Examples/test-suite/php/tests.php
+++ b/Examples/test-suite/php/tests.php
@@ -1,16 +1,5 @@
 <?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();
@@ -28,6 +17,10 @@
     if (! is_array($extra)) {
       $df=array_flip(get_declared_classes());
       foreach($_original_classes as $class) unset($df[$class]);
+      // Filter out pointer wrappers such as SWIG/_p_int.
+      foreach(array_keys($df) as $class) {
+	if (preg_match('/^SWIG\\\\/', $class)) unset($df[$class]);
+      }
       $extra=array_keys($df);
     }
     return $extra;
@@ -46,9 +39,7 @@
       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))
+        if ((self::GETSET && preg_match('/_[gs]et$/', $func)))
           $extrags[]=$func;
         else $extra[]=$func;
 //      $extra=array_keys($df);
@@ -210,24 +201,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) {
diff --git a/Examples/test-suite/php/threads_exception_runme.php b/Examples/test-suite/php/threads_exception_runme.php
index 80717eb..4257389 100644
--- a/Examples/test-suite/php/threads_exception_runme.php
+++ b/Examples/test-suite/php/threads_exception_runme.php
@@ -4,11 +4,11 @@
 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/Lib/php/const.i b/Lib/php/const.i
index 32b4b9b..9c65640 100644
--- a/Lib/php/const.i
+++ b/Lib/php/const.i
@@ -3,6 +3,54 @@
  *
  * 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);
+%}
+
+%typemap(classconsttab) 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,
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..9e3dc75 100644
--- a/Lib/php/phpinit.swg
+++ b/Lib/php/phpinit.swg
@@ -9,17 +9,3 @@
 SWIG_php_minit {
   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..540fd35 100644
--- a/Source/Modules/php.cxx
+++ b/Source/Modules/php.cxx
@@ -12,52 +12,30 @@
  * -----------------------------------------------------------------------------
  */
 
-/* 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;
@@ -72,7 +50,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 +57,49 @@
 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, "\n{\n  zend_class_entry internal_ce;\n");
+    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\n", result);
+  }
+  return result;
+}
 
 /* To reduce code size (generated and compiled) we only want to emit each
  * different arginfo once, so we need to track which have been used.
  */
 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 +110,117 @@
   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, "{\n");
+    Printf(s_oinit, "  zend_class_entry internal_ce;\n");
+    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\n");
 
     ki = Next(ki);
   }
@@ -202,7 +249,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 +304,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 +345,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");
@@ -361,20 +403,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 +414,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 +427,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 +462,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 +490,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");
@@ -563,27 +558,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 +634,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);
@@ -669,24 +654,26 @@
     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;
-    }
-    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 +681,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 +716,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 +846,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 +873,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 *baseClassExtend) {
+    if (Cmp(baseClassExtend, "Exception") == 0 || !is_class_wrapped(baseClassExtend)) {
+      baseClassExtend = 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, "classtypeobj"), "*)(arg->ptr));\n",
+		      "  if (director) director->swig_disown();\n",
+		      "}\n", NIL);
+    }
+    Printf(f->code, "} else {\n");
+    if (baseClassExtend) {
+      Printf(f->code, "PHP_MN(%s___set)(INTERNAL_FUNCTION_PARAM_PASSTHRU);\n}\n", baseClassExtend);
+    } 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 (baseClassExtend) {
+      Printf(f->code, "PHP_MN(%s___get)(INTERNAL_FUNCTION_PARAM_PASSTHRU);\n}\n", baseClassExtend);
+    } 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 (baseClassExtend) {
+      Printf(f->code, "PHP_MN(%s___isset)(INTERNAL_FUNCTION_PARAM_PASSTHRU);\n}\n", baseClassExtend);
+    } 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 +1099,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 +1126,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 +1235,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 +1262,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 +1276,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 +1287,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 +1298,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 +1336,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 +1363,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 +1375,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 +1425,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 +1451,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 +1464,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 +1489,30 @@
 
     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);
+	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);
+	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);
-      } else {
-	if (!s_fakeoowrappers)
-	  s_fakeoowrappers = NewStringEmpty();
-	Printf(s_fakeoowrappers, "\n\tconst %s = %s;\n", iname, set_to);
-      }
-    }
-
+    wrapperType = standard;
     return SWIG_OK;
   }
 
@@ -2055,17 +1574,27 @@
    * ------------------------------------------------------------ */
 
   virtual int classHandler(Node *n) {
-    current_class = n;
+    String *symname = Getattr(n, "sym:name");
+    String *baseClassExtend = NULL;
+    bool exceptionClassFlag = false;
+
+    class_name = symname;
+
+    Printf(all_cs_entry, "static zend_function_entry class_%s_functions[] = {\n", class_name);
+
+    Printf(s_oinit, "\n{\n  zend_class_entry internal_ce;\n");
+    
+    // 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");
@@ -2074,6 +1603,8 @@
 	while (base.item && GetFlag(base.item, "feature:ignore")) {
 	  base = Next(base);
 	}
+        if (base.item)
+          baseClassExtend = Getattr(base.item, "sym:name");
 	base = Next(base);
 	if (base.item) {
 	  /* Warn about multiple inheritance for additional base class(es) */
@@ -2092,181 +1623,69 @@
       }
     }
 
-    classnode = n;
-    Language::classHandler(n);
-    classnode = 0;
+    if (Cmp(Getattr(n, "feature:exceptionclass"), "1") == 0 && Getattr(n, "feature:except")) {
+        if (baseClassExtend) {
+          Swig_warning(WARN_PHP_MULTIPLE_INHERITANCE, input_file, line_number,
+                         "Warning for %s, base %s ignored. Multiple inheritance is not supported in PHP.\n", class_name, baseClassExtend);
+        }
+        baseClassExtend = NewString(class_name);
+        Append(baseClassExtend, "_Exception");
 
-    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;
+        Printf(s_oinit, "  zend_class_entry *SWIGTYPE_%s_ce = zend_lookup_class(zend_string_init(\"Exception\", sizeof(\"Exception\") - 1, 0));\n", baseClassExtend);
+        exceptionClassFlag = true;
     }
+
+    if (baseClassExtend && (exceptionClassFlag || is_class_wrapped(baseClassExtend))) {
+      Printf(s_oinit, "  SWIGTYPE_%s_ce = zend_register_internal_class_ex(&internal_ce, SWIGTYPE_%s_ce);\n", class_name, baseClassExtend);
+    } 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) {
+        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(s_oinit, "  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(s_oinit, "  zend_class_implements(SWIGTYPE_%s_ce, %d, %s);\n", class_name, num_interfaces, append_interface);
+      }
+    }
+
+    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\n");
+
+    Language::classHandler(n);
+
+    print_creation_free_wrapper(n);
+    generate_magic_property_methods(n, baseClassExtend);
+    Printf(all_cs_entry, " ZEND_FE_END\n};\n\n");
+
+    class_name = NULL;
     return SWIG_OK;
   }
 
@@ -2287,6 +1706,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 +1790,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 +1812,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 +1998,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 +2039,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 +2058,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 +2066,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 +2126,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 +2144,6 @@
 	}
       }
 
-      Delete(parse_args);
       Delete(cleanup);
       Delete(outarg);
     }
@@ -2823,36 +2199,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: