adopt CI-like patterns
authorgggeek <giunta.gaetano@gmail.com>
Wed, 13 Jan 2021 00:15:14 +0000 (00:15 +0000)
committergggeek <giunta.gaetano@gmail.com>
Wed, 13 Jan 2021 00:15:14 +0000 (00:15 +0000)
NEWS
src/Client.php
src/Encoder.php
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 b/NEWS
index 3ddc9ee..f10f1ef 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -3,6 +3,19 @@ XML-RPC for PHP version 4.6.0 - unreleased
 * improved: when encoding utf8 text into us-ascii xml, use character entity references for characters number 0-31
   (ascii non printable characters), as we were already doing when encoding iso-8859-1 text into us-ascii xml
 
+* new: method `Server::getDispatchMap()`. Useful for non-child classes which want to f.e. introspect the server
+
+* new: increase flexibility in class composition by adopting a Dependency Injection (...ish) pattern:
+  it is now possible to swap out the Logger, XMLParser and Charset classes with similar ones of your own making.
+  Example code:
+      // 1. create an instance of a custom character encoder
+      // $myCharsetEncoder = ...
+      // 2. then use it while serializing a Request:
+      Request::setCharsetEncoder($myCharsetEncoder);
+      $request->serialize($funkyCharset);
+
+* new: method `XMLParser::parse()` acquired a 4th argument
+
 
 XML-RPC for PHP version 4.5.2 - 2021/1/11
 
index fe4eb5d..4e03d3e 100644 (file)
@@ -13,6 +13,8 @@ class Client
     const USE_CURL_ALWAYS = 1;
     const USE_CURL_AUTO = 2;
 
+    protected static $logger;
+
     /// @todo: do these need to be public?
     public $method = 'http';
     public $server;
@@ -88,10 +90,11 @@ class Client
 
     /**
      * The charset encoding that will be used for serializing request sent by the client.
-     * It defaults to NULL, which means using US-ASCII and encoding all characters outside of the ASCII range using
-     * their xml character entity representation (this has the benefit that line end characters will not be mangled in
-     * the transfer, a CR-LF will be preserved as well as a singe LF).
-     * Valid values are 'US-ASCII', 'UTF-8' and 'ISO-8859-1'
+     * It defaults to NULL, which means using US-ASCII and encoding all characters outside of the ASCII printable range
+     * using their xml character entity representation (this has the benefit that line end characters will not be mangled
+     * in the transfer, a CR-LF will be preserved as well as a singe LF).
+     * Valid values are 'US-ASCII', 'UTF-8' and 'ISO-8859-1'.
+     * For the fastest mode of operation, set your both your app internal encoding as well as this to UTF-8.
      */
     public $request_charset_encoding = '';
 
@@ -115,6 +118,19 @@ class Client
      */
     public $user_agent;
 
