From b08a42e525d486f753ae8c1b2e79041c99d05601 Mon Sep 17 00:00:00 2001 From: gggeek Date: Mon, 28 Dec 2020 11:42:51 +0000 Subject: [PATCH] new features --- NEWS | 10 ++++++ composer.json | 8 ++++- extras/rsakey.pem | 9 ----- extras/workspace.testPhpServer.fttb | 1 - src/Encoder.php | 52 +++++++++++++++++++++++------ src/Helper/XMLParser.php | 6 ++-- src/PhpXmlRpc.php | 5 ++- src/Value.php | 18 ++++++---- 8 files changed, 78 insertions(+), 31 deletions(-) delete mode 100644 extras/rsakey.pem delete mode 100644 extras/workspace.testPhpServer.fttb diff --git a/NEWS b/NEWS index 42bc099..ea95aa6 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,13 @@ +XML-RPC for PHP version 4.5.0 - (unreleased) + +* new: it is now possible to control the precision used when serializing DOUBLE values via usage of + `PhpXmlRpc::$xmlpc_double_precision` + +* improvements to the support of the XMLRPC extension emulation (as provided by the phpxmlrpc/polyfill-xmlrpc package) + +* removed obsolete files from the 'extras' directory + + XML-RPC for PHP version 4.4.3 - 2020/12/17 * fixed: compatibility with PHP 8.0 (fixes to the debugger, to the server's 'system.methodHelp' method and to the diff --git a/composer.json b/composer.json index 38df192..04c4361 100644 --- a/composer.json +++ b/composer.json @@ -3,7 +3,7 @@ "description": "A php library for building xmlrpc clients and servers", "license": "BSD-3-Clause", "homepage": "https://gggeek.github.io/phpxmlrpc/", - "keywords": [ "xmlrpc", "webservices" ], + "keywords": [ "xmlrpc", "xml-rpc","webservices" ], "require": { "php": "^5.3.0 || ^7.0 || ^8.0", "ext-xml": "*" @@ -24,8 +24,14 @@ "ext-curl": "Needed for HTTPS and HTTP 1.1 support, NTLM Auth etc...", "ext-zlib": "Needed for sending compressed requests and receiving compressed responses, if cURL is not available", "ext-mbstring": "Needed to allow reception of requests/responses in character sets other than ASCII,LATIN-1,UTF-8", + "phpxmlrpc/extras": "Adds more featured Server classes and other useful bits", + "phpxmlrpc/jsonrpc": "Adds support for the JSON-RPC protocol", "sami/sami": "Required for doc generation using pake. Version constraints: ^3.3 || ^4.1" }, + "_comment::conflict": "Within the extras package, only the XMLRPC extension emulation is not compatible... the JSONRPC part should be ok. Both have been moved to different packages anyway", + "conflict": { + "phpxmlrpc/extras": "<= 0.6.3" + }, "autoload": { "psr-4": {"PhpXmlRpc\\": "src/"} }, diff --git a/extras/rsakey.pem b/extras/rsakey.pem deleted file mode 100644 index 6c51248..0000000 --- a/extras/rsakey.pem +++ /dev/null @@ -1,9 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIBOgIBAAJBAM12w6/J20HMj0V9VC24xPFQG9RKSDt8vmviM+tnc1BgCrzPyF1v -3/rWGoWDjkJrE9WFOeqIjJHeEWWT4uKq2ZkCAwEAAQJAZZYJ7Nld+et9DvuHak/H -uBRGnjDYA+mKcObXitWMUzk2ZodL8UoCP1J9kKqV8Zp/l2cBZkLo0aWTN94sWkHy -rQIhAOhxWxRXSZ4kArIQqZnDG9JgtOAeaaFso/zpxIHpN6OrAiEA4klzl+rUc32/ -7SDcJYa9j5vehp1jCTnkN+n0rujTM8sCIAGwMRUovSQk5tAcRt8TB7SzdxzZm7LM -czR3DjJTW1AZAiEAlYN+svPgJ+cAdwdtLgZXHZoZb8xx8Vik6CTXHPKNCf0CIBQL -zF4Qp8/C+gjsXtEZJvhxY7i1luHn6iNwNnE932r3 ------END RSA PRIVATE KEY----- diff --git a/extras/workspace.testPhpServer.fttb b/extras/workspace.testPhpServer.fttb deleted file mode 100644 index 8d50758..0000000 --- a/extras/workspace.testPhpServer.fttb +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/Encoder.php b/src/Encoder.php index f2bc88f..3b69f1a 100644 --- a/src/Encoder.php +++ b/src/Encoder.php @@ -41,16 +41,18 @@ class Encoder $typ = key($xmlrpcVal->me); switch ($typ) { case 'dateTime.iso8601': - $xmlrpcVal->scalar = $val; - $xmlrpcVal->type = 'datetime'; - $xmlrpcVal->timestamp = \PhpXmlRpc\Helper\Date::iso8601Decode($val); - - return $xmlrpcVal; + $xmlrpcVal = array( + 'xmlrpc_type' => 'datetime', + 'scalar' => $val, + 'timestamp' => \PhpXmlRpc\Helper\Date::iso8601Decode($val) + ); + return (object)$xmlrpcVal; case 'base64': - $xmlrpcVal->scalar = $val; - $xmlrpcVal->type = $typ; - - return $xmlrpcVal; + $xmlrpcVal = array( + 'xmlrpc_type' => 'base64', + 'scalar' => $val + ); + return (object)$xmlrpcVal; default: return $xmlrpcVal->scalarval(); } @@ -135,6 +137,7 @@ class Encoder $type = gettype($phpVal); switch ($type) { case 'string': + /// @todo should we be stricter in the accepted dates (ie. reject more of invalid days & times)? if (in_array('auto_dates', $options) && preg_match('/^[0-9]{8}T[0-9]{2}:[0-9]{2}:[0-9]{2}$/', $phpVal)) { $xmlrpcVal = new Value($phpVal, Value::$xmlrpcDateTime); } else { @@ -178,6 +181,23 @@ class Encoder $xmlrpcVal = $phpVal; } elseif (is_a($phpVal, 'DateTimeInterface')) { $xmlrpcVal = new Value($phpVal->format('Ymd\TH:i:s'), Value::$xmlrpcStruct); + } elseif (in_array('extension_api', $options) && $phpVal instanceof \stdClass && isset($phpVal->xmlrpc_type)) { + // Handle the 'pre-converted' base64 and datetime values + if (isset($phpVal->scalar)) { + switch ($phpVal->xmlrpc_type) { + case 'base64': + $xmlrpcVal = new Value($phpVal->scalar, Value::$xmlrpcBase64); + break; + case 'datetime': + $xmlrpcVal = new Value($phpVal->scalar, Value::$xmlrpcDateTime); + break; + default: + $xmlrpcVal = new Value(); + } + } else { + $xmlrpcVal = new Value(); + } + } else { $arr = array(); foreach($phpVal as $k => $v) { @@ -264,7 +284,7 @@ class Encoder } $xmlRpcParser = new XMLParser($options); - $xmlRpcParser->parse($xmlVal, XMLParser::RETURN_XMLRPCVALS, XMLParser::ACCEPT_REQUEST | XMLParser::ACCEPT_RESPONSE | XMLParser::ACCEPT_VALUE); + $xmlRpcParser->parse($xmlVal, XMLParser::RETURN_XMLRPCVALS, XMLParser::ACCEPT_REQUEST | XMLParser::ACCEPT_RESPONSE | XMLParser::ACCEPT_VALUE | XMLParser::ACCEPT_FAULT); if ($xmlRpcParser->_xh['isf'] > 1) { // test that $xmlrpc->_xh['value'] is an obj, too??? @@ -297,6 +317,18 @@ class Encoder return $req; case 'value': return $xmlRpcParser->_xh['value']; + case 'fault': + // EPI api emulation + $v = $xmlRpcParser->_xh['value']; + // use a known error code + /** @var Value $vc */ + $vc = isset($v['faultCode']) ? $v['faultCode']->scalarval() : PhpXmlRpc::$xmlrpcerr['invalid_return']; + /** @var Value $vs */ + $vs = isset($v['faultString']) ? $v['faultString']->scalarval() : ''; + if (!is_int($vc) || $vc == 0) { + $vc = PhpXmlRpc::$xmlrpcerr['invalid_return']; + } + return new Response(0, $vc, $vs); default: return false; } diff --git a/src/Helper/XMLParser.php b/src/Helper/XMLParser.php index 2b619a1..9e7efdd 100644 --- a/src/Helper/XMLParser.php +++ b/src/Helper/XMLParser.php @@ -16,6 +16,7 @@ class XMLParser const ACCEPT_REQUEST = 1; const ACCEPT_RESPONSE = 2; const ACCEPT_VALUE = 4; + const ACCEPT_FAULT = 8; // Used to store state during parsing. // Quick explanation of components: @@ -30,7 +31,7 @@ class XMLParser // method - used to store method name // params - used to store parameters in method calls // pt - used to store the type of each received parameter. Useful if parameters are automatically decoded to php values - // rt - 'methodcall', 'methodresponse' or 'value' + // rt - 'methodcall', 'methodresponse', 'value' or 'fault' (the last one used only in EPI emulation mode) public $_xh = array( 'ac' => '', 'stack' => array(), @@ -175,7 +176,8 @@ class XMLParser } if (($name == 'METHODCALL' && ($accept & self::ACCEPT_REQUEST)) || ($name == 'METHODRESPONSE' && ($accept & self::ACCEPT_RESPONSE)) || - ($name == 'VALUE' && ($accept & self::ACCEPT_VALUE))) { + ($name == 'VALUE' && ($accept & self::ACCEPT_VALUE)) || + ($name == 'FAULT' && ($accept & self::ACCEPT_FAULT))) { $this->_xh['rt'] = strtolower($name); } else { $this->_xh['isf'] = 2; diff --git a/src/PhpXmlRpc.php b/src/PhpXmlRpc.php index 63f1226..28b1dee 100644 --- a/src/PhpXmlRpc.php +++ b/src/PhpXmlRpc.php @@ -78,7 +78,7 @@ class PhpXmlRpc public static $xmlrpc_internalencoding = "UTF-8"; public static $xmlrpcName = "XML-RPC for PHP"; - public static $xmlrpcVersion = "4.4.3"; + public static $xmlrpcVersion = "4.5-dev"; // let user errors start at 800 public static $xmlrpcerruser = 800; @@ -93,6 +93,9 @@ class PhpXmlRpc public static $xmlrpc_null_apache_encoding_ns = "http://ws.apache.org/xmlrpc/namespaces/extensions"; + // number of decimal digits used to serialize Double values + public static $xmlpc_double_precision = 128; + /** * A function to be used for compatibility with legacy code: it creates all global variables which used to be declared, * such as library version etc... diff --git a/src/Value.php b/src/Value.php index fa402a5..2627e58 100644 --- a/src/Value.php +++ b/src/Value.php @@ -261,7 +261,7 @@ class Value implements \Countable, \IteratorAggregate, \ArrayAccess // sprintf('%F') could be most likely ok but it fails eg. on 2e-14. // The code below tries its best at keeping max precision while avoiding exp notation, // but there is of course no limit in the number of decimal places to be used... - $rs .= "<${typ}>" . preg_replace('/\\.?0+$/', '', number_format((double)$val, 128, '.', '')) . ""; + $rs .= "<${typ}>" . preg_replace('/\\.?0+$/', '', number_format((double)$val, PhpXmlRpc::$xmlpc_double_precision, '.', '')) . ""; break; case static::$xmlrpcDateTime: if (is_string($val)) { @@ -499,7 +499,8 @@ class Value implements \Countable, \IteratorAggregate, \ArrayAccess * * @return \ArrayIterator */ - public function getIterator() { + public function getIterator() + { switch ($this->mytype) { case 3: return new \ArrayIterator($this->me['struct']); @@ -512,8 +513,8 @@ class Value implements \Countable, \IteratorAggregate, \ArrayAccess } } - public function offsetSet($offset, $value) { - + public function offsetSet($offset, $value) + { switch ($this->mytype) { case 3: if (!($value instanceof \PhpXmlRpc\Value)) { @@ -552,7 +553,8 @@ class Value implements \Countable, \IteratorAggregate, \ArrayAccess } } - public function offsetExists($offset) { + public function offsetExists($offset) + { switch ($this->mytype) { case 3: return isset($this->me['struct'][$offset]); @@ -566,7 +568,8 @@ class Value implements \Countable, \IteratorAggregate, \ArrayAccess } } - public function offsetUnset($offset) { + public function offsetUnset($offset) + { switch ($this->mytype) { case 3: unset($this->me['struct'][$offset]); @@ -582,7 +585,8 @@ class Value implements \Countable, \IteratorAggregate, \ArrayAccess } } - public function offsetGet($offset) { + public function offsetGet($offset) + { switch ($this->mytype) { case 3: return isset($this->me['struct'][$offset]) ? $this->me['struct'][$offset] : null; -- 2.43.0