php: Stop using dl()

With modern PHP it only works with the CLI version of PHP, so it's
better to direct users to load the extension via "extension=" in
php.ini.

Suggested by ferdynator in #1529.
diff --git a/Doc/Manual/Php.html b/Doc/Manual/Php.html
index ad9773c..ad6192a 100644
--- a/Doc/Manual/Php.html
+++ b/Doc/Manual/Php.html
@@ -152,9 +152,9 @@
 </p>
 
 <p>
-For some SAPIs (for example, the CLI SAPI) you can instead use the
-<a href="https://www.php.net/manual/en/function.dl.php">dl() function</a> to load
-an extension at run time, by adding a line like this to the start of each
+If you're using the PHP CLI SAPI it's possible (but not recommended) to use the
+<a href="https://www.php.net/manual/en/function.dl.php">dl() function</a> to
+load an extension at run time, by adding a line like this to the start of each
 PHP script which uses your extension:
 </p>
 
@@ -163,25 +163,22 @@
 </pre></div>
 
 <p>
-But note that <tt>dl()</tt> isn't supported when running PHP through a
-webserver - you'll need to use <tt>extension</tt> in <tt>php.ini</tt> as
+But to do this portably you need to take into account that pathnames and the
+filename extension vary by platform, and for security reasons PHP no longer
+supports <tt>dl()</tt> when running PHP through a webserver.  Overall it's
+probably better to instead use <tt>extension</tt> in <tt>php.ini</tt> as
 described above.
 </p>
 
 <p>
-The PHP module which SWIG generates will also attempt to do the <tt>dl()</tt>
-call for you if the extension isn't already loaded:
+SWIG also generates a PHP module which defines PHP classes for the wrapped
+API, which you'll need to load, for example:
 </p>
 
 <div class="code"><pre>
         include("example.php");
 </pre></div>
 
-<p>
-This PHP module also defines the PHP classes for the wrapped API, so you'll
-almost certainly want to include it anyway.
-</p>
-
 <H2><a name="Php_nn2">32.2 Basic PHP interface</a></H2>
 
 
diff --git a/Examples/Makefile.in b/Examples/Makefile.in
index 2e719a6..16973c9 100644
--- a/Examples/Makefile.in
+++ b/Examples/Makefile.in
@@ -1053,6 +1053,7 @@
 PHP_INCLUDE = @PHPINC@
 PHP_SO      = @PHP_SO@
 PHP_SCRIPT  = $(SRCDIR)$(RUNME).php
+PHP_EXTENSION = example$(PHP_SO)
 
 # -------------------------------------------------------------------
 # Build a PHP dynamically loadable module (C)
@@ -1077,7 +1078,7 @@
 # -----------------------------------------------------------------
 
 php_run:
-	$(RUNTOOL) $(PHP) -n -d extension_dir=. -d display_errors=stderr -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)
+	$(RUNTOOL) $(PHP) -n -d extension_dir=. -d extension=$(PHP_EXTENSION) -d display_errors=stderr -r 'set_error_handler(function($$n,$$s,$$f,$$l){if($$f!==Null){print$$f;if($$l!==Null)print":$$l";print": ";}print"$$s\n";exit(1);});include($$argv[1]);' $(PHP_SCRIPT) $(RUNPIPE)
 
 # -----------------------------------------------------------------
 # Version display
diff --git a/Examples/test-suite/php/Makefile.in b/Examples/test-suite/php/Makefile.in
index 003e283..946549a 100644
--- a/Examples/test-suite/php/Makefile.in
+++ b/Examples/test-suite/php/Makefile.in
@@ -71,9 +71,9 @@
 # found, runs testcase.php, except for multicpptests.
 run_testcase = \
 	if [ -f $(SCRIPTDIR)/$(SCRIPTPREFIX)$*$(SCRIPTSUFFIX) ]; then \
