You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
328 lines
8.8 KiB
328 lines
8.8 KiB
<?php |
|
/* |
|
IXR - The Inutio XML-RPC Library - (c) Incutio Ltd 2002 |
|
Version 1.61 - Simon Willison, 11th July 2003 (htmlentities -> htmlspecialchars) |
|
Site: http://scripts.incutio.com/xmlrpc/ |
|
Manual: http://scripts.incutio.com/xmlrpc/manual.php |
|
Made available under the Artistic License: http://www.opensource.org/licenses/artistic-license.php |
|
*/ |
|
|
|
/** |
|
* IXR服务器 |
|
* |
|
* @package IXR |
|
*/ |
|
class IXR_Server |
|
{ |
|
/** |
|
* 输入参数 |
|
* |
|
* @access private |
|
* @var array |
|
*/ |
|
private $data; |
|
|
|
/** |
|
* 回调函数 |
|
* |
|
* @access private |
|
* @var array |
|
*/ |
|
private $callbacks = array(); |
|
|
|
/** |
|
* 消息体 |
|
* |
|
* @access private |
|
* @var IXR_Message |
|
*/ |
|
private $message; |
|
|
|
/** |
|
* 默认参数 |
|
* |
|
* @access private |
|
* @var array |
|
*/ |
|
private $capabilities; |
|
|
|
/** |
|
* 构造函数 |
|
* |
|
* @access public |
|
* @param mixed $callbacks 回调函数 |
|
* @param mixed $data 输入参数 |
|
* @return void |
|
*/ |
|
public function __construct($callbacks = false, $data = false) |
|
{ |
|
$this->setCapabilities(); |
|
if ($callbacks) { |
|
$this->callbacks = $callbacks; |
|
} |
|
$this->setCallbacks(); |
|
$this->serve($data); |
|
} |
|
|
|
/** |
|
* 呼叫内部方法 |
|
* |
|
* @access private |
|
* @param string $methodname 方法名 |
|
* @param mixed $args 参数 |
|
* @return mixed |
|
*/ |
|
private function call($methodname, $args) |
|
{ |
|
// hook |
|
if (0 !== strpos($methodname, 'hook.') && $this->hasMethod('hook.beforeCall')) { |
|
$this->call('hook.beforeCall', array($methodname)); |
|
} |
|
|
|
if (!$this->hasMethod($methodname)) { |
|
return new IXR_Error(-32601, 'server error. requested method '.$methodname.' does not exist.'); |
|
} |
|
$method = $this->callbacks[$methodname]; |
|
|
|
// Are we dealing with a function or a method? |
|
if (is_string($method) && substr($method, 0, 5) == 'this:') { |
|
// It's a class method - check it exists |
|
$method = substr($method, 5); |
|
if (!method_exists($this, $method)) { |
|
return new IXR_Error(-32601, 'server error. requested class method "'.$method.'" does not exist.'); |
|
} |
|
// Call the method |
|
$result = $this->$method($args); |
|
} else { |
|
if (is_array($method)) { |
|
list($object, $func) = $method; |
|
if (!is_callable($method)) { |
|
return new IXR_Error(-32601, 'server error. requested class method "'.$object . '.' . $func.'" does not exist.'); |
|
} |
|
|
|
$result = call_user_func_array(array($object, $func), $args); |
|
} elseif (!function_exists($method)) { |
|
// It's a function - does it exist? |
|
return new IXR_Error(-32601, 'server error. requested function "'.$method.'" does not exist.'); |
|
} else { |
|
// Call the function |
|
$result = $method($args); |
|
} |
|
} |
|
|
|
// hook |
|
if (0 !== strpos($methodname, 'hook.') && $this->hasMethod('hook.afterCall')) { |
|
$this->call('hook.afterCall', array($methodname)); |
|
} |
|
|
|
return $result; |
|
} |
|
|
|
/** |
|
* 抛出错误 |
|
* |
|
* @access private |
|
* @param integer $error 错误代码 |
|
* @param string $message 错误消息 |
|
* @return void |
|
*/ |
|
private function error($error, $message = false) |
|
{ |
|
// Accepts either an error object or an error code and message |
|
if ($message && !is_object($error)) { |
|
$error = new IXR_Error($error, $message); |
|
} |
|
$this->output($error->getXml()); |
|
} |
|
|
|
/** |
|
* 输出xml |
|
* |
|
* @access private |
|
* @param string $xml 输出xml |
|
* @return 输出xml |
|
*/ |
|
private function output($xml) |
|
{ |
|
$xml = '<?xml version="1.0"?>'."\n".$xml; |
|
$length = strlen($xml); |
|
header('Connection: close'); |
|
header('Content-Length: '.$length); |
|
header('Content-Type: text/xml'); |
|
header('Date: '.date('r')); |
|
echo $xml; |
|
exit; |
|
} |
|
|
|
/** |
|
* 是否存在方法 |
|
* |
|
* @access private |
|
* @param string $method 方法名 |
|
* @return mixed |
|
*/ |
|
private function hasMethod($method) |
|
{ |
|
return in_array($method, array_keys($this->callbacks)); |
|
} |
|
|
|
/** |
|
* 设置默认参数 |
|
* |
|
* @access public |
|
* @return void |
|
*/ |
|
private function setCapabilities() |
|
{ |
|
// Initialises capabilities array |
|
$this->capabilities = array( |
|
'xmlrpc' => array( |
|
'specUrl' => 'http://www.xmlrpc.com/spec', |
|
'specVersion' => 1 |
|
), |
|
'faults_interop' => array( |
|
'specUrl' => 'http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php', |
|
'specVersion' => 20010516 |
|
), |
|
'system.multicall' => array( |
|
'specUrl' => 'http://www.xmlrpc.com/discuss/msgReader$1208', |
|
'specVersion' => 1 |
|
), |
|
); |
|
} |
|
|
|
/** |
|
* 设置默认方法 |
|
* |
|
* @access private |
|
* @return void |
|
*/ |
|
private function setCallbacks() |
|
{ |
|
$this->callbacks['system.getCapabilities'] = 'this:getCapabilities'; |
|
$this->callbacks['system.listMethods'] = 'this:listMethods'; |
|
$this->callbacks['system.multicall'] = 'this:multiCall'; |
|
} |
|
|
|
/** |
|
* 服务入口 |
|
* |
|
* @access private |
|
* @param mixed $data 输入参数 |
|
* @return void |
|
*/ |
|
private function serve($data = false) |
|
{ |
|
if (!isset($GLOBALS['HTTP_RAW_POST_DATA'])) { |
|
$GLOBALS['HTTP_RAW_POST_DATA'] = file_get_contents("php://input"); |
|
} |
|
if (isset($GLOBALS['HTTP_RAW_POST_DATA'])) { |
|
$GLOBALS['HTTP_RAW_POST_DATA'] = trim($GLOBALS['HTTP_RAW_POST_DATA']); |
|
} |
|
|
|
if (!$data) { |
|
global $HTTP_RAW_POST_DATA; |
|
if (!$HTTP_RAW_POST_DATA) { |
|
die('XML-RPC server accepts POST requests only.'); |
|
} |
|
$data = $HTTP_RAW_POST_DATA; |
|
} |
|
$this->message = new IXR_Message($data); |
|
if (!$this->message->parse()) { |
|
$this->error(-32700, 'parse error. not well formed'); |
|
} |
|
if ($this->message->messageType != 'methodCall') { |
|
$this->error(-32600, 'server error. invalid xml-rpc. not conforming to spec. Request must be a methodCall'); |
|
} |
|
|
|
if (0 === strpos($this->message->methodName, 'hook.')) { |
|
die('THIS METHOD MUST BE CALLED INSIDE.'); |
|
} |
|
|
|
$result = $this->call($this->message->methodName, $this->message->params); |
|
// Is the result an error? |
|
if (is_a($result, 'IXR_Error')) { |
|
$this->error($result); |
|
} |
|
// Encode the result |
|
$r = new IXR_Value($result); |
|
$resultxml = $r->getXml(); |
|
// Create the XML |
|
$xml = <<<EOD |
|
<methodResponse> |
|
<params> |
|
<param> |
|
<value> |
|
$resultxml |
|
</value> |
|
</param> |
|
</params> |
|
</methodResponse> |
|
|
|
EOD; |
|
// hook |
|
if ($this->hasMethod('hook.beforeOutput')) { |
|
$this->call('hook.beforeOutput', array()); |
|
} |
|
|
|
// Send it |
|
$this->output($xml); |
|
} |
|
|
|
/** |
|
* 获取默认参数 |
|
* |
|
* @access public |
|
* @param mixed $args 输入参数 |
|
* @return array |
|
*/ |
|
public function getCapabilities($args) |
|
{ |
|
return $this->capabilities; |
|
} |
|
|
|
/** |
|
* 列出所有方法 |
|
* |
|
* @access public |
|
* @param mixed $args 输入参数 |
|
* @return mixed |
|
*/ |
|
public function listMethods($args) |
|
{ |
|
// 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)); |
|
} |
|
|
|
/** |
|
* 一次处理多个请求 |
|
* |
|
* @access public |
|
* @param void $methodcalls |
|
* @return array |
|
*/ |
|
public function multiCall($methodcalls) |
|
{ |
|
// See http://www.xmlrpc.com/discuss/msgReader$1208 |
|
$return = array(); |
|
foreach ($methodcalls as $call) { |
|
$method = $call['methodName']; |
|
$params = $call['params']; |
|
if ($method == 'system.multicall') { |
|
$result = new IXR_Error(-32600, 'Recursive calls to system.multicall are forbidden'); |
|
} else { |
|
$result = $this->call($method, $params); |
|
} |
|
if (is_a($result, 'IXR_Error')) { |
|
$return[] = array( |
|
'faultCode' => $result->code, |
|
'faultString' => $result->message |
|
); |
|
} else { |
|
$return[] = array($result); |
|
} |
|
} |
|
return $return; |
|
} |
|
}
|
|
|