+    public function getLogger()
+    {
+        if (self::$logger === null) {
+            self::$logger = Logger::instance();
+        }
+        return self::$logger;
+    }
+
+    public static function setLogger($logger)
+    {
+        self::$logger = $logger;
+    }
+
     /**
      * @param string $path either the PATH part of the xmlrpc server URL, or complete server URL (in which case you
      *                     should use and empty string for all other parameters)
@@ -670,7 +686,7 @@ class Client
         if ($username != '') {
             $credentials = 'Authorization: Basic ' . base64_encode($username . ':' . $password) . "\r\n";
             if ($authType != 1) {
-                Logger::instance()->errorLog('XML-RPC: ' . __METHOD__ . ': warning. Only Basic auth is supported with HTTP 1.0');
+                $this->getLogger()->errorLog('XML-RPC: ' . __METHOD__ . ': warning. Only Basic auth is supported with HTTP 1.0');
             }
         }
 
@@ -690,7 +706,7 @@ class Client
             $uri = 'http://' . $server . ':' . $port . $this->path;
             if ($proxyUsername != '') {
                 if ($proxyAuthType != 1) {
-                    Logger::instance()->errorLog('XML-RPC: ' . __METHOD__ . ': warning. Only Basic auth to proxy is supported with HTTP 1.0');
+                    $this->getLogger()->errorLog('XML-RPC: ' . __METHOD__ . ': warning. Only Basic auth to proxy is supported with HTTP 1.0');
                 }
                 $proxyCredentials = 'Proxy-Authorization: Basic ' . base64_encode($proxyUsername . ':' . $proxyPassword) . "\r\n";
             }
@@ -746,7 +762,7 @@ class Client
             $payload;
 
         if ($this->debug > 1) {
-            Logger::instance()->debugMessage("---SENDING---\n$op\n---END---");
+            $this->getLogger()->debugMessage("---SENDING---\n$op\n---END---");
         }
 
         $contextOptions = array();
@@ -900,7 +916,7 @@ class Client
         }
 
         if ($this->debug > 1) {
-            Logger::instance()->debugMessage("---SENDING---\n$payload\n---END---");
+            $this->getLogger()->debugMessage("---SENDING---\n$payload\n---END---");
         }
 
         if (!$keepAlive || !$this->xmlrpc_curl_handle) {
@@ -976,7 +992,7 @@ class Client
             if (defined('CURLOPT_HTTPAUTH')) {
                 curl_setopt($curl, CURLOPT_HTTPAUTH, $authType);
             } elseif ($authType != 1) {
-                Logger::instance()->errorLog('XML-RPC: ' . __METHOD__ . ': warning. Only Basic auth is supported by the current PHP/curl install');
+                $this->getLogger()->errorLog('XML-RPC: ' . __METHOD__ . ': warning. Only Basic auth is supported by the current PHP/curl install');
             }
         }
 
@@ -1024,7 +1040,7 @@ class Client
                 if (defined('CURLOPT_PROXYAUTH')) {
                     curl_setopt($curl, CURLOPT_PROXYAUTH, $proxyAuthType);
                 } elseif ($proxyAuthType != 1) {
-                    Logger::instance()->errorLog('XML-RPC: ' . __METHOD__ . ': warning. Only Basic auth to proxy is supported by the current PHP/curl install');
+                    $this->getLogger()->errorLog('XML-RPC: ' . __METHOD__ . ': warning. Only Basic auth to proxy is supported by the current PHP/curl install');
                 }
             }
         }
@@ -1054,7 +1070,7 @@ class Client
                 $message .= $name . ': ' . $val . "\n";
             }
             $message .= '---END---';
-            Logger::instance()->debugMessage($message);
+            $this->getLogger()->debugMessage($message);
         }
 
         if (!$result) {
index ecb64bd..7ad2adb 100644 (file)
@@ -12,6 +12,35 @@ use PhpXmlRpc\Helper\XMLParser;
  */
 class Encoder
 {
+    protected static $logger;
+    protected static $parser;
+
+    public function getLogger()
+    {
+        if (self::$logger === null) {
+            self::$logger = Logger::instance();
+        }
+        return self::$logger;
+    }
+
+    public static function setLogger($logger)
+    {
+        self::$logger = $logger;
+    }
+
+    public function getParser()
+    {
+        if (self::$parser === null) {
+            self::$parser = new XMLParser();
+        }
+        return self::$parser;
+    }
+
+    public static function setParser($parser)
+    {
+        self::$parser = $parser;
+    }
+
     /**
      * Takes an xmlrpc value in object format and translates it into native PHP types.
      *
@@ -280,7 +309,7 @@ class Encoder
                     if (extension_loaded('mbstring')) {
                         $xmlVal = mb_convert_encoding($xmlVal, 'UTF-8', $valEncoding);
                     } else {
-                        Logger::instance()->errorLog('XML-RPC: ' . __METHOD__ . ': invalid charset encoding of xml text: ' . $valEncoding);
+                        $this->getLogger()->errorLog('XML-RPC: ' . __METHOD__ . ': invalid charset encoding of xml text: ' . $valEncoding);
                     }
                 }
             }
@@ -294,13 +323,18 @@ class Encoder
             $parserOptions = array(XML_OPTION_TARGET_ENCODING => PhpXmlRpc::$xmlrpc_internalencoding);
         }
 
-        $xmlRpcParser = new XMLParser($parserOptions);
-        $xmlRpcParser->parse($xmlVal, XMLParser::RETURN_XMLRPCVALS, XMLParser::ACCEPT_REQUEST | XMLParser::ACCEPT_RESPONSE | XMLParser::ACCEPT_VALUE | XMLParser::ACCEPT_FAULT);
+        $xmlRpcParser = $this->getParser();
+        $xmlRpcParser->parse(
+            $xmlVal,
+            XMLParser::RETURN_XMLRPCVALS,
+            XMLParser::ACCEPT_REQUEST | XMLParser::ACCEPT_RESPONSE | XMLParser::ACCEPT_VALUE | XMLParser::ACCEPT_FAULT,
+            $parserOptions
+        );
 
         if ($xmlRpcParser->_xh['isf'] > 1) {
             // test that $xmlrpc->_xh['value'] is an obj, too???
 
-            Logger::instance()->errorLog($xmlRpcParser->_xh['isf_reason']);
+            $this->getLogger()->errorLog($xmlRpcParser->_xh['isf_reason']);
 
             return false;
         }
index 78bc813..5fa5422 100644 (file)
@@ -51,6 +51,8 @@ class Charset
      * @param string $tableName
      * @throws \Exception 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
+     *       which are not even included until needed
      * @todo should we add to the latin-1 table the characters from cp_1252 range, i.e. 128 to 159 ?
      *       Those will NOT be present in true ISO-8859-1, but will save the unwary windows user from sending junk
      *       (though no luck when receiving them...)
@@ -125,6 +127,7 @@ class Charset
      *       but then take those into account as well in other methods, ie.isValidCharset)
      * @todo when converting to ASCII, allow to choose whether to escape the range 0-31,127 (non-print chars) or not
      * @todo allow picking different strategies to deal w. invalid chars? eg. source in latin-1 and chars 128-159
+     * @todo add support for escaping using CDATA sections? (add cdata start and end tokens, replace only ']]>' with ']]]]><![CDATA[>')
      *
      * @param string $data
      * @param string $srcEncoding
@@ -176,6 +179,7 @@ class Charset
                     }
                     else if ($ii < 128) {
                         /// @todo shall we replace this with a (supposedly) faster str_replace?
+                        /// @todo to be 'print safe', should we encode as well character 127 (DEL) ?
                         switch ($ii) {
                             case 34:
                                 $escapedData .= '&quot;';
index 59eec67..f6e79a9 100644 (file)
@@ -97,8 +97,9 @@ class XMLParser
      * @param string $data
      * @param string $returnType
      * @param int $accept a bit-combination of self::ACCEPT_REQUEST, self::ACCEPT_RESPONSE, self::ACCEPT_VALUE
+     * @param array $options
      */
-    public function parse($data, $returnType = self::RETURN_XMLRPCVALS, $accept = 3)
+    public function parse($data, $returnType = self::RETURN_XMLRPCVALS, $accept = 3, $options = array())
     {
         $this->_xh = array(
             'ac' => '',
@@ -127,6 +128,9 @@ class XMLParser
         foreach ($this->parsing_options as $key => $val) {
             xml_parser_set_option($parser, $key, $val);
         }
+        foreach ($options as $key => $val) {
+            xml_parser_set_option($parser, $key, $val);
+        }
         // always set this, in case someone tries to disable it via options...
         xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 1);
 
index 66cc788..01fc9d6 100644 (file)
@@ -13,6 +13,10 @@ use PhpXmlRpc\Helper\XMLParser;
  */
 class Request
 {
+    protected static $logger;
+    protected static $parser;
+    protected static $charsetEncoder;
+
     /// @todo: do these need to be public?
     public $payload;
     /** @internal */
@@ -25,6 +29,45 @@ class Request
     // holds data while parsing the response. NB: Not a full Response object
     protected $httpResponse = array();
 
+    public function getLogger()
+    {
+        if (self::$logger === null) {
+            self::$logger = Logger::instance();
+        }
+        return self::$logger;
+    }
+
+    public static function setLogger($logger)
+    {
+        self::$logger = $logger;
+    }
+
+    public function getParser()
+    {
+        if (self::$parser === null) {
+            self::$parser = new XMLParser();
+        }
+        return self::$parser;
+    }
+
+    public static function setParser($parser)
+    {
+        self::$parser = $parser;
+    }
+
+    public function getCharsetEncoder()
+    {
+        if (self::$charsetEncoder === null) {
+            self::$charsetEncoder = Charset::instance();
+        }
+        return self::$charsetEncoder;
+    }
+
+    public function setCharsetEncoder($charsetEncoder)
+    {
+        self::$charsetEncoder = $charsetEncoder;
+    }
+
     /**
      * @param string $methodName the name of the method to invoke
      * @param Value[] $params array of parameters to be passed to the method (NB: Value objects, not plain php values)
@@ -72,7 +115,7 @@ class Request
             $this->content_type = 'text/xml';
         }
         $this->payload = $this->xml_header($charsetEncoding);
-        $this->payload .= '<methodName>' . Charset::instance()->encodeEntities(
+        $this->payload .= '<methodName>' . $this->getCharsetEncoder()->encodeEntities(
             $this->methodname, PhpXmlRpc::$xmlrpc_internalencoding, $charsetEncoding) . "</methodName>\n";
         $this->payload .= "<params>\n";
         foreach ($this->params as $p) {
@@ -197,13 +240,13 @@ class Request
     public function parseResponse($data = '', $headersProcessed = false, $returnType = 'xmlrpcvals')
     {
         if ($this->debug) {
-            Logger::instance()->debugMessage("---GOT---\n$data\n---END---");
+            $this->getLogger()->debugMessage("---GOT---\n$data\n---END---");
         }
 
         $this->httpResponse = array('raw_data' => $data, 'headers' => array(), 'cookies' => array());
 
         if ($data == '') {
-            Logger::instance()->errorLog('XML-RPC: ' . __METHOD__ . ': no response received from server.');
+            $this->getLogger()->errorLog('XML-RPC: ' . __METHOD__ . ': no response received from server.');
             return new Response(0, PhpXmlRpc::$xmlrpcerr['no_data'], PhpXmlRpc::$xmlrpcstr['no_data']);
         }
 
@@ -243,7 +286,7 @@ class Request
                 $start += strlen('<!-- SERVER DEBUG INFO (BASE64 ENCODED):');
                 $end = strpos($data, '-->', $start);
                 $comments = substr($data, $start, $end - $start);
-                Logger::instance()->debugMessage("---SERVER DEBUG INFO (DECODED) ---\n\t" .
+                $this->getLogger()->debugMessage("---SERVER DEBUG INFO (DECODED) ---\n\t" .
                     str_replace("\n", "\n\t", base64_decode($comments)) . "\n---END---", $respEncoding);
             }
         }
@@ -272,7 +315,7 @@ class Request
                     if (extension_loaded('mbstring')) {
                         $data = mb_convert_encoding($data, 'UTF-8', $respEncoding);
                     } else {
-                        Logger::instance()->errorLog('XML-RPC: ' . __METHOD__ . ': invalid charset encoding of received response: ' . $respEncoding);
+                        $this->getLogger()->errorLog('XML-RPC: ' . __METHOD__ . ': invalid charset encoding of received response: ' . $respEncoding);
                     }
                 }
             }
@@ -289,8 +332,8 @@ class Request
             $options = array(XML_OPTION_TARGET_ENCODING => PhpXmlRpc::$xmlrpc_internalencoding);
         }
 
-        $xmlRpcParser = new XMLParser($options);
-        $xmlRpcParser->parse($data, $returnType, XMLParser::ACCEPT_RESPONSE);
+        $xmlRpcParser = $this->getParser();
+        $xmlRpcParser->parse($data, $returnType, XMLParser::ACCEPT_RESPONSE, $options);
 
         // first error check: xml not well formed
         if ($xmlRpcParser->_xh['isf'] > 2) {
@@ -323,7 +366,7 @@ class Request
                 PhpXmlRpc::$xmlrpcstr['invalid_return']);
         } else {
             if ($this->debug > 1) {
-                Logger::instance()->debugMessage(
+                $this->getLogger()->debugMessage(
                     "---PARSED---\n".var_export($xmlRpcParser->_xh['value'], true)."\n---END---"
                 );
             }
index cc33ebf..e634ce5 100644 (file)
@@ -11,6 +11,8 @@ use PhpXmlRpc\Helper\Charset;
  */
 class Response
 {
+    protected static $charsetEncoder;
+
     /// @todo: do these need to be public?
     /** @internal */
     public $val = 0;
@@ -26,6 +28,19 @@ class Response
     public $_cookies = array();
     public $raw_data = '';
 
+    public function getCharsetEncoder()
+    {
+        if (self::$charsetEncoder === null) {
+            self::$charsetEncoder = Charset::instance();
+        }
+        return self::$charsetEncoder;
+    }
+
+    public function setCharsetEncoder($charsetEncoder)
+    {
+        self::$charsetEncoder = $charsetEncoder;
+    }
+
     /**
      * @param Value|string|mixed $val either a Value object, a php value or the xml serialization of an xmlrpc value (a string)
      * @param integer $fCode set it to anything but 0 to create an error response. In that case, $val is discarded
index 199a7a6..e859c7b 100644 (file)
@@ -11,6 +11,10 @@ use PhpXmlRpc\Helper\XMLParser;
  */
 class Server
 {
+    protected static $logger;
+    protected static $parser;
+    protected static $charsetEncoder;
+
     /**
      * Defines how functions in dmap will be invoked: either using an xmlrpc request object
      * or plain php values.
@@ -95,6 +99,45 @@ class Server
     protected static $_xmlrpcs_occurred_errors = '';
     protected static $_xmlrpcs_prev_ehandler = '';
 
+    public function getLogger()
+    {
+        if (self::$logger === null) {
+            self::$logger = Logger::instance();
+        }
+        return self::$logger;
+    }
+
+    public static function setLogger($logger)
+    {
+        self::$logger = $logger;
+    }
+
+    public function getParser()
+    {
+        if (self::$parser === null) {
+            self::$parser = new XMLParser();
+        }
+        return self::$parser;
+    }
+
+    public static function setParser($parser)
+    {
+        self::$parser = $parser;
+    }
+
+    public function getCharsetEncoder()
+    {
+        if (self::$charsetEncoder === null) {
+            self::$charsetEncoder = Charset::instance();
+        }
+        return self::$charsetEncoder;
+    }
+
+    public function setCharsetEncoder($charsetEncoder)
+    {
+        self::$charsetEncoder = $charsetEncoder;
+    }
+
     /**
      * @param array[] $dispatchMap the dispatch map with definition of exposed services
      *                             Array keys are the names of the method names.
@@ -194,7 +237,7 @@ class Server
             $out .= "<!-- SERVER DEBUG INFO (BASE64 ENCODED):\n" . base64_encode($this->debug_info) . "\n-->\n";
         }
         if (static::$_xmlrpc_debuginfo != '') {
-            $out .= "<!-- DEBUG INFO:\n" . Charset::instance()->encodeEntities(str_replace('--', '_-', static::$_xmlrpc_debuginfo), PhpXmlRpc::$xmlrpc_internalencoding, $charsetEncoding) . "\n-->\n";
+            $out .= "<!-- DEBUG INFO:\n" . $this->getCharsetEncoder()->encodeEntities(str_replace('--', '_-', static::$_xmlrpc_debuginfo), PhpXmlRpc::$xmlrpc_internalencoding, $charsetEncoding) . "\n-->\n";
             // NB: a better solution MIGHT be to use CDATA, but we need to insert it
             // into return payload AFTER the beginning tag
             //$out .= "<![CDATA[ DEBUG INFO:\n\n" . str_replace(']]>', ']_]_>', static::$_xmlrpc_debuginfo) . "\n]]>\n";
@@ -292,7 +335,7 @@ class Server
                 header('Content-Length: ' . (int)strlen($payload));
             }
         } else {
-            Logger::instance()->errorLog('XML-RPC: ' . __METHOD__ . ': http headers already sent before response is fully generated. Check for php warning or error messages');
+            $this->getLogger()->errorLog('XML-RPC: ' . __METHOD__ . ': http headers already sent before response is fully generated. Check for php warning or error messages');
         }
 
         print $payload;
@@ -392,7 +435,7 @@ class Server
         // check if $_SERVER is populated: it might have been disabled via ini file
         // (this is true even when in CLI mode)
         if (count($_SERVER) == 0) {
-            Logger::instance()->errorLog('XML-RPC: ' . __METHOD__ . ': cannot parse request headers as $_SERVER is not populated');
+            $this->getLogger()->errorLog('XML-RPC: ' . __METHOD__ . ': cannot parse request headers as $_SERVER is not populated');
         }
 
         if ($this->debug > 1) {
@@ -513,7 +556,7 @@ class Server
                     if (extension_loaded('mbstring')) {
                         $data = mb_convert_encoding($data, 'UTF-8', $reqEncoding);
                     } else {
-                        Logger::instance()->errorLog('XML-RPC: ' . __METHOD__ . ': invalid charset encoding of received request: ' . $reqEncoding);
+                        $this->getLogger()->errorLog('XML-RPC: ' . __METHOD__ . ': invalid charset encoding of received request: ' . $reqEncoding);
                     }
                 }
             }
@@ -530,8 +573,8 @@ class Server
             $options = array(XML_OPTION_TARGET_ENCODING => PhpXmlRpc::$xmlrpc_internalencoding);
         }
 
-        $xmlRpcParser = new XMLParser($options);
-        $xmlRpcParser->parse($data, $this->functions_parameters_type, XMLParser::ACCEPT_REQUEST);
+        $xmlRpcParser = $this->getParser();
+        $xmlRpcParser->parse($data, $this->functions_parameters_type, XMLParser::ACCEPT_REQUEST, $options);
         if ($xmlRpcParser->_xh['isf'] > 2) {
             // (BC) we return XML error as a faultCode
             preg_match('/^XML error ([0-9]+)/', $xmlRpcParser->_xh['isf_reason'], $matches);
@@ -643,7 +686,7 @@ class Server
 
         // verify that function to be invoked is in fact callable
         if (!is_callable($func)) {
-            Logger::instance()->errorLog("XML-RPC: " . __METHOD__ . ": function '$funcName' registered as method handler is not callable");
+            $this->getLogger()->errorLog("XML-RPC: " . __METHOD__ . ": function '$funcName' registered as method handler is not callable");
             return new Response(
                 0,
                 PhpXmlRpc::$xmlrpcerr['server_error'],
@@ -666,7 +709,7 @@ class Server
                     $r = call_user_func($func, $req);
                 }
                 if (!is_a($r, 'PhpXmlRpc\Response')) {
-                    Logger::instance()->errorLog("XML-RPC: " . __METHOD__ . ": function '$funcName' registered as method handler does not return an xmlrpc response object but a " . gettype($r));
+                    $this->getLogger()->errorLog("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);
                     } else {
@@ -773,7 +816,13 @@ class Server
         return (strpos($methName, "system.") === 0);
     }
 
-    /* Functions that implement system.XXX methods of xmlrpc servers */
+    /**
+     * @return array[]
+     */
+    public function getDispatchMap()
+    {
+        return $this->dmap;
+    }
 
     /**
      * @return array[]
@@ -820,6 +869,8 @@ class Server
         );
     }
 
+    /* Functions that implement system.XXX methods of xmlrpc servers */
+
     /**
      * @return array[]
      */
@@ -1119,7 +1170,10 @@ class Server
             // The previous error handler was the default: all we should do is log error
             // to the default error log (if level high enough)
             if (ini_get('log_errors') && (intval(ini_get('error_reporting')) & $errCode)) {
-                Logger::instance()->errorLog($errString);
+                if (self::$logger === null) {
+                    self::$logger = Logger::instance();
+                }
+                self::$logger->errorLog($errString);
             }
         } else {
             // Pass control on to previous error handler, trying to avoid loops...
index e3db3a1..1e7e288 100644 (file)
@@ -37,6 +37,9 @@ class Value implements \Countable, \IteratorAggregate, \ArrayAccess
         "null" => 1,
     );
 
+    protected static $logger;
+    protected static $charsetEncoder;
+
     /// @todo: do these need to be public?
     /** @var Value[]|mixed */
     public $me = array();
@@ -48,6 +51,32 @@ class Value implements \Countable, \IteratorAggregate, \ArrayAccess
     /** @var string|null $_php_class */
     public $_php_class = null;
 
+    public function getLogger()
+    {
+        if (self::$logger === null) {
+            self::$logger = Logger::instance();
+        }
+        return self::$logger;
+    }
+
+    public static function setLogger($logger)
+    {
+        self::$logger = $logger;
+    }
+
+    public function getCharsetEncoder()
+    {
+        if (self::$charsetEncoder === null) {
+            self::$charsetEncoder = Charset::instance();
+        }
+        return self::$charsetEncoder;
+    }
+
+    public function setCharsetEncoder($charsetEncoder)
+    {
+        self::$charsetEncoder = $charsetEncoder;
+    }
+
     /**
      * Build an xmlrpc value.
      *
@@ -90,7 +119,7 @@ class Value implements \Countable, \IteratorAggregate, \ArrayAccess
                     $this->me['struct'] = $val;
                     break;
                 default:
-                    Logger::instance()->errorLog("XML-RPC: " . __METHOD__ . ": not a known type ($type)");
+                    $this->getLogger()->errorLog("XML-RPC: " . __METHOD__ . ": not a known type ($type)");
             }
         }
     }
@@ -115,7 +144,7 @@ class Value implements \Countable, \IteratorAggregate, \ArrayAccess
         }
 
         if ($typeOf !== 1) {
-            Logger::instance()->errorLog("XML-RPC: " . __METHOD__ . ": not a scalar type ($type)");
+            $this->getLogger()->errorLog("XML-RPC: " . __METHOD__ . ": not a scalar type ($type)");
             return 0;
         }
 
@@ -132,10 +161,10 @@ class Value implements \Countable, \IteratorAggregate, \ArrayAccess
 
         switch ($this->mytype) {
             case 1:
-                Logger::instance()->errorLog('XML-RPC: ' . __METHOD__ . ': scalar xmlrpc value can have only one value');
+                $this->getLogger()->errorLog('XML-RPC: ' . __METHOD__ . ': scalar xmlrpc value can have only one value');
                 return 0;
             case 3:
-                Logger::instance()->errorLog('XML-RPC: ' . __METHOD__ . ': cannot add anonymous scalar to struct xmlrpc value');
+                $this->getLogger()->errorLog('XML-RPC: ' . __METHOD__ . ': cannot add anonymous scalar to struct xmlrpc value');
                 return 0;
             case 2:
                 // we're adding a scalar value to an array here
@@ -177,7 +206,7 @@ class Value implements \Countable, \IteratorAggregate, \ArrayAccess
 
             return 1;
         } else {
-            Logger::instance()->errorLog('XML-RPC: ' . __METHOD__ . ': already initialized as a [' . $this->kindOf() . ']');
+            $this->getLogger()->errorLog('XML-RPC: ' . __METHOD__ . ': already initialized as a [' . $this->kindOf() . ']');
             return 0;
         }
     }
@@ -208,7 +237,7 @@ class Value implements \Countable, \IteratorAggregate, \ArrayAccess
 
             return 1;
         } else {
-            Logger::instance()->errorLog('XML-RPC: ' . __METHOD__ . ': already initialized as a [' . $this->kindOf() . ']');
+            $this->getLogger()->errorLog('XML-RPC: ' . __METHOD__ . ': already initialized as a [' . $this->kindOf() . ']');
             return 0;
         }
     }
@@ -257,7 +286,7 @@ class Value implements \Countable, \IteratorAggregate, \ArrayAccess
                         break;
                     case static::$xmlrpcString:
                         // Do NOT use htmlentities, since it will produce named html entities, which are invalid xml
-                        $rs .= "<${typ}>" . Charset::instance()->encodeEntities($val, PhpXmlRpc::$xmlrpc_internalencoding, $charsetEncoding) . "</${typ}>";
+                        $rs .= "<${typ}>" . $this->getCharsetEncoder()->encodeEntities($val, PhpXmlRpc::$xmlrpc_internalencoding, $charsetEncoding) . "</${typ}>";
                         break;
                     case static::$xmlrpcInt:
                     case static::$xmlrpcI4:
@@ -304,7 +333,7 @@ class Value implements \Countable, \IteratorAggregate, \ArrayAccess
                 } else {
                     $rs .= "<struct>\n";
                 }
-                $charsetEncoder = Charset::instance();
+                $charsetEncoder = $this->getCharsetEncoder();
                 /** @var Value $val2 */
                 foreach ($val as $key2 => $val2) {
                     $rs .= '<member><name>' . $charsetEncoder->encodeEntities($key2, PhpXmlRpc::$xmlrpc_internalencoding, $charsetEncoding) . "</name>\n";
index 2751ec2..15f84ce 100644 (file)
@@ -24,6 +24,21 @@ class Wrapper
     /// used to hold a reference to object instances whose methods get wrapped by wrapPhpFunction(), in 'create source' mode
     public static $objHolder = array();
 
+    protected static $logger;
+
+    public function getLogger()
+    {
+        if (self::$logger === null) {
+            self::$logger = Logger::instance();
+        }
+        return self::$logger;
+    }
+
+    public static function setLogger($logger)
+    {
+        self::$logger = $logger;
+    }
+
     /**
      * Given a string defining a php type or phpxmlrpc type (loosely defined: strings
      * accepted come from javadoc blocks), return corresponding phpxmlrpc type.
@@ -168,7 +183,7 @@ class Wrapper
         }
         if (is_array($callable)) {
             if (count($callable) < 2 || (!is_string($callable[0]) && !is_object($callable[0]))) {
-                Logger::instance()->errorLog('XML-RPC: ' . __METHOD__ . ': syntax for function to be wrapped is wrong');
+                $this->getLogger()->errorLog('XML-RPC: ' . __METHOD__ . ': syntax for function to be wrapped is wrong');
                 return false;
             }
             if (is_string($callable[0])) {
@@ -180,7 +195,7 @@ class Wrapper
         } else if ($callable instanceof \Closure) {
             // we do not support creating code which wraps closures, as php does not allow to serialize them
             if (!$buildIt) {
-                Logger::instance()->errorLog('XML-RPC: ' . __METHOD__ . ': a closure can not be wrapped in generated source code');
+                $this->getLogger()->errorLog('XML-RPC: ' . __METHOD__ . ': a closure can not be wrapped in generated source code');
                 return false;
             }
 
@@ -192,7 +207,7 @@ class Wrapper
         }
 
         if (!$exists) {
-            Logger::instance()->errorLog('XML-RPC: ' . __METHOD__ . ': function to be wrapped is not defined: ' . $plainFuncName);
+            $this->getLogger()->errorLog('XML-RPC: ' . __METHOD__ . ': function to be wrapped is not defined: ' . $plainFuncName);
             return false;
         }
 
@@ -236,23 +251,23 @@ class Wrapper
         if (is_array($callable)) {
             $func = new \ReflectionMethod($callable[0], $callable[1]);
             if ($func->isPrivate()) {
-                Logger::instance()->errorLog('XML-RPC: ' . __METHOD__ . ': method to be wrapped is private: ' . $plainFuncName);
+                $this->getLogger()->errorLog('XML-RPC: ' . __METHOD__ . ': method to be wrapped is private: ' . $plainFuncName);
                 return false;
             }
             if ($func->isProtected()) {
-                Logger::instance()->errorLog('XML-RPC: ' . __METHOD__ . ': method to be wrapped is protected: ' . $plainFuncName);
+                $this->getLogger()->errorLog('XML-RPC: ' . __METHOD__ . ': method to be wrapped is protected: ' . $plainFuncName);
                 return false;
             }
             if ($func->isConstructor()) {
-                Logger::instance()->errorLog('XML-RPC: ' . __METHOD__ . ': method to be wrapped is the constructor: ' . $plainFuncName);
+                $this->getLogger()->errorLog('XML-RPC: ' . __METHOD__ . ': method to be wrapped is the constructor: ' . $plainFuncName);
                 return false;
             }
             if ($func->isDestructor()) {
-                Logger::instance()->errorLog('XML-RPC: ' . __METHOD__ . ': method to be wrapped is the destructor: ' . $plainFuncName);
+                $this->getLogger()->errorLog('XML-RPC: ' . __METHOD__ . ': method to be wrapped is the destructor: ' . $plainFuncName);
                 return false;
             }
             if ($func->isAbstract()) {
-                Logger::instance()->errorLog('XML-RPC: ' . __METHOD__ . ': method to be wrapped is abstract: ' . $plainFuncName);
+                $this->getLogger()->errorLog('XML-RPC: ' . __METHOD__ . ': method to be wrapped is abstract: ' . $plainFuncName);
                 return false;
             }
             /// @todo add more checks for static vs. nonstatic?
@@ -262,7 +277,7 @@ class Wrapper
         if ($func->isInternal()) {
             // Note: from PHP 5.1.0 onward, we will possibly be able to use invokeargs
             // instead of getparameters to fully reflect internal php functions ?
-            Logger::instance()->errorLog('XML-RPC: ' . __METHOD__ . ': function to be wrapped is internal: ' . $plainFuncName);
+            $this->getLogger()->errorLog('XML-RPC: ' . __METHOD__ . ': function to be wrapped is internal: ' . $plainFuncName);
             return false;
         }
 
@@ -745,7 +760,7 @@ class Wrapper
         $client->setDebug($debug);
         $response = $client->send($req, $timeout, $protocol);
         if ($response->faultCode()) {
-            Logger::instance()->errorLog('XML-RPC: ' . __METHOD__ . ': could not retrieve method signature from remote server for method ' . $methodName);
+            $this->getLogger()->errorLog('XML-RPC: ' . __METHOD__ . ': could not retrieve method signature from remote server for method ' . $methodName);
             return false;
         }
 
@@ -756,7 +771,7 @@ class Wrapper
         }
 
         if (!is_array($mSig) || count($mSig) <= $sigNum) {
-            Logger::instance()->errorLog('XML-RPC: ' . __METHOD__ . ': could not retrieve method signature nr.' . $sigNum . ' from remote server for method ' . $methodName);
+            $this->getLogger()->errorLog('XML-RPC: ' . __METHOD__ . ': could not retrieve method signature nr.' . $sigNum . ' from remote server for method ' . $methodName);
             return false;
         }
 
@@ -1021,7 +1036,7 @@ class Wrapper
         $req = new $reqClass('system.listMethods');
         $response = $client->send($req, $timeout, $protocol);
         if ($response->faultCode()) {
-            Logger::instance()->errorLog('XML-RPC: ' . __METHOD__ . ': could not retrieve method list from remote server');
+            $this->getLogger()->errorLog('XML-RPC: ' . __METHOD__ . ': could not retrieve method list from remote server');
 
             return false;
         } else {
@@ -1031,7 +1046,7 @@ class Wrapper
                 $mList = $decoder->decode($mList);
             }
             if (!is_array($mList) || !count($mList)) {
-                Logger::instance()->errorLog('XML-RPC: ' . __METHOD__ . ': could not retrieve meaningful method list from remote server');
+                $this->getLogger()->errorLog('XML-RPC: ' . __METHOD__ . ': could not retrieve meaningful method list from remote server');
 
                 return false;
             } else {
@@ -1073,7 +1088,7 @@ class Wrapper
                             }
                             $source .= $methodWrap['source'] . "\n";
                         } else {
-                            Logger::instance()->errorLog('XML-RPC: ' . __METHOD__ . ': will not create class method to wrap remote method ' . $mName);
+                            $this->getLogger()->errorLog('XML-RPC: ' . __METHOD__ . ': will not create class method to wrap remote method ' . $mName);
                         }
                     }
                 }
@@ -1084,7 +1099,7 @@ class Wrapper
                     if ($allOK) {
                         return $xmlrpcClassName;
                     } else {
-                        Logger::instance()->errorLog('XML-RPC: ' . __METHOD__ . ': could not create class ' . $xmlrpcClassName . ' to wrap remote server ' . $client->server);
+                        $this->getLogger()->errorLog('XML-RPC: ' . __METHOD__ . ': could not create class ' . $xmlrpcClassName . ' to wrap remote server ' . $client->server);
                         return false;
                     }
                 } else {