-	  $(MAKE) -f $(top_builddir)/$(EXAMPLES)/Makefile PHP_SCRIPT=$(SCRIPTDIR)/$(SCRIPTPREFIX)$*$(SCRIPTSUFFIX) RUNTOOL='$(RUNTOOL)' php_run; \
+	  $(MAKE) -f $(top_builddir)/$(EXAMPLES)/Makefile PHP_EXTENSION=$(TARGETPREFIX)$*$(PHP_SO) PHP_SCRIPT=$(SCRIPTDIR)/$(SCRIPTPREFIX)$*$(SCRIPTSUFFIX) RUNTOOL='$(RUNTOOL)' php_run; \
 	elif [ -f $(SCRIPTDIR)/$(SCRIPTPREFIX)$*.php -a ! -f $(top_srcdir)/$(EXAMPLES)/$(TEST_SUITE)/$*.list ]; then \
-	  $(MAKE) -f $(top_builddir)/$(EXAMPLES)/Makefile PHP_SCRIPT=$(SCRIPTDIR)/$(SCRIPTPREFIX)$*.php RUNTOOL='$(RUNTOOL)' php_run; \
+	  $(MAKE) -f $(top_builddir)/$(EXAMPLES)/Makefile PHP_EXTENSION=$(TARGETPREFIX)$*$(PHP_SO) PHP_SCRIPT=$(SCRIPTDIR)/$(SCRIPTPREFIX)$*.php RUNTOOL='$(RUNTOOL)' php_run; \
 	fi
 
 # Clean: remove the generated .php file
