php: Throw exceptions instead of using errors

Parameter type errors and some other cases in SWIG-generated wrappers
now throw a PHP exception, which is how PHP's native parameter handling
deals with similar situations.

See #2014, but not closing yet as there may be more cases to convert.
diff --git a/CHANGES.current b/CHANGES.current
index d82cde7..388df86 100644
--- a/CHANGES.current
+++ b/CHANGES.current
@@ -7,6 +7,13 @@
 Version 4.1.0 (in progress)
 ===========================
 
+2021-05-04: olly
+	    [PHP] #2014 Throw exceptions instead of using errors
+
+	    Parameter type errors and some other cases in SWIG-generated wrappers
+	    now throw a PHP exception, which is how PHP's native parameter handling
+	    deals with similar situations.
+
 2021-05-17: adr26
             [Python] #1985 Fix memory leaks:
 
diff --git a/Lib/php/php.swg b/Lib/php/php.swg
index 42985ea..3b579b9 100644
--- a/Lib/php/php.swg
+++ b/Lib/php/php.swg
@@ -86,7 +86,8 @@
 %typemap(in) 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");
+    zend_type_error("Expected $&1_descriptor for argument $argnum of $symname");
+    return;
   }
   $1 = *tmp;
 %}
@@ -94,7 +95,8 @@
 %typemap(directorout) SWIGTYPE ($&1_ltype tmp)
 %{
   if (SWIG_ConvertPtr($input, (void **) &tmp, $&1_descriptor, 0) < 0 || tmp == NULL) {
-    SWIG_PHP_Error(E_ERROR, "Type error in argument $argnum of $symname. Expected $&1_descriptor");
+    zend_type_error("Expected $&1_descriptor for argument $argnum of $symname");
+    goto thrown;
   }
   $result = *tmp;
 %}
@@ -103,7 +105,8 @@
 	     SWIGTYPE []
 %{
   if (SWIG_ConvertPtr(&$input, (void **) &$1, $1_descriptor, 0) < 0) {
-    SWIG_PHP_Error(E_ERROR, "Type error in argument $argnum of $symname. Expected $1_descriptor");
+    zend_type_error("Expected $1_descriptor for argument $argnum of $symname");
+    return;
   }
 %}
 
@@ -111,7 +114,8 @@
 		      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");
+    zend_type_error("Expected $1_descriptor for argument $argnum of $symname");
+    goto thrown;
   }
   swig_acquire_ownership_obj((void*)$result, own);
 %}
@@ -120,7 +124,8 @@
              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");
+    zend_type_error("Expected $1_descriptor for argument $argnum of $symname");
+    return;
   }
 %}
 
@@ -128,7 +133,8 @@
 		      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");
+    zend_type_error("Expected $1_descriptor for argument $argnum of $symname");
+    goto thrown;
   }
   $result = tmp;
 %}
@@ -136,7 +142,8 @@
 %typemap(in) SWIGTYPE *const& ($*ltype temp)
 %{
   if (SWIG_ConvertPtr(&$input, (void **) &temp, $*1_descriptor, 0) < 0) {
-    SWIG_PHP_Error(E_ERROR, "Type error in argument $argnum of $symname. Expected $*1_descriptor");
+    zend_type_error("Expected $*1_descriptor for argument $argnum of $symname");
+    return;
   }
   $1 = ($1_ltype)&temp;
 %}
@@ -144,7 +151,8 @@
 %typemap(in) SWIGTYPE *DISOWN
 %{
   if (SWIG_ConvertPtr(&$input, (void **) &$1, $1_descriptor, SWIG_POINTER_DISOWN) < 0) {
-    SWIG_PHP_Error(E_ERROR, "Type error in argument $argnum of $symname. Expected $1_descriptor");
+    zend_type_error("Expected $1_descriptor for argument $argnum of $symname");
+    return;
   }
 %}
 
@@ -157,10 +165,12 @@
 %{
   if (SWIG_ConvertPtr(&$input, (void **) &$1, 0, 0) < 0) {
     /* Allow NULL from php for void* */
-    if (Z_ISNULL($input))
+    if (Z_ISNULL($input)) {
       $1=0;
-    else
-      SWIG_PHP_Error(E_ERROR, "Type error in argument $argnum of $symname. Expected $1_descriptor");
+    } else {
+      zend_type_error("Expected $1_descriptor for argument $argnum of $symname");
+      return;
+    }
   }
 %}
 
@@ -175,7 +185,8 @@
     /* So... we didn't get a ref or ptr, but we'll accept NULL by reference */
     if (!(Z_ISREF($input) && Z_ISNULL_P(Z_REFVAL($input)))) {
       /* wasn't a pre/ref/thing, OR anything like an int thing */
-      SWIG_PHP_Error(E_ERROR, "Type error in argument $arg of $symname.");
+      zend_throw_exception(zend_ce_type_error, "Type error in argument $arg of $symname", 0);
+      return;
     }
   }
   force=0;
