data = $data; if (!$type) { $type = $this->calculateType(); } $this->type = $type; if ($type == 'struct') { # Turn all the values in the array in to new xmlrpcValue objects foreach ($this->data as $key => $value) { $this->data[$key] = new xmlrpcValue($value); } } if ($type == 'array') { for ($i = 0, $j = count($this->data); $i < $j; $i++) { $this->data[$i] = new xmlrpcValue($this->data[$i]); } } } /** * XML Data * * Returns an XML subset of the Value. * * @return string */ public function getXml() { # Return XML for this value switch ($this->type) { case 'boolean': return '' . (($this->data) ? '1' : '0') . ''; break; case 'int': return '' . $this->data . ''; break; case 'double': return '' . $this->data . ''; break; case 'string': return '' . htmlspecialchars($this->data) . ''; break; case 'array': $return = '' . "\n"; foreach ($this->data as $item) { $return .= ' ' . $item->getXml() . "\n"; } $return .= ''; return $return; break; case 'struct': $return = '' . "\n"; foreach ($this->data as $name => $value) { $return .= " $name"; $return .= $value->getXml() . "\n"; } $return .= ''; return $return; break; case 'date': case 'base64': return $this->data->getXml(); break; } return false; } /** * Calculate Type * * Returns the type of the value if it was not given in constructor. * * @return string */ protected function calculateType() { if ($this->data === true || $this->data === false) { return 'boolean'; } if (is_integer($this->data)) { return 'int'; } if (is_double($this->data)) { return 'double'; } # Deal with xmlrpc object types base64 and date if (is_object($this->data) && $this->data instanceof xmlrpcDate) { return 'date'; } if (is_object($this->data) && $this->data instanceof xmlrpcBase64) { return 'base64'; } # If it is a normal PHP object convert it in to a struct if (is_object($this->data)) { $this->data = get_object_vars($this->data); return 'struct'; } if (!is_array($this->data)) { return 'string'; } # We have an array - is it an array or a struct ? if ($this->isStruct($this->data)) { return 'struct'; } else { return 'array'; } } /** * Data is struct * * Returns true if $array is a Struct and not only an Array. * * @param array $array Array * @return boolean */ protected function isStruct($array) { # Nasty function to check if an array is a struct or not $expected = 0; foreach ($array as $key => $value) { if ((string) $key != (string) $expected) { return true; } $expected++; } return false; } } /** * @class xmlrpcMessage * @brief XML-RPC Message */ class xmlrpcMessage { protected $brutxml; ///< string Brut XML message protected $message; ///< string XML message public $messageType; ///< string Type of message - methodCall / methodResponse / fault public $faultCode; ///< string Fault code public $faultString; ///< string Fault string public $methodName; ///< string Method name public $params = []; ///< array Method parameters # Currentstring variable stacks protected $_arraystructs = []; ///< The stack used to keep track of the current array/struct protected $_arraystructstypes = []; ///< Stack keeping track of if things are structs or array protected $_currentStructName = []; ///< A stack as well protected $_param; protected $_value; protected $_currentTag; protected $_currentTagContents; protected $_parser; ///< The XML parser /** * Constructor * * @param string $message XML Message */ public function __construct($message) { $this->brutxml = $this->message = $message; } /** * Message parser */ public function parse() { // first remove the XML declaration $this->message = preg_replace('/<\?xml(.*)?\?' . '>/', '', $this->message); if (trim($this->message) == '') { throw new Exception('XML Parser Error. Empty message'); } // Strip DTD. $header = preg_replace('/^]*+>/i', '', substr($this->message, 0, 200), 1); $xml = trim(substr_replace($this->message, $header, 0, 200)); if ($xml == '') { throw new Exception('XML Parser Error.'); } // Confirm the XML now starts with a valid root tag. A root tag can end in [> \t\r\n] $root_tag = substr($xml, 0, strcspn(substr($xml, 0, 20), "> \t\r\n")); // Reject a second DTD. if (strtoupper($root_tag) == 'loadXML($xml); if ($dom->getElementsByTagName('*')->length > 30000) { throw new Exception('XML Parser Error.'); } } catch (Exception $e) { throw new Exception('XML Parser Error.'); } $this->_parser = xml_parser_create(); # Set XML parser to take the case of tags in to account xml_parser_set_option($this->_parser, XML_OPTION_CASE_FOLDING, false); # Set XML parser callback functions xml_set_object($this->_parser, $this); xml_set_element_handler($this->_parser, 'tag_open', 'tag_close'); xml_set_character_data_handler($this->_parser, 'cdata'); if (!xml_parse($this->_parser, $this->message)) { $c = xml_get_error_code($this->_parser); $e = xml_error_string($c); $e .= ' on line ' . xml_get_current_line_number($this->_parser); throw new Exception('XML Parser Error. ' . $e, $c); } xml_parser_free($this->_parser); # Grab the error messages, if any if ($this->messageType == 'fault') { $this->faultCode = $this->params[0]['faultCode']; $this->faultString = $this->params[0]['faultString']; } return true; } protected function tag_open($parser, $tag, $attr) { $this->currentTag = $tag; switch ($tag) { case 'methodCall': case 'methodResponse': case 'fault': $this->messageType = $tag; break; # Deal with stacks of arrays and structs case 'data': # data is to all intents and puposes more interesting than array $this->_arraystructstypes[] = 'array'; $this->_arraystructs[] = []; break; case 'struct': $this->_arraystructstypes[] = 'struct'; $this->_arraystructs[] = []; break; } } protected function cdata($parser, $cdata) { $this->_currentTagContents .= $cdata; } protected function tag_close($parser, $tag) { $valueFlag = false; switch ($tag) { case 'int': case 'i4': $value = (int) trim($this->_currentTagContents); $this->_currentTagContents = ''; $valueFlag = true; break; case 'double': $value = (double) trim($this->_currentTagContents); $this->_currentTagContents = ''; $valueFlag = true; break; case 'string': $value = (string) trim($this->_currentTagContents); $this->_currentTagContents = ''; $valueFlag = true; break; case 'dateTime.iso8601': $value = new xmlrpcDate(trim($this->_currentTagContents)); # $value = $iso->getTimestamp(); $this->_currentTagContents = ''; $valueFlag = true; break; case 'value': # "If no type is indicated, the type is string." if (trim($this->_currentTagContents) != '') { $value = (string) $this->_currentTagContents; $this->_currentTagContents = ''; $valueFlag = true; } break; case 'boolean': $value = (boolean) trim($this->_currentTagContents); $this->_currentTagContents = ''; $valueFlag = true; break; case 'base64': $value = base64_decode($this->_currentTagContents); $this->_currentTagContents = ''; $valueFlag = true; break; # Deal with stacks of arrays and structs case 'data': case 'struct': $value = array_pop($this->_arraystructs); array_pop($this->_arraystructstypes); $valueFlag = true; break; case 'member': array_pop($this->_currentStructName); break; case 'name': $this->_currentStructName[] = trim($this->_currentTagContents); $this->_currentTagContents = ''; break; case 'methodName': $this->methodName = trim($this->_currentTagContents); $this->_currentTagContents = ''; break; } if ($valueFlag) { if (count($this->_arraystructs) > 0) { # Add value to struct or array if ($this->_arraystructstypes[count($this->_arraystructstypes) - 1] == 'struct') { # Add to struct $this->_arraystructs[count($this->_arraystructs) - 1][$this->_currentStructName[count($this->_currentStructName) - 1]] = $value; } else { # Add to array $this->_arraystructs[count($this->_arraystructs) - 1][] = $value; } } else { # Just add as a paramater $this->params[] = $value; } } } } /** * @class xmlrpcRequest * @brief XML-RPC Request */ class xmlrpcRequest { public $method; ///< string Request method name public $args; ///< array Request method arguments public $xml; ///< string Request XML string /** * Constructor * * @param string $method Method name * @param array $args Method arguments */ public function __construct($method, $args) { $this->method = $method; $this->args = $args; $this->xml = '' . "\n" . "\n" . ' ' . $this->method . "\n" . " \n"; foreach ($this->args as $arg) { $this->xml .= ' '; $v = new xmlrpcValue($arg); $this->xml .= $v->getXml(); $this->xml .= "\n"; } $this->xml .= ' '; } /** * Request length * * Returns {@link $xml} content length. * * @return integer */ public function getLength() { return strlen($this->xml); } /** * Request XML * * Returns request XML version. * * @return string */ public function getXml() { return $this->xml; } } /** * @class xmlrpcDate * @brief XML-RPC Date object */ class xmlrpcDate { protected $year; ///< string protected $month; ///< string protected $day; ///< string protected $hour; ///< string protected $minute; ///< string protected $second; ///< string /** * Constructor * * Creates a new instance of xmlrpcDate. $time could be a * timestamp or a litteral date. * * @param integer|string $time Timestamp or litteral date. */ public function __construct($time) { # $time can be a PHP timestamp or an ISO one if (is_numeric($time)) { $this->parseTimestamp($time); } else { $this->parseTimestamp(strtotime($time)); } } /** * Timestamp parser * * @param integer $timestamp Timestamp */ protected function parseTimestamp($timestamp) { $this->year = date('Y', $timestamp); $this->month = date('m', $timestamp); $this->day = date('d', $timestamp); $this->hour = date('H', $timestamp); $this->minute = date('i', $timestamp); $this->second = date('s', $timestamp); $this->ts = $timestamp; } /** * ISO Date * * Returns the date in ISO-8601 format. * * @return string */ public function getIso() { return $this->year . $this->month . $this->day . 'T' . $this->hour . ':' . $this->minute . ':' . $this->second; } /** * XML Date * * Returns the XML fragment for XML-RPC message inclusion. * * @return string */ public function getXml() { return '' . $this->getIso() . ''; } /** * Timestamp * * Returns the date timestamp. * * @return integer */ public function getTimestamp() { return mktime($this->hour, $this->minute, $this->second, $this->month, $this->day, $this->year); } } /** * @class xmlrpcBase64 * @brief XML-RPC Base 64 object */ class xmlrpcBase64 { protected $data; ///< string /** * Constructor * * Create a new instance of xmlrpcBase64. * * @param string $data Data */ public function __construct($data) { $this->data = $data; } /** * XML Data * * Returns the XML fragment for XML-RPC message inclusion. * * @return string */ public function getXml() { return '' . base64_encode($this->data) . ''; } } /** * @class xmlrpcClient * @brief XML-RPC Client * * XML-RPC Client * * This class library is fully based on Simon Willison's IXR library (http://scripts.incutio.com/xmlrpc/). * * Basic XML-RPC Client. */ /** @cond ONCE */ if (class_exists('netHttp')) { /** @endcond */ class xmlrpcClient extends netHttp { protected $request; ///< xmlrpcRequest XML-RPC Request object protected $message; ///< xmlrpcMessage XML-RPC Message object /** * Constructor * * Creates a new instance. $url is the XML-RPC Server end point. * * @param string $url Service URL */ public function __construct($url) { if (!$this->readUrl($url, $ssl, $host, $port, $path, $user, $pass)) { return false; } parent::__construct($host, $port); $this->useSSL($ssl); $this->setAuthorization($user, $pass); $this->path = $path; $this->user_agent = 'Clearbricks XML/RPC Client'; } /** * XML-RPC Query * * This method calls the given query (first argument) on XML-RPC Server. * All other arguments of this method are XML-RPC method arguments. * This method throws an exception if XML-RPC method returns an error or * returns the server's response. * * Example: * * query('method1','hello','world'); * ?> * * * @return mixed */ public function query() { $args = func_get_args(); $method = array_shift($args); $this->request = new xmlrpcRequest($method, $args); $this->doRequest(); if ($this->status != 200) { throw new Exception('HTTP Error. ' . $this->status . ' ' . $this->status_string); } # Now parse what we've got back $this->message = new xmlrpcMessage($this->content); $this->message->parse(); # Is the message a fault? if ($this->message->messageType == 'fault') { throw new xmlrpcException($this->message->faultString, $this->message->faultCode); } return $this->message->params[0]; } # Overloading netHttp::buildRequest method, we don't need all the stuff of # HTTP client. protected function buildRequest() { if ($this->proxy_host) { $path = $this->getRequestURL(); } else { $path = $this->path; } return [ 'POST ' . $path . ' HTTP/1.0', 'Host: ' . $this->host, 'Content-Type: text/xml', 'User-Agent: ' . $this->user_agent, 'Content-Length: ' . $this->request->getLength(), '', $this->request->getXML() ]; } } /** @cond ONCE */ } /** @endcond */ /** * @class xmlrpcClientMulticall * @brief Multicall XML-RPC Client * * Multicall XML-RPC Client * * This class library is fully based on Simon Willison's IXR library (http://scripts.incutio.com/xmlrpc/). * * Multicall client using system.multicall method of server. */ /** @cond ONCE */ if (class_exists('xmlrpcClient')) { /** @endcond */ class xmlrpcClientMulticall extends xmlrpcClient { protected $calls = []; ///< array public function __construct($url) { parent::__construct($url); } /** * Add call to stack * * This method adds a method call for the given query (first argument) to * calls stack. * All other arguments of this method are XML-RPC method arguments. * * Example: * * addCall('method1','hello','world'); * $o->addCall('method2','foo','bar'); * $r = $o->query(); * ?> * * * @return mixed */ public function addCall() { $args = func_get_args(); $methodName = array_shift($args); $struct = [ 'methodName' => $methodName, 'params' => $args ]; $this->calls[] = $struct; } /** * XML-RPC Query * * This method sends calls stack to XML-RPC system.multicall method. * See {@link xmlrpcServer::multiCall()} for details and links about it. * * @return array */ public function query() { # Prepare multicall, then call the parent::query() method return parent::query('system.multicall', $this->calls); } } /** @cond ONCE */ } /** @endcond */ /** * @class xmlrpcServer * @brief Basic XML-RPC Server * * XML-RPC Server * * This class library is fully based on Simon Willison's IXR library (http://scripts.incutio.com/xmlrpc/). * * This is the most basic XML-RPC server you can create. Built-in methods are: * * - system.getCapabilities * - system.listMethods * - system.multicall */ class xmlrpcServer { protected $callbacks = []; ///< array Server methods protected $data; ///< string Received data protected $encoding; ///< string Server encoding protected $message; ///< xmlrpcMessage Returned message protected $capabilities; ///< array Server capabilities public $strict_check = false; ///< boolean Strict XML-RPC checks /** * Constructor * * @param array $callbacks Server callbacks * @param string $data Server data * @param string $encoding Server encoding */ public function __construct($callbacks = false, $data = false, $encoding = 'UTF-8') { $this->encoding = $encoding; $this->setCapabilities(); if ($callbacks) { $this->callbacks = $callbacks; } $this->setCallbacks(); $this->serve($data); } /** * Start XML-RPC Server * * This method starts the XML-RPC Server. It could take a data argument * which should be a valid XML-RPC raw stream. If data is not specified, it * take values from raw POST data. * * @param string $data XML-RPC raw stream */ public function serve($data = false) { if (!$data) { try { # Check HTTP Method if ($_SERVER['REQUEST_METHOD'] != 'POST') { throw new Exception('XML-RPC server accepts POST requests only.', 405); } # Check HTTP_HOST if (!isset($_SERVER['HTTP_HOST'])) { throw new Exception('No Host Specified', 400); } global $HTTP_RAW_POST_DATA; if (!$HTTP_RAW_POST_DATA) { $HTTP_RAW_POST_DATA = @file_get_contents('php://input'); if (!$HTTP_RAW_POST_DATA) { throw new Exception('No Message', 400); } } if ($this->strict_check) { # Check USER_AGENT if (!isset($_SERVER['HTTP_USER_AGENT'])) { throw new Exception('No User Agent Specified', 400); } # Check CONTENT_TYPE if (!isset($_SERVER['CONTENT_TYPE']) || strpos($_SERVER['CONTENT_TYPE'], 'text/xml') !== 0) { throw new Exception('Invalid Content-Type', 400); } # Check CONTENT_LENGTH if (!isset($_SERVER['CONTENT_LENGTH']) || $_SERVER['CONTENT_LENGTH'] != strlen($HTTP_RAW_POST_DATA)) { throw new Exception('Invalid Content-Lenth', 400); } } $data = $HTTP_RAW_POST_DATA; } catch (Exception $e) { if ($e->getCode() == 400) { $this->head(400, 'Bad Request'); } elseif ($e->getCode() == 405) { $this->head(405, 'Method Not Allowed'); header('Allow: POST'); } header('Content-Type: text/plain'); echo $e->getMessage(); exit; } } $this->message = new xmlrpcMessage($data); try { $this->message->parse(); if ($this->message->messageType != 'methodCall') { throw new xmlrpcException('Server error. Invalid xml-rpc. not conforming to spec. Request must be a methodCall', -32600); } $result = $this->call($this->message->methodName, $this->message->params); } catch (Exception $e) { $this->error($e); } # Encode the result $r = new xmlrpcValue($result); $resultxml = $r->getXml(); # Create the XML $xml = "\n" . "\n" . "\n" . " \n" . ' ' . $resultxml . "\n" . " \n" . "\n" . "\n" . ""; # Send it $this->output($xml); } /** * Send HTTP Headers * * This method sends a HTTP Header * * @param integer $code HTTP Status Code * @param string $msg Header message */ protected function head($code, $msg) { $status_mode = preg_match('/cgi/', PHP_SAPI); if ($status_mode) { header('Status: ' . $code . ' ' . $msg); } else { header($msg, true, $code); } } /** * Method call * * This method calls the given XML-RPC method with arguments. * * @param string $methodname Method name * @param array $args Method arguments * @return mixed */ protected function call($methodname, $args) { if (!$this->hasMethod($methodname)) { throw new xmlrpcException('server error. requested method "' . $methodname . '" does not exist.', -32601); } $method = $this->callbacks[$methodname]; # Perform the callback and send the response if (!is_callable($method)) { throw new xmlrpcException('server error. internal requested function for "' . $methodname . '" does not exist.', -32601); } return call_user_func_array($method, $args); } /** * XML-RPC Error * * This method create an XML-RPC error message from a PHP Exception object. * You should avoid using this in your own method and throw exceptions * instead. * * @param Exception $e Exception object */ protected function error($e) { $msg = $e->getMessage(); $this->output( "\n" . " \n" . " \n" . " \n" . " \n" . " faultCode\n" . ' ' . $e->getCode() . "\n" . " \n" . " \n" . " faultString\n" . ' ' . $msg . "\n" . " \n" . " \n" . " \n" . " \n" . "\n" ); } /** * Output response * * This method sends the whole XML-RPC response through HTTP. * * @param string $xml XML Content */ protected function output($xml) { $xml = 'encoding . '"?>' . "\n" . $xml; $length = strlen($xml); header('Connection: close'); header('Content-Length: ' . $length); header('Content-Type: text/xml'); header('Date: ' . date('r')); echo $xml; exit; } /** * XML-RPC Server has method? * * Returns true if the server has the given method $method * * @param string $method Method name * @return boolean */ protected function hasMethod($method) { return in_array($method, array_keys($this->callbacks)); } /** * Server Capabilities * * This method initiates the server capabilities: * - xmlrpc * - faults_interop * - system.multicall */ protected function setCapabilities() { # Initialises capabilities array $this->capabilities = [ 'xmlrpc' => [ 'specUrl' => 'http://www.xmlrpc.com/spec', 'specVersion' => 1 ], 'faults_interop' => [ 'specUrl' => 'http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php', 'specVersion' => 20010516 ], 'system.multicall' => [ 'specUrl' => 'http://www.xmlrpc.com/discuss/msgReader$1208', 'specVersion' => 1 ] ]; } /** * Server Methods * * This method creates the three main server's methods: * - system.getCapabilities * - system.listMethods * - system.multicall * * @see getCapabilities() * @see listMethods() * @see multiCall() */ protected function setCallbacks() { $this->callbacks['system.getCapabilities'] = [$this, 'getCapabilities']; $this->callbacks['system.listMethods'] = [$this, 'listMethods']; $this->callbacks['system.multicall'] = [$this, 'multiCall']; } /** * Server Capabilities * * Returns server capabilities * * @return array */ protected function getCapabilities() { return $this->capabilities; } /** * Server methods * * Returns all server methods * * @return array */ protected function listMethods() { # Returns a list of methods - uses array_reverse to ensure user defined # methods are listed before server defined methods return array_reverse(array_keys($this->callbacks)); } /** * Multicall * * This method handles a multi-methods call * * @see http://www.xmlrpc.com/discuss/msgReader$1208 * * @param array $methodcalls Array of methods * @return array */ protected function multiCall($methodcalls) { $return = []; foreach ($methodcalls as $call) { $method = $call['methodName']; $params = $call['params']; try { if ($method == 'system.multicall') { throw new xmlrpcException('Recursive calls to system.multicall are forbidden', -32600); } $result = $this->call($method, $params); $return[] = [$result]; } catch (Exception $e) { $return[] = [ 'faultCode' => $e->getCode(), 'faultString' => $e->getMessage() ]; } } return $return; } } /** * @class xmlrpcIntrospectionServer * @brief XML-RPC Introspection Server * * This class implements the most used type of XML-RPC Server. * It allows you to create classes inherited from this one and add methods * with {@link addCallback() addCallBack method}. * * This server class implements the following XML-RPC methods: * - system.methodSignature * - system.getCapabilities * - system.listMethods * - system.methodHelp * - system.multicall */ /** @cond ONCE */ if (class_exists('xmlrpcServer')) { /** @endcond */ class xmlrpcIntrospectionServer extends xmlrpcServer { protected $signatures; protected $help; /** * Constructor * * This method should be inherited to add new callbacks with * {@link addCallback()}. * * @param string $encoding Server encoding */ public function __construct($encoding = 'UTF-8') { $this->encoding = $encoding; $this->setCallbacks(); $this->setCapabilities(); $this->capabilities['introspection'] = [ 'specUrl' => 'http://xmlrpc.usefulinc.com/doc/reserved.html', 'specVersion' => 1 ]; $this->addCallback( 'system.methodSignature', [$this, 'methodSignature'], ['array', 'string'], 'Returns an array describing the return type and required parameters of a method' ); $this->addCallback( 'system.getCapabilities', [$this, 'getCapabilities'], ['struct'], 'Returns a struct describing the XML-RPC specifications supported by this server' ); $this->addCallback( 'system.listMethods', [$this, 'listMethods'], ['array'], 'Returns an array of available methods on this server' ); $this->addCallback( 'system.methodHelp', [$this, 'methodHelp'], ['string', 'string'], 'Returns a documentation string for the specified method' ); $this->addCallback( 'system.multicall', [$this, 'multiCall'], ['struct', 'array'], 'Returns result of multiple methods calls' ); } /** * Add Server Callback * * This method creates a new XML-RPC method which references a class * callback. $callback should be a valid PHP callback. * * @param string $method Method name * @param callback $callback Method callback * @param array $args Array of arguments type. The first is the returned one. * @param string $help Method help string */ protected function addCallback($method, $callback, $args, $help) { $this->callbacks[$method] = $callback; $this->signatures[$method] = $args; $this->help[$method] = $help; } /** * Method call * * This method calls the callbacks function or method for the given XML-RPC * method $methodname with arguments in $args array. * * @param string $methodname Method name * @param array $args Arguments * @return mixed */ protected function call($methodname, $args) { # Make sure it's in an array if ($args && !is_array($args)) { $args = [$args]; } # Over-rides default call method, adds signature check if (!$this->hasMethod($methodname)) { throw new xmlrpcException('Server error. Requested method "' . $methodname . '" not specified.', -32601); } $method = $this->callbacks[$methodname]; $signature = $this->signatures[$methodname]; if (!is_array($signature)) { throw new xmlrpcException('Server error. Wrong method signature', -36600); } $return_type = array_shift($signature); # Check the number of arguments if (count($args) > count($signature)) { throw new xmlrpcException('Server error. Wrong number of method parameters', -32602); } # Check the argument types if (!$this->checkArgs($args, $signature)) { throw new xmlrpcException('Server error. Invalid method parameters', -32602); } # It passed the test - run the "real" method call return parent::call($methodname, $args); } /** * Method Arguments Check * * This method checks the validity of method arguments. * * @param array $args Method given arguments * @param array $signature Method defined arguments * @return boolean */ protected function checkArgs($args, $signature) { for ($i = 0, $j = count($args); $i < $j; $i++) { $arg = array_shift($args); $type = array_shift($signature); switch ($type) { case 'int': case 'i4': if (is_array($arg) || !is_int($arg)) { return false; } break; case 'base64': case 'string': if (!is_string($arg)) { return false; } break; case 'boolean': if ($arg !== false && $arg !== true) { return false; } break; case 'float': case 'double': if (!is_float($arg)) { return false; } break; case 'date': case 'dateTime.iso8601': if (!($arg instanceof xmlrpcDate)) { return false; } break; } } return true; } /** * Method Signature * * This method return given XML-RPC method signature. * * @param string $method Method name * @return array */ protected function methodSignature($method) { if (!$this->hasMethod($method)) { throw new xmlrpcException('Server error. Requested method "' . $method . '" not specified.', -32601); } # We should be returning an array of types $types = $this->signatures[$method]; $return = []; foreach ($types as $type) { switch ($type) { case 'string': $return[] = 'string'; break; case 'int': case 'i4': $return[] = 42; break; case 'double': $return[] = 3.1415; break; case 'dateTime.iso8601': $return[] = new xmlrpcDate(time()); break; case 'boolean': $return[] = true; break; case 'base64': $return[] = new xmlrpcBase64('base64'); break; case 'array': $return[] = ['array']; break; case 'struct': $return[] = ['struct' => 'struct']; break; } } return $return; } /** * Method Help * * This method return given XML-RPC method help string. * * @param string $method Method name * @return string */ protected function methodHelp($method) { return $this->help[$method]; } } /** @cond ONCE */ } /** @endcond */