From 3c7609ede36c694cbf0263c3d43dbc40d9999907 Mon Sep 17 00:00:00 2001 From: gggeek Date: Mon, 13 Feb 2023 17:42:52 +0000 Subject: [PATCH] make Server and Client more flexible for subclassing --- NEWS.md | 6 ++++++ src/Client.php | 40 +++++++++++++++++----------------- src/Server.php | 58 +++++++++++++++++++++++++++----------------------- 3 files changed, 57 insertions(+), 47 deletions(-) diff --git a/NEWS.md b/NEWS.md index 16c440b5..23919672 100644 --- 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 diff --git a/src/Client.php b/src/Client.php index d1c34bae..9ed54fc5 100644 --- a/src/Client.php +++ b/src/Client.php @@ -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()); } diff --git a/src/Server.php b/src/Server.php index 74b9db03..e0436d3f 100644 --- a/src/Server.php +++ b/src/Server.php @@ -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')); } /** -- 2.47.0