introduce structured exceptions; make addParam and Response::__construct stricter...
authorgggeek <giunta.gaetano@gmail.com>
Wed, 25 Jan 2023 16:01:47 +0000 (16:01 +0000)
committergggeek <giunta.gaetano@gmail.com>
Wed, 25 Jan 2023 16:01:47 +0000 (16:01 +0000)
23 files changed:
NEWS.md
doc/manual/phpxmlrpc_manual.adoc
lib/xmlrpc.inc
src/Exception.php [new file with mode: 0644]
src/Exception/FaultResponseException.php [new file with mode: 0644]
src/Exception/HttpException.php
src/Exception/NoSuchMethodException.php [new file with mode: 0644]
src/Exception/ParsingException.php [new file with mode: 0644]
src/Exception/PhpXmlrpcException.php
src/Exception/ServerException.php [new file with mode: 0644]
src/Exception/StateErrorException.php [new file with mode: 0644]
src/Exception/TransportException.php [new file with mode: 0644]
src/Exception/TypeErrorException.php [new file with mode: 0644]
src/Exception/ValueErrorException.php [new file with mode: 0644]
src/Exception/XmlException.php [new file with mode: 0644]
src/Exception/XmlRpcException.php [new file with mode: 0644]
src/Helper/Charset.php
src/Helper/XMLParser.php
src/Request.php
src/Response.php
src/Server.php
src/Value.php
src/Wrapper.php

diff --git a/NEWS.md b/NEWS.md
index 7350994..5923099 100644 (file)
--- a/NEWS.md
+++ b/NEWS.md
@@ -25,6 +25,9 @@
   NB: if the received strings are not parseable as dates, NULL will be returned instead of an object (but that can
   be avoided by setting `PhpXmlRpc\PhpXmlRpc::$xmlrpc_reject_invalid_values = true`, see below).
 
+* improved: be more strict in the `Response` constructor and in `Request::addParam`: both of those will now generate
+  an error message in the log if passed unexpected values
+
 * improved: be more strict in the data accepted as valid for dateTime xml-rpc values. Clearly invalid dates such as a
   month '13', day '32' or hour '25' will cause an error message to be logged or the value to be rejected, depending
   on configuration
@@ -89,6 +92,8 @@
 
 * improved: made sure all debug output goes through the logger at response parsing time (there was one printf call left)
 
+* improved: all the Exceptions thrown by the library are now `\PhpXmlRpc\Exception` or subclasses thereof
+
 * improved: all the Client's `setSomething()` methods now return the client object, allowing for usage of fluent style
   calling. The same applies to `Request::setDebug`
 
     if you subclassed id)
   - traits have been introduced for all classes dealing with Logger, XMLParser and CharsetEncoder; method `setCharsetEncoder`
     is now static
+  - exception `\PhpXmlRpc\Exception\PhpXmlRpcException` is deprecated. Use `\PhpXmlRpc\Exception` instead
 
 ## XML-RPC for PHP version 4.9.5 - 2023/01/11
 
index 1e6ed30..540a34e 100644 (file)
@@ -1880,3 +1880,21 @@ the demo files are also available for download as a separate tarball from the re
 
 *Note* when downloading the demo files, make sure that the demo folder is not directly accessible from the internet, i.e.
 it is not within the webserver root directory.