diff --git a/Examples/test-suite/php/tests.php b/Examples/test-suite/php/tests.php
index cbdb9e2..7491bf1 100644
--- a/Examples/test-suite/php/tests.php
+++ b/Examples/test-suite/php/tests.php
@@ -175,7 +175,7 @@
     $extra=array_flip(check::get_extra_globals());
     foreach ($globals as $glob) {
       if (self::GETSET) {
-        if (! isset($extra[$glob])) $missing[]=$glob;
+        if (! function_exists($glob . "_get") && ! function_exists($glob . "_set")) $missing[]=$glob;
         else unset($extra[$glob]);
       } else {
         if (! isset($GLOBALS[$glob])) $missing[]=$glob;
diff --git a/Lib/php/const.i b/Lib/php/const.i
index 9c65640..79c6d24 100644
--- a/Lib/php/const.i
+++ b/Lib/php/const.i
@@ -38,7 +38,10 @@
   zend_declare_class_constant_string(SWIGTYPE_$class_ce, "$const_name", sizeof("$const_name") - 1, $value);
 %}
 
-%typemap(classconsttab) SWIGTYPE *,
+// This creates a zend_object to wrap the pointer, and we can't do that
+// before the Zend runtime has been initialised so we delay it until
+// RINIT.  The downside is it then happens for every request.
+%typemap(classconsttab,rinit=1) SWIGTYPE *,
                         SWIGTYPE &,
                         SWIGTYPE &&,
                         SWIGTYPE [] %{
@@ -79,7 +82,10 @@
                    const char []
   "SWIG_STRING_CONSTANT($symname, $value);";
 
-%typemap(consttab) SWIGTYPE *,
+// This creates a zend_object to wrap the pointer, and we can't do that
+// before the Zend runtime has been initialised so we delay it until
+// RINIT.  The downside is it then happens for every request.
+%typemap(consttab,rinit=1) SWIGTYPE *,
                    SWIGTYPE &,
                    SWIGTYPE &&,
                    SWIGTYPE [] {
diff --git a/Source/Modules/php.cxx b/Source/Modules/php.cxx
index b763867..05cb570 100644
--- a/Source/Modules/php.cxx
+++ b/Source/Modules/php.cxx
@@ -371,19 +371,6 @@
     Swig_banner(f_phpcode);
 
     Printf(f_phpcode, "\n");
-    Printf(f_phpcode, "// Try to load our extension if it's not already loaded.\n");
-    Printf(f_phpcode, "if (!extension_loaded('%s')) {\n", module);
-    Printf(f_phpcode, "  if (strtolower(substr(PHP_OS, 0, 3)) === 'win') {\n");
-    Printf(f_phpcode, "    if (!dl('php_%s.dll')) return;\n", module);
-    Printf(f_phpcode, "  } else {\n");
-    Printf(f_phpcode, "    // PHP_SHLIB_SUFFIX gives 'dylib' on MacOS X but modules are 'so'.\n");
-    Printf(f_phpcode, "    if (PHP_SHLIB_SUFFIX === 'dylib') {\n");
-    Printf(f_phpcode, "      if (!dl('%s.so')) return;\n", module);
-    Printf(f_phpcode, "    } else {\n");
-    Printf(f_phpcode, "      if (!dl('%s.'.PHP_SHLIB_SUFFIX)) return;\n", module);
-    Printf(f_phpcode, "    }\n");
-    Printf(f_phpcode, "  }\n");
-    Printf(f_phpcode, "}\n\n");
 
     /* sub-sections of the php file */
     pragma_code = NewStringEmpty();
@@ -1493,7 +1480,11 @@
       {
 	tm = Swig_typemap_lookup("consttab", n, name, 0);
 	Replaceall(tm, "$value", value);
-	Printf(s_cinit, "%s\n", tm);
+        if (Getattr(n, "tmap:consttab:rinit")) {
+          Printf(r_init, "%s\n", tm);
+        } else {
+          Printf(s_cinit, "%s\n", tm);
+        }
       }
 
       {
@@ -1502,14 +1493,22 @@
         Replaceall(tm, "$class", fake_class_name());
         Replaceall(tm, "$const_name", iname);
 	Replaceall(tm, "$value", value);
-	Printf(s_cinit, "%s\n", tm);
+        if (Getattr(n, "tmap:classconsttab:rinit")) {
+          Printf(r_init, "%s\n", tm);
+        } else {
+          Printf(s_cinit, "%s\n", tm);
+        }
       }
     } else {
       tm = Swig_typemap_lookup("classconsttab", n, name, 0);
       Replaceall(tm, "$class", class_name);
       Replaceall(tm, "$const_name", wrapping_member_constant);
       Replaceall(tm, "$value", value);
-      Printf(s_cinit, "%s\n", tm);
+      if (Getattr(n, "tmap:classconsttab:rinit")) {
+        Printf(r_init, "%s\n", tm);
+      } else {
+        Printf(s_cinit, "%s\n", tm);
+      }
     }
 
     wrapperType = standard;
@@ -1653,6 +1652,9 @@
       String *interfaces = Swig_typemap_lookup("phpinterfaces", node, "", 0);
       Replaceall(interfaces, " ", "");
       if (interfaces) {
+	// It seems we need to wait until RINIT time to look up classes.
+	// The downside is that this then happens for every request.
+	Printf(r_init, "{\n");
         List *interface_list = Split(interfaces, ',', -1);
         int num_interfaces = Len(interface_list);
         String *append_interface = NewStringEmpty();
@@ -1660,13 +1662,14 @@
           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);
+          Printf(r_init, "  zend_class_entry *%s = zend_lookup_class(zend_string_init(\"%s\", sizeof(\"%s\") - 1, 0));\n", interface_ce, interface, interface);
           Append(append_interface, interface_ce);
           Append(append_interface, " ");
         }
         Chop(append_interface);
         Replaceall(append_interface, " ", ",");
-        Printf(s_oinit, "  zend_class_implements(SWIGTYPE_%s_ce, %d, %s);\n", class_name, num_interfaces, append_interface);
+        Printf(r_init, "  zend_class_implements(SWIGTYPE_%s_ce, %d, %s);\n", class_name, num_interfaces, append_interface);
+	Printf(r_init, "}\n");
       }
     }