make Server and Client more flexible for subclassing
authorgggeek <giunta.gaetano@gmail.com>
Mon, 13 Feb 2023 17:42:52 +0000 (17:42 +0000)
committergggeek <giunta.gaetano@gmail.com>
Mon, 13 Feb 2023 17:42:52 +0000 (17:42 +0000)
NEWS.md
src/Client.php
src/Server.php

diff --git a/NEWS.md b/NEWS.md
index 16c440b..2391967 100644 (file)
--- a/NEWS.md
+++ b/NEWS.md
@@ -1,3 +1,9 @@
+## XML-RPC for PHP version 4.10.1 - unreleased
+
+* fixed: let the Server create Response objects whose class can be overridden by subclasses (this is required by the
+  json-rpc server now that the `xml_header` method has been moved to the `Request` object)
+
+
 ## XML-RPC for PHP version 4.10.0 - 2023/02/11
 
 * changed: the minimum php version required has been increased to 5.4
index d1c34ba..9ed54fc 100644 (file)
@@ -1554,7 +1554,7 @@ class Client
             $call['params'] = new Value($params, 'array');
             $calls[] = new Value($call, 'struct');
         }
-        $multiCall = new Request('system.multicall');
+        $multiCall = new static::$requestClass('system.multicall');
         $multiCall->addParam(new Value($calls, 'array'));
 
         // Attempt RPC call