+
+== Exception hierarchy
+
+    Exception
+    `-PhpXmlRpc/Exception
+      |-PhpXmlRpc/Exception/FaultResponseException.php
+      |-PhpXmlRpc/Exception/ParsingException.php
+      | |-PhpXmlRpc/Exception/XmlException.php
+      | `-PhpXmlRpc/Exception/XmlRpcException.php
+      |-PhpXmlRpc/Exception/TransportException.php
+      | `-PhpXmlRpc/Exception/HttpException.php
+      |-PhpXmlRpc/Exception/ServerException.php
+      | `-PhpXmlRpc/Exception/NoSuchMethodException.php
+      |-PhpXmlRpc/Exception/StateErrorException.php
+      |-PhpXmlRpc/Exception/TypeErrorException.php
+      `-PhpXmlRpc/Exception/ValueErrorException.php
+
+*Note* not all of the above exceptions are in use at the moment, but they might be in the future
index b2e1ea9..5201320 100644 (file)
@@ -54,8 +54,18 @@ include_once(__DIR__.'/../src/PhpXmlRpc.php');
 include_once(__DIR__.'/../src/Request.php');
 include_once(__DIR__.'/../src/Response.php');
 include_once(__DIR__.'/../src/Value.php');
-include_once(__DIR__.'/../src/Exception/PhpXmlrpcException.php');
+include_once(__DIR__.'/../src/Exception.php');
+include_once(__DIR__.'/../src/Exception/FaultResponseException.php');
+include_once(__DIR__.'/../src/Exception/ParsingException.php');
+include_once(__DIR__.'/../src/Exception/XmlException.php');
+include_once(__DIR__.'/../src/Exception/XmlRpcException.php');
+include_once(__DIR__.'/../src/Exception/TransportException.php');
 include_once(__DIR__.'/../src/Exception/HttpException.php');
+include_once(__DIR__.'/../src/Exception/ServerException.php');
+include_once(__DIR__.'/../src/Exception/NoSuchMethodException.php');
+include_once(__DIR__.'/../src/Exception/StateErrorException.php');
+include_once(__DIR__.'/../src/Exception/TypeErrorException.php');
+include_once(__DIR__.'/../src/Exception/ValueErrorException.php');
 include_once(__DIR__.'/../src/Helper/Charset.php');
 include_once(__DIR__.'/../src/Helper/Date.php');
 include_once(__DIR__.'/../src/Helper/Http.php');
diff --git a/src/Exception.php b/src/Exception.php
new file mode 100644 (file)
index 0000000..d5d85b4
--- /dev/null
@@ -0,0 +1,7 @@
+<?php
+
+namespace PhpXmlRpc;
+
+class Exception extends \Exception
+{
+}
diff --git a/src/Exception/FaultResponseException.php b/src/Exception/FaultResponseException.php
new file mode 100644 (file)
index 0000000..fc59b3c
--- /dev/null
@@ -0,0 +1,12 @@
+<?php
+
+namespace PhpXmlRpc\Exception;
+
+use PhpXmlRpc\Exception as BaseExtension;
+
+/**
+ * To be used when throwing exceptions instead of returning Response objects (future API?)
+ */
+class FaultResponseException extends BaseExtension
+{
+}
index 5b9322e..8e06095 100644 (file)
@@ -2,7 +2,10 @@
 
 namespace PhpXmlRpc\Exception;
 
-class HttpException extends PhpXmlrpcException
+/**
+ * To be used for all errors related to parsing HTTP requests and responses
+ */
+class HttpException extends TransportException
 {
     protected $statusCode;
 
diff --git a/src/Exception/NoSuchMethodException.php b/src/Exception/NoSuchMethodException.php
new file mode 100644 (file)
index 0000000..079a4a4
--- /dev/null
@@ -0,0 +1,7 @@
+<?php
+
+namespace PhpXmlRpc\Exception;
+
+class NoSuchMethodException extends ServerException
+{
+}
diff --git a/src/Exception/ParsingException.php b/src/Exception/ParsingException.php
new file mode 100644 (file)
index 0000000..a7c2912
--- /dev/null
@@ -0,0 +1,12 @@
+<?php
+
+namespace PhpXmlRpc\Exception;
+
+use PhpXmlRpc\Exception as BaseExtension;
+
+/**
+ * Base exception for errors while parsing xml-rpc requests/responses: charset issue, xml issues, xml-rpc issues
+ */
+class ParsingException extends BaseExtension
+{
+}
index e143f4e..c51ed85 100644 (file)
@@ -1,7 +1,4 @@
 <?php
 
-namespace PhpXmlRpc\Exception;
-
-class PhpXmlrpcException extends \Exception
-{
-}
+// deprecated. Kept around for BC
+class_alias('PhpXmlRpc\Exception', 'PhpXmlRpc\Exception\PhpXmlRpcException');
diff --git a/src/Exception/ServerException.php b/src/Exception/ServerException.php
new file mode 100644 (file)
index 0000000..2fafaf8
--- /dev/null
@@ -0,0 +1,12 @@
+<?php
+
+namespace PhpXmlRpc\Exception;
+
+use PhpXmlRpc\Exception as BaseExtension;
+
+/**
+ * Base exception for errors thrown by the server while trying to handle the requests, such as errors with the dispatch map
+ */
+class ServerException extends BaseExtension
+{
+}
diff --git a/src/Exception/StateErrorException.php b/src/Exception/StateErrorException.php
new file mode 100644 (file)
index 0000000..9e3deef
--- /dev/null
@@ -0,0 +1,12 @@
+<?php
+
+namespace PhpXmlRpc\Exception;
+
+use PhpXmlRpc\Exception as BaseExtension;
+
+/**
+ * Exception thrown when an object is in such a state that it can not fulfill execution of a method
+ */
+class StateErrorException extends BaseExtension
+{
+}
diff --git a/src/Exception/TransportException.php b/src/Exception/TransportException.php
new file mode 100644 (file)
index 0000000..2fef021
--- /dev/null
@@ -0,0 +1,12 @@
+<?php
+
+namespace PhpXmlRpc\Exception;
+
+use PhpXmlRpc\Exception as BaseExtension;
+
+/**
+ * To be used for all errors related to the transport, which are not related to specifically to HTTP. Eg: can not open socket
+ */
+class TransportException extends BaseExtension
+{
+}
diff --git a/src/Exception/TypeErrorException.php b/src/Exception/TypeErrorException.php
new file mode 100644 (file)
index 0000000..77c1e10
--- /dev/null
@@ -0,0 +1,12 @@
+<?php
+
+namespace PhpXmlRpc\Exception;
+
+use PhpXmlRpc\Exception as BaseExtension;
+
+/**
+ * Exception thrown when an argument passed to a function or method has an unsupported type
+ */
+class TypeErrorException extends BaseExtension
+{
+}
diff --git a/src/Exception/ValueErrorException.php b/src/Exception/ValueErrorException.php
new file mode 100644 (file)
index 0000000..601c074
--- /dev/null
@@ -0,0 +1,12 @@
+<?php
+
+namespace PhpXmlRpc\Exception;
+
+use PhpXmlRpc\Exception as BaseExtension;
+
+/**
+ * Exception thrown when an argument passed to a function or method has an unsupported value (but its type is ok)
+ */
+class ValueErrorException extends BaseExtension
+{
+}
diff --git a/src/Exception/XmlException.php b/src/Exception/XmlException.php
new file mode 100644 (file)
index 0000000..2141599
--- /dev/null
@@ -0,0 +1,7 @@
+<?php
+
+namespace PhpXmlRpc\Exception;
+
+class XmlException extends ParsingException
+{
+}
diff --git a/src/Exception/XmlRpcException.php b/src/Exception/XmlRpcException.php
new file mode 100644 (file)
index 0000000..8037040
--- /dev/null
@@ -0,0 +1,7 @@
+<?php
+
+namespace PhpXmlRpc\Exception;
+
+class XmlRpcException extends ParsingException
+{
+}
index d25ee3e..5488561 100644 (file)
@@ -2,6 +2,7 @@
 
 namespace PhpXmlRpc\Helper;
 
+use PhpXmlRpc\Exception\ValueErrorException;
 use PhpXmlRpc\PhpXmlRpc;
 use PhpXmlRpc\Traits\LoggerAware;
 
@@ -55,7 +56,7 @@ class Charset
      * @param string $tableName
      * @return void
      *
-     * @throws \Exception for unsupported $tableName
+     * @throws ValueErrorException for unsupported $tableName
      *
      * @todo add support for cp1252 as well as latin-2 .. latin-10
      *       Optimization creep: instead of building all those tables on load, keep them ready-made php files
@@ -110,7 +111,7 @@ class Charset
                 break;*/
 
             default:
-                throw new \Exception('Unsupported table: ' . $tableName);
+                throw new ValueErrorException('Unsupported table: ' . $tableName);
         }
     }
 
@@ -372,7 +373,7 @@ class Charset
      *
      * @param string $charset
      * @return array
-     * @throws \Exception for unknown/unsupported charsets
+     * @throws ValueErrorException for unknown/unsupported charsets
      */
     public function getEntities($charset)
     {
@@ -383,7 +384,7 @@ class Charset
             case 'iso88591':
                 return $this->xml_iso88591_Entities;
             default:
-                throw new \Exception('Unsupported charset: ' . $charset);
+                throw new ValueErrorException('Unsupported charset: ' . $charset);
         }
     }
 }
index ddfeefd..45f2bce 100644 (file)
@@ -172,8 +172,6 @@ class XMLParser
             return;
         }
 
-        //$prevAccept = $this->accept;
-        //$this->accept = $accept;
         $this->current_parsing_options = array('accept' => $accept);
 
         $mergedOptions = $this->parsing_options;
@@ -272,17 +270,22 @@ class XMLParser
                     break;
                 }
             }
+        /// @todo bump minimum php version to 5.5 and use a finally clause instead of doing cleanup 3 times
         } catch (\Exception $e) {
             xml_parser_free($parser);
             $this->current_parsing_options = array();
-            //$this->accept = $prevAccept;
             /// @todo should we set $this->_xh['isf'] and $this->_xh['isf_reason'] ?
             throw $e;
+        } catch (\Error $e) {
+            xml_parser_free($parser);
+            $this->current_parsing_options = array();
+                //$this->accept = $prevAccept;
+                /// @todo should we set $this->_xh['isf'] and $this->_xh['isf_reason'] ?
+            throw $e;
         }
 
         xml_parser_free($parser);
         $this->current_parsing_options = array();
-        //$this->accept = $prevAccept;
     }
 
     /**
index 311c241..8a5fcaf 100644 (file)
@@ -141,6 +141,7 @@ class Request
 
             return true;
         } else {
+            $this->getLogger()->error('XML-RPC: ' . __METHOD__ . ': value passed in must be a PhpXmlRpc\Value');
             return false;
         }
     }
index 0ccb46a..b4ab39b 100644 (file)
@@ -2,7 +2,9 @@
 
 namespace PhpXmlRpc;
 
+use PhpXmlRpc\Exception\StateErrorException;
 use PhpXmlRpc\Traits\CharsetEncoderAware;
+use PhpXmlRpc\Traits\LoggerAware;
 
 /**
  * This class provides the representation of the response of an XML-RPC server.
@@ -16,6 +18,7 @@ use PhpXmlRpc\Traits\CharsetEncoderAware;
 class Response
 {
     use CharsetEncoderAware;
+    use LoggerAware;
 
     /// @todo: do these need to be public?
     /** @internal */
@@ -32,18 +35,20 @@ class Response
     protected $httpResponse = array('headers' => array(), 'cookies' => array(), 'raw_data' => '', 'status_code' => null);
 
     /**
-     * @param Value|string|mixed $val either a Value object, a php value or the xml serialization of an xml-rpc value (a string)
+     * @param Value|string|mixed $val either a Value object, a php value or the xml serialization of an xml-rpc value (a string).
+     *                                Note that using anything other than a Value object wll have an impact on serialization.
      * @param integer $fCode set it to anything but 0 to create an error response. In that case, $val is discarded
      * @param string $fString the error string, in case of an error response
      * @param string $valType The type of $val passed in. Either 'xmlrpcvals', 'phpvals' or 'xml'. Leave empty to let
-     *                        the code guess the correct type.
+     *                        the code guess the correct type by looking at $val - in which case strings are assumed
+     *                        to be serialized xml
      * @param array|null $httpResponse this should be set when the response is being built out of data received from
      *                                 http (i.e. not when programmatically building a Response server-side). Array
      *                                 keys should include, if known: headers, cookies, raw_data, status_code
      *
-     * @todo add check that $val / $fCode / $fString is of correct type???
+     * @todo add check that $val / $fCode / $fString is of correct type? We could at least log a warning for fishy cases...
      *       NB: as of now we do not do it, since it might be either an xml-rpc value or a plain php val, or a complete
-     *       xml chunk, depending on usage of Client::send() inside which the constructor is called...
+     *       xml chunk, depending on usage of Client::send() inside which the constructor is called.
      */
     public function __construct($val, $fCode = 0, $fString = '', $valType = '', $httpResponse = null)
     {
@@ -64,8 +69,12 @@ class Response
                     $this->valtyp = 'phpvals';
                 }
             } else {
-                // user declares type of resp value: believe him
                 $this->valtyp = $valType;
+                // user declares the type of resp value: we "almost" trust it... but log errors just in case
+                if (($this->valtyp == 'xmlrpcvals' && (!is_a($this->val, 'PhpXmlRpc\Value'))) ||
+                    ($this->valtyp == 'xml' && (!is_string($this->val)))) {
+                    $this->getLogger()->error('XML-RPC: ' . __METHOD__ . ': value passed in does not match type ' . $valType);
+                }
             }
         }
 
@@ -138,7 +147,7 @@ class Response
      *
      * @param string $charsetEncoding the charset to be used for serialization. If null, US-ASCII is assumed
      * @return string the xml representation of the response
-     * @throws \Exception
+     * @throws StateErrorException if the response was built out of a value of an unsupported type
      */
     public function serialize($charsetEncoding = '')
     {
@@ -157,22 +166,21 @@ class Response
             $result .= "<fault>\n" .
                 "<value>\n<struct><member><name>faultCode</name>\n<value><int>" . $this->errno .
                 "</int></value>\n</member>\n<member>\n<name>faultString</name>\n<value><string>" .
-                $this->getCharsetEncoder()->encodeEntities($this->errstr, PhpXmlRpc::$xmlrpc_internalencoding, $charsetEncoding) . "</string></value>\n</member>\n" .
-                "</struct>\n</value>\n</fault>";
+                $this->getCharsetEncoder()->encodeEntities($this->errstr, PhpXmlRpc::$xmlrpc_internalencoding, $charsetEncoding) .
+                "</string></value>\n</member>\n</struct>\n</value>\n</fault>";
         } else {
-            if (!is_object($this->val) || !is_a($this->val, 'PhpXmlRpc\Value')) {
-                if (is_string($this->val) && $this->valtyp == 'xml') {
-                    $result .= "<params>\n<param>\n" .
-                        $this->val .
-                        "</param>\n</params>";
-                } else {
-                    /// @todo try to build something serializable using the Encoder...
-                    throw new \Exception('cannot serialize xmlrpc response objects whose content is native php values');
-                }
-            } else {
+            if (is_object($this->val) && is_a($this->val, 'PhpXmlRpc\Value')) {
+                $result .= "<params>\n<param>\n" . $this->val->serialize($charsetEncoding) . "</param>\n</params>";
+            } else if (is_string($this->val) && $this->valtyp == 'xml') {
                 $result .= "<params>\n<param>\n" .
-                    $this->val->serialize($charsetEncoding) .
+                    $this->val .
                     "</param>\n</params>";
+            } else if ($this->valtyp == 'phpvals') {
+                    $encoder = new Encoder();
+                    $val = $encoder->encode($this->val);
+                    $result .= "<params>\n<param>\n" . $val->serialize($charsetEncoding) . "</param>\n</params>";
+            } else {
+                throw new StateErrorException('cannot serialize xmlrpc response objects whose content is native php values');
             }
         }
         $result .= "\n</methodResponse>";
index 5d7b2bb..fdec46c 100644 (file)
@@ -2,7 +2,7 @@
 
 namespace PhpXmlRpc;
 
-use PhpXmlRpc\Exception\PhpXmlrpcException;
+use PhpXmlRpc\Exception\NoSuchMethodException;
 use PhpXmlRpc\Helper\Http;
 use PhpXmlRpc\Helper\Interop;
 use PhpXmlRpc\Helper\Logger;
@@ -567,7 +567,7 @@ class Server
         $xmlRpcParser = $this->getParser();
         try {
             $xmlRpcParser->parse($data, $this->functions_parameters_type, XMLParser::ACCEPT_REQUEST, $options);
-        } catch (PhpXmlrpcException $e) {
+        } catch (NoSuchMethodException $e) {
             return new Response(0, $e->getCode(), $e->getMessage());
         }
 
@@ -751,6 +751,7 @@ class Server
                     $r = new Response($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
         } catch (\Exception $e) {
             // (barring errors in the lib) an uncaught exception happened in the called function, we wrap it in a
             // proper error-response
@@ -822,7 +823,7 @@ class Server
      * @param XMLParser $xmlParser
      * @param resource $parser
      * @return void
-     * @throws PhpXmlrpcException
+     * @throws NoSuchMethodException
      *
      * @todo feature creep - we could validate here that the method in the dispatch map is valid, but that would mean
      *       dirtying a lot the logic, as we would have back to both parseRequest() and execute() methods the info
@@ -835,7 +836,7 @@ class Server
 
         if (!isset($dmap[$methodName]['function'])) {
             // No such method
-            throw new PhpXmlrpcException(PhpXmlRpc::$xmlrpcstr['unknown_method'], PhpXmlRpc::$xmlrpcerr['unknown_method']);
+            throw new NoSuchMethodException(PhpXmlRpc::$xmlrpcstr['unknown_method'], PhpXmlRpc::$xmlrpcerr['unknown_method']);
         }
 
         // alter on-the-fly the config of the xml parser if needed
index a12c98f..276aea0 100644 (file)
@@ -2,6 +2,9 @@
 
 namespace PhpXmlRpc;
 
+use PhpXmlRpc\Exception\StateErrorException;
+use PhpXmlRpc\Exception\TypeErrorException;
+use PhpXmlRpc\Exception\ValueErrorException;
 use PhpXmlRpc\Traits\CharsetEncoderAware;
 use PhpXmlRpc\Traits\LoggerAware;
 
@@ -533,26 +536,26 @@ class Value implements \Countable, \IteratorAggregate, \ArrayAccess
      * @param mixed $value
      * @return void
      *
-     * @throws \Exception
+     * @throws ValueErrorException|TypeErrorException
      */
     #[\ReturnTypeWillChange]
     public function offsetSet($offset, $value)
     {
         switch ($this->mytype) {
             case 3:
-                if (!($value instanceof \PhpXmlRpc\Value)) {
-                    throw new \Exception('It is only possible to add Value objects to an XML-RPC Struct');
+                if (!($value instanceof Value)) {
+                    throw new TypeErrorException('It is only possible to add Value objects to an XML-RPC Struct');
                 }
                 if (is_null($offset)) {
                     // disallow struct members with empty names
-                    throw new \Exception('It is not possible to add anonymous members to an XML-RPC Struct');
+                    throw new ValueErrorException('It is not possible to add anonymous members to an XML-RPC Struct');
                 } else {
                     $this->me['struct'][$offset] = $value;
                 }
                 return;
             case 2:
-                if (!($value instanceof \PhpXmlRpc\Value)) {
-                    throw new \Exception('It is only possible to add Value objects to an XML-RPC Array');
+                if (!($value instanceof Value)) {
+                    throw new TypeErrorException('It is only possible to add Value objects to an XML-RPC Array');
                 }
                 if (is_null($offset)) {
                     $this->me['array'][] = $value;
@@ -566,13 +569,13 @@ class Value implements \Countable, \IteratorAggregate, \ArrayAccess
                 reset($this->me);
                 $type = key($this->me);
                 if ($type != $offset) {
-                    throw new \Exception('');
+                    throw new ValueErrorException('');
                 }
                 $this->me[$type] = $value;
                 return;
             default:
                 // it would be nice to allow empty values to be turned into non-empty ones this way, but we miss info to do so
-                throw new \Exception("XML-RPC Value is of type 'undef' and its value can not be set using array index");
+                throw new ValueErrorException("XML-RPC Value is of type 'undef' and its value can not be set using array index");
         }
     }
 
@@ -604,7 +607,7 @@ class Value implements \Countable, \IteratorAggregate, \ArrayAccess
      * @param mixed $offset
      * @return void
      *
-     * @throws \Exception
+     * @throws ValueErrorException|StateErrorException
      */
     #[\ReturnTypeWillChange]
     public function offsetUnset($offset)
@@ -618,9 +621,9 @@ class Value implements \Countable, \IteratorAggregate, \ArrayAccess
                 return;
             case 1:
                 // can not remove value from a scalar
-                throw new \Exception("XML-RPC Value is of type 'scalar' and its value can not be unset using array index");
+                throw new StateErrorException("XML-RPC Value is of type 'scalar' and its value can not be unset using array index");
             default:
-                throw new \Exception("XML-RPC Value is of type 'undef' and its value can not be unset using array index");
+                throw new StateErrorException("XML-RPC Value is of type 'undef' and its value can not be unset using array index");
         }
     }
 
@@ -629,7 +632,7 @@ class Value implements \Countable, \IteratorAggregate, \ArrayAccess
      *
      * @param mixed $offset
      * @return mixed|Value|null
-     * @throws \Exception
+     * @throws StateErrorException
      */
     #[\ReturnTypeWillChange]
     public function offsetGet($offset)
@@ -646,7 +649,7 @@ class Value implements \Countable, \IteratorAggregate, \ArrayAccess
                 return $type == $offset ? $value : null;
             default:
 // return null or exception?
-                throw new \Exception("XML-RPC Value is of type 'undef' and can not be accessed using array index");
+                throw new StateErrorException("XML-RPC Value is of type 'undef' and can not be accessed using array index");
         }
     }
 }
index bf613c5..04a0a83 100644 (file)
@@ -7,6 +7,7 @@
 
 namespace PhpXmlRpc;
 
+use PhpXmlRpc\Exception\ValueErrorException;
 use PhpXmlRpc\Traits\LoggerAware;
 
 /**
@@ -908,7 +909,7 @@ class Wrapper
                     if (is_string($throwFault)) {
                         throw new $throwFault($resp->faultString(), $resp->faultCode());
                     } else {
-                        throw new \Exception($resp->faultString(), $resp->faultCode());
+                        throw new \PhpXmlRpc\Exception($resp->faultString(), $resp->faultCode());
                     }
                 } else if ($decodeFault) {
                     if (is_string($faultResponse) && ((strpos($faultResponse, '%faultCode%') !== false) ||
@@ -1017,7 +1018,7 @@ class Wrapper
         $plist = implode(', ', $plist);
         $mDesc .= ' * @return ' . $this->xmlrpc2PhpType($mSig[0]);
         if ($throwFault) {
-            $mDesc .= "\n * @throws " . (is_string($throwFault) ? $throwFault : '\\Exception');
+            $mDesc .= "\n * @throws " . (is_string($throwFault) ? $throwFault : '\\PhpXmlRpc\\Exception');
         } else if ($decodeFault) {
             $mDesc .= '|' . gettype($faultResponse) . " (a " . gettype($faultResponse) . " if call fails)";
         } else {
@@ -1028,7 +1029,7 @@ class Wrapper
         $innerCode .= "  \$res = \${$this_}client->send(\$req, $timeout, '$protocol');\n";
         if ($throwFault) {
             if (!is_string($throwFault)) {
-                $throwFault = '\\Exception';
+                $throwFault = '\\PhpXmlRpc\\Exception';
             }
             $respCode = "throw new $throwFault(\$res->faultString(), \$res->faultCode())";
         } else if ($decodeFault) {
@@ -1219,7 +1220,7 @@ class Wrapper
     /**
      * @param string $index
      * @return object
-     * @throws \Exception
+     * @throws ValueErrorException
      */
     public static function getHeldObject($index)
     {
@@ -1227,6 +1228,6 @@ class Wrapper
             return self::$objHolder[$index];
         }
 
-        throw new \Exception("No object held for index '$index'");
+        throw new ValueErrorException("No object held for index '$index'");
     }
 }