diff --git a/Lib/php/phppointers.i b/Lib/php/phppointers.i
index 1475683..8b4e75e 100644
--- a/Lib/php/phppointers.i
+++ b/Lib/php/phppointers.i
@@ -6,7 +6,7 @@
     CONVERT_IN(tmp, $*1_ltype, $input);
     $1 = &tmp;
   } else {
-    SWIG_PHP_Error(E_ERROR, SWIG_PHP_Arg_Error_Msg($argnum, Expected a reference));
+    zend_type_error(SWIG_PHP_Arg_Error_Msg($argnum, Expected a reference));
   }
 %}
 %typemap(argout) TYPE *REF,
diff --git a/Lib/php/typemaps.i b/Lib/php/typemaps.i
index c248a58..aaea0a2 100644
--- a/Lib/php/typemaps.i
+++ b/Lib/php/typemaps.i
@@ -276,7 +276,8 @@
     /* So... we didn't get a ref or ptr, but we'll accept NULL by reference */
     if (!(Z_ISREF($input) && Z_ISNULL_P(Z_REFVAL($input)))) {
       /* wasn't a pre/ref/thing, OR anything like an int thing */
-      SWIG_PHP_Error(E_ERROR, "Type error in argument $arg of $symname.");
+      zend_type_error("Expected reference or NULL for argument $arg of $symname");
+      SWIG_FAIL;
     }
   }
   force=0;
diff --git a/Source/Modules/php.cxx b/Source/Modules/php.cxx
index 1ef67d0..40076fc 100644
--- a/Source/Modules/php.cxx
+++ b/Source/Modules/php.cxx
@@ -830,9 +830,7 @@
 
     Printv(f->code, dispatch, "\n", NIL);
 
-    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);
+    Printf(f->code, "zend_throw_exception(zend_ce_type_error, \"No matching function for overloaded '%s'\", 0);\n", symname);
     Printv(f->code, "thrown:\n", NIL);
     Printv(f->code, "return;\n", NIL);
     Printv(f->code, "}\n", NIL);
@@ -924,7 +922,10 @@
     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, "  if (!arg) {\n");
+    Printf(f->code, "    zend_throw_exception(zend_ce_type_error, \"this pointer is NULL\", 0);\n");
+    Printf(f->code, "    return;\n");
+    Printf(f->code, "  }\n");
     Printf(f->code, "  arg2 = Z_STR(args[0]);\n\n");
 
     Printf(f->code, "if (!arg2) {\n  RETVAL_NULL();\n}\n");
@@ -963,7 +964,10 @@
     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, "  if (!arg) {\n");
+    Printf(f->code, "    zend_throw_exception(zend_ce_type_error, \"this pointer is NULL\", 0);\n");
+    Printf(f->code, "    return;\n");
+    Printf(f->code, "  }\n");
     Printf(f->code, "  arg2 = Z_STR(args[0]);\n\n");
 
     Printf(f->code, "if (!arg2) {\n  RETVAL_NULL();\n}\n");
@@ -997,7 +1001,9 @@
     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, "  if(!arg) {\n");
+    Printf(f->code, "    zend_throw_exception(zend_ce_type_error, \"this pointer is NULL\", 0);\n");
+    Printf(f->code, "  }\n");
     Printf(f->code, "  arg2 = Z_STR(args[0]);\n\n");
 
     Printf(f->code, "if (!arg2) {\n  RETVAL_FALSE;\n}\n");
@@ -1301,7 +1307,10 @@
 	Setattr(p, "emit:input", source);
 	Printf(f->code, "%s\n", tm);
 	if (i == 0 && Getattr(p, "self")) {
-	  Printf(f->code, "\tif(!arg1) SWIG_PHP_Error(E_ERROR, \"this pointer is NULL\");\n");
+	  Printf(f->code, "\tif(!arg1) {\n");
+	  Printf(f->code, "\t  zend_throw_exception(zend_ce_type_error, \"this pointer is NULL\", 0);\n");
+	  Printf(f->code, "\t  return;\n");
+	  Printf(f->code, "\t}\n");
 	}
 	p = Getattr(p, "tmap:in:next");
 	if (i >= num_required) {
@@ -1820,7 +1829,9 @@
       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");
+      Printf(director_prot_ctor_code,
+	     "  zend_throw_exception(zend_ce_type_error, \"accessing abstract class or protected constructor\", 0);\n"
+	     "  return;\n");
       if (i) {
 	Insert(args, 0, ", ");
       }