@@ -1571,19 +1571,19 @@ class Client
 
         if ($this->return_type == 'xml') {
             for ($i = 0; $i < count($reqs); $i++) {
-                $response[] = new Response($rets, 0, '', 'xml', $result->httpResponse());
+                $response[] = new static::$responseClass($rets, 0, '', 'xml', $result->httpResponse());
             }
 
         } elseif ($this->return_type == 'phpvals') {
             if (!is_array($rets)) {
                 // bad return type from system.multicall
-                return new Response(0, PhpXmlRpc::$xmlrpcerr['multicall_error'],
+                return new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['multicall_error'],
                     PhpXmlRpc::$xmlrpcstr['multicall_error'] . ': not an array', 'phpvals', $result->httpResponse());
             }
             $numRets = count($rets);
             if ($numRets != count($reqs)) {
                 // wrong number of return values.
-                return new Response(0, PhpXmlRpc::$xmlrpcerr['multicall_error'],
+                return new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['multicall_error'],
                     PhpXmlRpc::$xmlrpcstr['multicall_error'] . ': incorrect number of responses', 'phpvals',
                     $result->httpResponse());
             }
@@ -1591,7 +1591,7 @@ class Client
             for ($i = 0; $i < $numRets; $i++) {
                 $val = $rets[$i];
                 if (!is_array($val)) {
-                    return new Response(0, PhpXmlRpc::$xmlrpcerr['multicall_error'],
+                    return new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['multicall_error'],
                         PhpXmlRpc::$xmlrpcstr['multicall_error'] . ": response element $i is not an array or struct",
                         'phpvals', $result->httpResponse());
                 }
@@ -1599,32 +1599,32 @@ class Client
                     case 1:
                         if (!isset($val[0])) {
                             // Bad value
-                            return new Response(0, PhpXmlRpc::$xmlrpcerr['multicall_error'],
+                            return new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['multicall_error'],
                                 PhpXmlRpc::$xmlrpcstr['multicall_error'] . ": response element $i has no value",
                                 'phpvals', $result->httpResponse());
                         }
                         // Normal return value
-                        $response[$i] = new Response($val[0], 0, '', 'phpvals', $result->httpResponse());
+                        $response[$i] = new static::$responseClass($val[0], 0, '', 'phpvals', $result->httpResponse());
                         break;
                     case 2:
                         /// @todo remove usage of @: it is apparently quite slow
                         $code = @$val['faultCode'];
                         if (!is_int($code)) {
                             /// @todo should we check that it is != 0?
-                            return new Response(0, PhpXmlRpc::$xmlrpcerr['multicall_error'],
+                            return new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['multicall_error'],
                                 PhpXmlRpc::$xmlrpcstr['multicall_error'] . ": response element $i has invalid or no faultCode",
                                 'phpvals', $result->httpResponse());
                         }
                         $str = @$val['faultString'];
                         if (!is_string($str)) {
-                            return new Response(0, PhpXmlRpc::$xmlrpcerr['multicall_error'],
+                            return new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['multicall_error'],
                                 PhpXmlRpc::$xmlrpcstr['multicall_error'] . ": response element $i has invalid or no FaultString",
                                 'phpvals', $result->httpResponse());
                         }
-                        $response[$i] = new Response(0, $code, $str, 'phpvals', $result->httpResponse());
+                        $response[$i] = new static::$responseClass(0, $code, $str, 'phpvals', $result->httpResponse());
                         break;
                     default:
-                        return new Response(0, PhpXmlRpc::$xmlrpcerr['multicall_error'],
+                        return new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['multicall_error'],
                             PhpXmlRpc::$xmlrpcstr['multicall_error'] . ": response element $i has too many items",
                             'phpvals', $result->httpResponse());
                 }
@@ -1633,14 +1633,14 @@ class Client
         } else {
             // return type == 'xmlrpcvals'
             if ($rets->kindOf() != 'array') {
-                return new Response(0, PhpXmlRpc::$xmlrpcerr['multicall_error'],
+                return new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['multicall_error'],
                     PhpXmlRpc::$xmlrpcstr['multicall_error'] . ": response element $i is not an array", 'xmlrpcvals',
                     $result->httpResponse());
             }
             $numRets = $rets->count();
             if ($numRets != count($reqs)) {
                 // wrong number of return values.
-                return new Response(0, PhpXmlRpc::$xmlrpcerr['multicall_error'],
+                return new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['multicall_error'],
                     PhpXmlRpc::$xmlrpcstr['multicall_error'] . ': incorrect number of responses', 'xmlrpcvals',
                     $result->httpResponse());
             }
@@ -1649,37 +1649,37 @@ class Client
                 switch ($val->kindOf()) {
                     case 'array':
                         if ($val->count() != 1) {
-                            return new Response(0, PhpXmlRpc::$xmlrpcerr['multicall_error'],
+                            return new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['multicall_error'],
                                 PhpXmlRpc::$xmlrpcstr['multicall_error'] . ": response element $i has too many items",
                                 'phpvals', $result->httpResponse());
                         }
                         // Normal return value
-                        $response[] = new Response($val[0], 0, '', 'xmlrpcvals', $result->httpResponse());
+                        $response[] = new static::$responseClass($val[0], 0, '', 'xmlrpcvals', $result->httpResponse());
                         break;
                     case 'struct':
                         if ($val->count() != 2) {
-                            return new Response(0, PhpXmlRpc::$xmlrpcerr['multicall_error'],
+                            return new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['multicall_error'],
                                 PhpXmlRpc::$xmlrpcstr['multicall_error'] . ": response element $i has too many items",
                                 'phpvals', $result->httpResponse());
                         }
                         /** @var Value $code */
                         $code = $val['faultCode'];
                         if ($code->kindOf() != 'scalar' || $code->scalarTyp() != 'int') {
-                            return new Response(0, PhpXmlRpc::$xmlrpcerr['multicall_error'],
+                            return new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['multicall_error'],
                                 PhpXmlRpc::$xmlrpcstr['multicall_error'] . ": response element $i has invalid or no faultCode",
                                 'xmlrpcvals', $result->httpResponse());
                         }
                         /** @var Value $str */
                         $str = $val['faultString'];
                         if ($str->kindOf() != 'scalar' || $str->scalarTyp() != 'string') {
-                            return new Response(0, PhpXmlRpc::$xmlrpcerr['multicall_error'],
+                            return new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['multicall_error'],
                                 PhpXmlRpc::$xmlrpcstr['multicall_error'] . ": response element $i has invalid or no faultCode",
                                 'xmlrpcvals', $result->httpResponse());
                         }
-                        $response[] = new Response(0, $code->scalarVal(), $str->scalarVal(), 'xmlrpcvals', $result->httpResponse());
+                        $response[] = new static::$responseClass(0, $code->scalarVal(), $str->scalarVal(), 'xmlrpcvals', $result->httpResponse());
                         break;
                     default:
-                        return new Response(0, PhpXmlRpc::$xmlrpcerr['multicall_error'],
+                        return new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['multicall_error'],
                             PhpXmlRpc::$xmlrpcstr['multicall_error'] . ": response element $i is not an array or struct",
                             'xmlrpcvals', $result->httpResponse());
                 }
index 74b9db0..e0436d3 100644 (file)
@@ -39,6 +39,9 @@ class Server
     const OPT_PHPVALS_ENCODING_OPTIONS = 'phpvals_encoding_options';
     const OPT_RESPONSE_CHARSET_ENCODING = 'response_charset_encoding';
 
+    /** @var string */
+    protected static $responseClass = '\\PhpXmlRpc\\Response';
+
     /**
      * @var string
      * Defines how functions in $dmap will be invoked: either using an xml-rpc Request object or plain php values.
@@ -605,14 +608,14 @@ class Server
                             $this->debugMsg("+++INFLATED REQUEST+++[" . strlen($data) . " chars]+++\n" . $data . "\n+++END+++");
                         }
                     } else {
-                        $r = new Response(0, PhpXmlRpc::$xmlrpcerr['server_decompress_fail'],
+                        $r = new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['server_decompress_fail'],
                             PhpXmlRpc::$xmlrpcstr['server_decompress_fail'], '', array('raw_data' => $rawData)
                         );
 
                         return $r;
                     }
                 } else {
-                    $r = new Response(0, PhpXmlRpc::$xmlrpcerr['server_cannot_decompress'],
+                    $r = new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['server_cannot_decompress'],
                         PhpXmlRpc::$xmlrpcstr['server_cannot_decompress'], '', array('raw_data' => $rawData)
                     );
 
@@ -712,20 +715,20 @@ class Server
                 $_xh = $xmlRpcParser->_xh;
             }
         } catch (NoSuchMethodException $e) {
-            return new Response(0, $e->getCode(), $e->getMessage());
+            return new static::$responseClass(0, $e->getCode(), $e->getMessage());
         }
 
         if ($_xh['isf'] == 3) {
             // (BC) we return XML error as a faultCode
             preg_match('/^XML error ([0-9]+)/', $_xh['isf_reason'], $matches);
-            return new Response(
+            return new static::$responseClass(
                 0,
                 PhpXmlRpc::$xmlrpcerrxml + (int)$matches[1],
                 $_xh['isf_reason']);
         } elseif ($_xh['isf']) {
             /// @todo separate better the various cases, as we have done in Request::parseResponse: invalid xml-rpc vs.
             ///       parsing error
-            return new Response(
+            return new static::$responseClass(
                 0,
                 PhpXmlRpc::$xmlrpcerr['invalid_request'],
                 PhpXmlRpc::$xmlrpcstr['invalid_request'] . ' ' . $_xh['isf_reason']);
@@ -746,6 +749,7 @@ class Server
             } else {
                 // build a Request object with data parsed from xml and add parameters in
                 $req = new Request($_xh['method']);
+                /// @todo for more speed, we could just pass in the array to the constructor (and loose the type validation)...
                 for ($i = 0; $i < count($_xh['params']); $i++) {
                     $req->addParam($_xh['params'][$i]);
                 }
@@ -785,7 +789,7 @@ class Server
 
         if (!isset($dmap[$methodName]['function'])) {
             // No such method
-            return new Response(0, PhpXmlRpc::$xmlrpcerr['unknown_method'], PhpXmlRpc::$xmlrpcstr['unknown_method']);
+            return new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['unknown_method'], PhpXmlRpc::$xmlrpcstr['unknown_method']);
         }
 
         // Check signature
@@ -798,7 +802,7 @@ class Server
             }
             if (!$ok) {
                 // Didn't match.
-                return new Response(
+                return new static::$responseClass(
                     0,
                     PhpXmlRpc::$xmlrpcerr['incorrect_params'],
                     PhpXmlRpc::$xmlrpcstr['incorrect_params'] . ": {$errStr}"
@@ -829,7 +833,7 @@ class Server
         // verify that function to be invoked is in fact callable
         if (!is_callable($func)) {
             $this->getLogger()->error("XML-RPC: " . __METHOD__ . ": function '$funcName' registered as method handler is not callable");
-            return new Response(
+            return new static::$responseClass(
                 0,
                 PhpXmlRpc::$xmlrpcerr['server_error'],
                 PhpXmlRpc::$xmlrpcstr['server_error'] . ": no function matches method"
@@ -860,9 +864,9 @@ class Server
                 if (!is_a($r, 'PhpXmlRpc\Response')) {
                     $this->getLogger()->error("XML-RPC: " . __METHOD__ . ": function '$funcName' registered as method handler does not return an xmlrpc response object but a " . gettype($r));
                     if (is_a($r, 'PhpXmlRpc\Value')) {
-                        $r = new Response($r);
+                        $r = new static::$responseClass($r);
                     } else {
-                        $r = new Response(
+                        $r = new static::$responseClass(
                             0,
                             PhpXmlRpc::$xmlrpcerr['server_error'],
                             PhpXmlRpc::$xmlrpcstr['server_error'] . ": function does not return xmlrpc response object"
@@ -880,12 +884,12 @@ class Server
                         $r = call_user_func_array($func, array($methodName, $params, $this->user_data));
                         // mimic EPI behaviour: if we get an array that looks like an error, make it an error response
                         if (is_array($r) && array_key_exists('faultCode', $r) && array_key_exists('faultString', $r)) {
-                            $r = new Response(0, (integer)$r['faultCode'], (string)$r['faultString']);
+                            $r = new static::$responseClass(0, (integer)$r['faultCode'], (string)$r['faultString']);
                         } else {
                             // functions using EPI api should NOT return resp objects, so make sure we encode the
                             // return type correctly
                             $encoder = new Encoder();
-                            $r = new Response($encoder->encode($r, array('extension_api')));
+                            $r = new static::$responseClass($encoder->encode($r, array('extension_api')));
                         }
                     } else {
                         $r = call_user_func_array($func, $params);
@@ -896,7 +900,7 @@ class Server
                     // q: what should we assume here about automatic encoding of datetimes and php classes instances?
                     // a: let the user decide
                     $encoder = new Encoder();
-                    $r = new Response($encoder->encode($r, $this->phpvals_encoding_options));
+                    $r = new static::$responseClass($encoder->encode($r, $this->phpvals_encoding_options));
                 }
             }
         /// @todo bump minimum php version to 7.1 and use a single catch clause instead of the duplicate blocks
@@ -918,10 +922,10 @@ class Server
                     if ($errCode == 0) {
                         $errCode = PhpXmlRpc::$xmlrpcerr['server_error'];
                     }
-                    $r = new Response(0, $errCode, $e->getMessage());
+                    $r = new static::$responseClass(0, $errCode, $e->getMessage());
                     break;
                 default:
-                    $r = new Response(0, PhpXmlRpc::$xmlrpcerr['server_error'], PhpXmlRpc::$xmlrpcstr['server_error']);
+                    $r = new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['server_error'], PhpXmlRpc::$xmlrpcstr['server_error']);
             }
         } catch (\Error $e) {
             // (barring errors in the lib) an uncaught exception happened in the called function, we wrap it in a
@@ -941,10 +945,10 @@ class Server
                     if ($errCode == 0) {
                         $errCode = PhpXmlRpc::$xmlrpcerr['server_error'];
                     }
-                    $r = new Response(0, $errCode, $e->getMessage());
+                    $r = new static::$responseClass(0, $errCode, $e->getMessage());
                     break;
                 default:
-                    $r = new Response(0, PhpXmlRpc::$xmlrpcerr['server_error'], PhpXmlRpc::$xmlrpcstr['server_error']);
+                    $r = new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['server_error'], PhpXmlRpc::$xmlrpcstr['server_error']);
             }
         }
 
@@ -1145,7 +1149,7 @@ class Server
     public static function _xmlrpcs_getCapabilities($server, $req = null)
     {
         $encoder = new Encoder();
-        return new Response($encoder->encode($server->getCapabilities()));
+        return new static::$responseClass($encoder->encode($server->getCapabilities()));
     }
 
     /**
@@ -1165,7 +1169,7 @@ class Server
             $outAr[] = new Value($key, 'string');
         }
 
-        return new Response(new Value($outAr, 'array'));
+        return new static::$responseClass(new Value($outAr, 'array'));
     }
 
     /**
@@ -1199,14 +1203,14 @@ class Server
                     }
                     $sigs[] = new Value($curSig, 'array');
                 }
-                $r = new Response(new Value($sigs, 'array'));
+                $r = new static::$responseClass(new Value($sigs, 'array'));
             } else {
                 // NB: according to the official docs, we should be returning a
                 // "none-array" here, which means not-an-array
-                $r = new Response(new Value('undef', 'string'));
+                $r = new static::$responseClass(new Value('undef', 'string'));
             }
         } else {
-            $r = new Response(0, PhpXmlRpc::$xmlrpcerr['introspect_unknown'], PhpXmlRpc::$xmlrpcstr['introspect_unknown']);
+            $r = new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['introspect_unknown'], PhpXmlRpc::$xmlrpcstr['introspect_unknown']);
         }
 
         return $r;
@@ -1235,12 +1239,12 @@ class Server
         }
         if (isset($dmap[$methName])) {
             if (isset($dmap[$methName]['docstring'])) {
-                $r = new Response(new Value($dmap[$methName]['docstring'], 'string'));
+                $r = new static::$responseClass(new Value($dmap[$methName]['docstring'], 'string'));
             } else {
-                $r = new Response(new Value('', 'string'));
+                $r = new static::$responseClass(new Value('', 'string'));
             }
         } else {
-            $r = new Response(0, PhpXmlRpc::$xmlrpcerr['introspect_unknown'], PhpXmlRpc::$xmlrpcstr['introspect_unknown']);
+            $r = new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['introspect_unknown'], PhpXmlRpc::$xmlrpcstr['introspect_unknown']);
         }
 
         return $r;
@@ -1303,7 +1307,7 @@ class Server
         foreach ($params as $i => $param) {
             if (!$req->addParam($param)) {
                 $i++; // for error message, we count params from 1
-                return static::_xmlrpcs_multicall_error(new Response(0,
+                return static::_xmlrpcs_multicall_error(new static::$responseClass(0,
                     PhpXmlRpc::$xmlrpcerr['incorrect_params'],
                     PhpXmlRpc::$xmlrpcstr['incorrect_params'] . ": probable xml error in param " . $i));
             }
@@ -1391,7 +1395,7 @@ class Server
             }
         }
 
-        return new Response(new Value($result, 'array'));
+        return new static::$responseClass(new Value($result, 'array'));
     }
 
     /**