587 lines
17 KiB
PHP
587 lines
17 KiB
PHP
<?php
|
|
/**
|
|
* Zend Framework
|
|
*
|
|
* LICENSE
|
|
*
|
|
* This source file is subject to the new BSD license that is bundled
|
|
* with this package in the file LICENSE.txt.
|
|
* It is also available through the world-wide-web at this URL:
|
|
* http://framework.zend.com/license/new-bsd
|
|
* If you did not receive a copy of the license and are unable to
|
|
* obtain it through the world-wide-web, please send an email
|
|
* to license@zend.com so we can send you a copy immediately.
|
|
*
|
|
* @category Zend
|
|
* @package Zend_XmlRpc
|
|
* @subpackage Server
|
|
* @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
|
|
* @license http://framework.zend.com/license/new-bsd New BSD License
|
|
* @version $Id: Server.php 13223 2008-12-14 11:21:31Z thomas $
|
|
*/
|
|
|
|
/**
|
|
* Extends Zend_Server_Abstract
|
|
*/
|
|
require_once 'Zend/Server/Abstract.php';
|
|
|
|
/**
|
|
* XMLRPC Request
|
|
*/
|
|
require_once 'Zend/XmlRpc/Request.php';
|
|
|
|
/**
|
|
* XMLRPC Response
|
|
*/
|
|
require_once 'Zend/XmlRpc/Response.php';
|
|
|
|
/**
|
|
* XMLRPC HTTP Response
|
|
*/
|
|
require_once 'Zend/XmlRpc/Response/Http.php';
|
|
|
|
/**
|
|
* XMLRPC server fault class
|
|
*/
|
|
require_once 'Zend/XmlRpc/Server/Fault.php';
|
|
|
|
/**
|
|
* XMLRPC server system methods class
|
|
*/
|
|
require_once 'Zend/XmlRpc/Server/System.php';
|
|
|
|
/**
|
|
* Convert PHP to and from xmlrpc native types
|
|
*/
|
|
require_once 'Zend/XmlRpc/Value.php';
|
|
|
|
/**
|
|
* Reflection API for function/method introspection
|
|
*/
|
|
require_once 'Zend/Server/Reflection.php';
|
|
|
|
/**
|
|
* Zend_Server_Reflection_Function_Abstract
|
|
*/
|
|
require_once 'Zend/Server/Reflection/Function/Abstract.php';
|
|
|
|
/**
|
|
* Specifically grab the Zend_Server_Reflection_Method for manually setting up
|
|
* system.* methods and handling callbacks in {@link loadFunctions()}.
|
|
*/
|
|
require_once 'Zend/Server/Reflection/Method.php';
|
|
|
|
/**
|
|
* An XML-RPC server implementation
|
|
*
|
|
* Example:
|
|
* <code>
|
|
* require_once 'Zend/XmlRpc/Server.php';
|
|
* require_once 'Zend/XmlRpc/Server/Cache.php';
|
|
* require_once 'Zend/XmlRpc/Server/Fault.php';
|
|
* require_once 'My/Exception.php';
|
|
* require_once 'My/Fault/Observer.php';
|
|
*
|
|
* // Instantiate server
|
|
* $server = new Zend_XmlRpc_Server();
|
|
*
|
|
* // Allow some exceptions to report as fault responses:
|
|
* Zend_XmlRpc_Server_Fault::attachFaultException('My_Exception');
|
|
* Zend_XmlRpc_Server_Fault::attachObserver('My_Fault_Observer');
|
|
*
|
|
* // Get or build dispatch table:
|
|
* if (!Zend_XmlRpc_Server_Cache::get($filename, $server)) {
|
|
* require_once 'Some/Service/Class.php';
|
|
* require_once 'Another/Service/Class.php';
|
|
*
|
|
* // Attach Some_Service_Class in 'some' namespace
|
|
* $server->setClass('Some_Service_Class', 'some');
|
|
*
|
|
* // Attach Another_Service_Class in 'another' namespace
|
|
* $server->setClass('Another_Service_Class', 'another');
|
|
*
|
|
* // Create dispatch table cache file
|
|
* Zend_XmlRpc_Server_Cache::save($filename, $server);
|
|
* }
|
|
*
|
|
* $response = $server->handle();
|
|
* echo $response;
|
|
* </code>
|
|
*
|
|
* @category Zend
|
|
* @package Zend_XmlRpc
|
|
* @subpackage Server
|
|
* @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
|
|
* @license http://framework.zend.com/license/new-bsd New BSD License
|
|
*/
|
|
class Zend_XmlRpc_Server extends Zend_Server_Abstract
|
|
{
|
|
/**
|
|
* Character encoding
|
|
* @var string
|
|
*/
|
|
protected $_encoding = 'UTF-8';
|
|
|
|
/**
|
|
* Request processed
|
|
* @var null|Zend_XmlRpc_Request
|
|
*/
|
|
protected $_request = null;
|
|
|
|
/**
|
|
* Class to use for responses; defaults to {@link Zend_XmlRpc_Response_Http}
|
|
* @var string
|
|
*/
|
|
protected $_responseClass = 'Zend_XmlRpc_Response_Http';
|
|
|
|
/**
|
|
* Dispatch table of name => method pairs
|
|
* @var Zend_XmlRpc_Server_ServerDefinition
|
|
*/
|
|
protected $_table;
|
|
|
|
/**
|
|
* PHP types => XML-RPC types
|
|
* @var array
|
|
*/
|
|
protected $_typeMap = array(
|
|
'i4' => 'i4',
|
|
'int' => 'int',
|
|
'integer' => 'int',
|
|
'double' => 'double',
|
|
'float' => 'double',
|
|
'real' => 'double',
|
|
'boolean' => 'boolean',
|
|
'bool' => 'boolean',
|
|
'true' => 'boolean',
|
|
'false' => 'boolean',
|
|
'string' => 'string',
|
|
'str' => 'string',
|
|
'base64' => 'base64',
|
|
'dateTime.iso8601' => 'dateTime.iso8601',
|
|
'date' => 'dateTime.iso8601',
|
|
'time' => 'dateTime.iso8601',
|
|
'time' => 'dateTime.iso8601',
|
|
'array' => 'array',
|
|
'struct' => 'struct',
|
|
'null' => 'nil',
|
|
'nil' => 'nil',
|
|
'void' => 'void',
|
|
'mixed' => 'struct'
|
|
);
|
|
|
|
/**
|
|
* Constructor
|
|
*
|
|
* Creates system.* methods.
|
|
*
|
|
* @return void
|
|
*/
|
|
public function __construct()
|
|
{
|
|
$this->_table = new Zend_Server_Definition();
|
|
$this->_registerSystemMethods();
|
|
}
|
|
|
|
/**
|
|
* Proxy calls to system object
|
|
*
|
|
* @param string $method
|
|
* @param array $params
|
|
* @return mixed
|
|
* @throws Zend_XmlRpc_Server_Exception
|
|
*/
|
|
public function __call($method, $params)
|
|
{
|
|
$system = $this->getSystem();
|
|
if (!method_exists($system, $method)) {
|
|
require_once 'Zend/XmlRpc/Server/Exception.php';
|
|
throw new Zend_XmlRpc_Server_Exception('Unknown instance method called on server: ' . $method);
|
|
}
|
|
return call_user_func_array(array($system, $method), $params);
|
|
}
|
|
|
|
/**
|
|
* Attach a callback as an XMLRPC method
|
|
*
|
|
* Attaches a callback as an XMLRPC method, prefixing the XMLRPC method name
|
|
* with $namespace, if provided. Reflection is done on the callback's
|
|
* docblock to create the methodHelp for the XMLRPC method.
|
|
*
|
|
* Additional arguments to pass to the function at dispatch may be passed;
|
|
* any arguments following the namespace will be aggregated and passed at
|
|
* dispatch time.
|
|
*
|
|
* @param string|array $function Valid callback
|
|
* @param string $namespace Optional namespace prefix
|
|
* @return void
|
|
* @throws Zend_XmlRpc_Server_Exception
|
|
*/
|
|
public function addFunction($function, $namespace = '')
|
|
{
|
|
if (!is_string($function) && !is_array($function)) {
|
|
require_once 'Zend/XmlRpc/Server/Exception.php';
|
|
throw new Zend_XmlRpc_Server_Exception('Unable to attach function; invalid', 611);
|
|
}
|
|
|
|
$argv = null;
|
|
if (2 < func_num_args()) {
|
|
$argv = func_get_args();
|
|
$argv = array_slice($argv, 2);
|
|
}
|
|
|
|
$function = (array) $function;
|
|
foreach ($function as $func) {
|
|
if (!is_string($func) || !function_exists($func)) {
|
|
require_once 'Zend/XmlRpc/Server/Exception.php';
|
|
throw new Zend_XmlRpc_Server_Exception('Unable to attach function; invalid', 611);
|
|
}
|
|
$reflection = Zend_Server_Reflection::reflectFunction($func, $argv, $namespace);
|
|
$this->_buildSignature($reflection);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Attach class methods as XMLRPC method handlers
|
|
*
|
|
* $class may be either a class name or an object. Reflection is done on the
|
|
* class or object to determine the available public methods, and each is
|
|
* attached to the server as an available method; if a $namespace has been
|
|
* provided, that namespace is used to prefix the XMLRPC method names.
|
|
*
|
|
* Any additional arguments beyond $namespace will be passed to a method at
|
|
* invocation.
|
|
*
|
|
* @param string|object $class
|
|
* @param string $namespace Optional
|
|
* @param mixed $argv Optional arguments to pass to methods
|
|
* @return void
|
|
* @throws Zend_XmlRpc_Server_Exception on invalid input
|
|
*/
|
|
public function setClass($class, $namespace = '', $argv = null)
|
|
{
|
|
if (is_string($class) && !class_exists($class)) {
|
|
if (!class_exists($class)) {
|
|
require_once 'Zend/XmlRpc/Server/Exception.php';
|
|
throw new Zend_XmlRpc_Server_Exception('Invalid method class', 610);
|
|
}
|
|
}
|
|
|
|
$argv = null;
|
|
if (3 < func_num_args()) {
|
|
$argv = func_get_args();
|
|
$argv = array_slice($argv, 3);
|
|
}
|
|
|
|
$dispatchable = Zend_Server_Reflection::reflectClass($class, $argv, $namespace);
|
|
foreach ($dispatchable->getMethods() as $reflection) {
|
|
$this->_buildSignature($reflection, $class);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Raise an xmlrpc server fault
|
|
*
|
|
* @param string|Exception $fault
|
|
* @param int $code
|
|
* @return Zend_XmlRpc_Server_Fault
|
|
*/
|
|
public function fault($fault = null, $code = 404)
|
|
{
|
|
if (!$fault instanceof Exception) {
|
|
$fault = (string) $fault;
|
|
if (empty($fault)) {
|
|
$fault = 'Unknown error';
|
|
}
|
|
require_once 'Zend/XmlRpc/Server/Exception.php';
|
|
$fault = new Zend_XmlRpc_Server_Exception($fault, $code);
|
|
}
|
|
|
|
return Zend_XmlRpc_Server_Fault::getInstance($fault);
|
|
}
|
|
|
|
/**
|
|
* Handle an xmlrpc call
|
|
*
|
|
* @param Zend_XmlRpc_Request $request Optional
|
|
* @return Zend_XmlRpc_Response|Zend_XmlRpc_Fault
|
|
*/
|
|
public function handle($request = false)
|
|
{
|
|
// Get request
|
|
if ((!$request || !$request instanceof Zend_XmlRpc_Request)
|
|
&& (null === ($request = $this->getRequest()))
|
|
) {
|
|
require_once 'Zend/XmlRpc/Request/Http.php';
|
|
$request = new Zend_XmlRpc_Request_Http();
|
|
$request->setEncoding($this->getEncoding());
|
|
}
|
|
|
|
$this->setRequest($request);
|
|
|
|
if ($request->isFault()) {
|
|
$response = $request->getFault();
|
|
} else {
|
|
try {
|
|
$response = $this->_handle($request);
|
|
} catch (Exception $e) {
|
|
$response = $this->fault($e);
|
|
}
|
|
}
|
|
|
|
// Set output encoding
|
|
$response->setEncoding($this->getEncoding());
|
|
|
|
return $response;
|
|
}
|
|
|
|
/**
|
|
* Load methods as returned from {@link getFunctions}
|
|
*
|
|
* Typically, you will not use this method; it will be called using the
|
|
* results pulled from {@link Zend_XmlRpc_Server_Cache::get()}.
|
|
*
|
|
* @param array|Zend_Server_Definition $definition
|
|
* @return void
|
|
* @throws Zend_XmlRpc_Server_Exception on invalid input
|
|
*/
|
|
public function loadFunctions($definition)
|
|
{
|
|
if (!is_array($definition) && (!$definition instanceof Zend_Server_Definition)) {
|
|
if (is_object($definition)) {
|
|
$type = get_class($definition);
|
|
} else {
|
|
$type = gettype($definition);
|
|
}
|
|
require_once 'Zend/XmlRpc/Server/Exception.php';
|
|
throw new Zend_XmlRpc_Server_Exception('Unable to load server definition; must be an array or Zend_Server_Definition, received ' . $type, 612);
|
|
}
|
|
|
|
$this->_table->clearMethods();
|
|
$this->_registerSystemMethods();
|
|
|
|
if ($definition instanceof Zend_Server_Definition) {
|
|
$definition = $definition->getMethods();
|
|
}
|
|
|
|
foreach ($definition as $key => $method) {
|
|
if ('system.' == substr($key, 0, 7)) {
|
|
continue;
|
|
}
|
|
$this->_table->addMethod($method, $key);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set encoding
|
|
*
|
|
* @param string $encoding
|
|
* @return Zend_XmlRpc_Server
|
|
*/
|
|
public function setEncoding($encoding)
|
|
{
|
|
$this->_encoding = $encoding;
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Retrieve current encoding
|
|
*
|
|
* @return string
|
|
*/
|
|
public function getEncoding()
|
|
{
|
|
return $this->_encoding;
|
|
}
|
|
|
|
/**
|
|
* Do nothing; persistence is handled via {@link Zend_XmlRpc_Server_Cache}
|
|
*
|
|
* @param mixed $mode
|
|
* @return void
|
|
*/
|
|
public function setPersistence($mode)
|
|
{
|
|
}
|
|
|
|
/**
|
|
* Set the request object
|
|
*
|
|
* @param string|Zend_XmlRpc_Request $request
|
|
* @return Zend_XmlRpc_Server
|
|
* @throws Zend_XmlRpc_Server_Exception on invalid request class or object
|
|
*/
|
|
public function setRequest($request)
|
|
{
|
|
if (is_string($request) && class_exists($request)) {
|
|
$request = new $request();
|
|
if (!$request instanceof Zend_XmlRpc_Request) {
|
|
require_once 'Zend/XmlRpc/Server/Exception.php';
|
|
throw new Zend_XmlRpc_Server_Exception('Invalid request class');
|
|
}
|
|
$request->setEncoding($this->getEncoding());
|
|
} elseif (!$request instanceof Zend_XmlRpc_Request) {
|
|
require_once 'Zend/XmlRpc/Server/Exception.php';
|
|
throw new Zend_XmlRpc_Server_Exception('Invalid request object');
|
|
}
|
|
|
|
$this->_request = $request;
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Return currently registered request object
|
|
*
|
|
* @return null|Zend_XmlRpc_Request
|
|
*/
|
|
public function getRequest()
|
|
{
|
|
return $this->_request;
|
|
}
|
|
|
|
/**
|
|
* Set the class to use for the response
|
|
*
|
|
* @param string $class
|
|
* @return boolean True if class was set, false if not
|
|
*/
|
|
public function setResponseClass($class)
|
|
{
|
|
if (class_exists($class)) {
|
|
$reflection = new ReflectionClass($class);
|
|
if ($reflection->isSubclassOf(new ReflectionClass('Zend_XmlRpc_Response'))) {
|
|
$this->_responseClass = $class;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Retrieve current response class
|
|
*
|
|
* @return string
|
|
*/
|
|
public function getResponseClass()
|
|
{
|
|
return $this->_responseClass;
|
|
}
|
|
|
|
/**
|
|
* Retrieve dispatch table
|
|
*
|
|
* @return array
|
|
*/
|
|
public function getDispatchTable()
|
|
{
|
|
return $this->_table;
|
|
}
|
|
|
|
/**
|
|
* Returns a list of registered methods
|
|
*
|
|
* Returns an array of dispatchables (Zend_Server_Reflection_Function,
|
|
* _Method, and _Class items).
|
|
*
|
|
* @return array
|
|
*/
|
|
public function getFunctions()
|
|
{
|
|
return $this->_table->toArray();
|
|
}
|
|
|
|
/**
|
|
* Retrieve system object
|
|
*
|
|
* @return Zend_XmlRpc_Server_System
|
|
*/
|
|
public function getSystem()
|
|
{
|
|
return $this->_system;
|
|
}
|
|
|
|
/**
|
|
* Map PHP type to XML-RPC type
|
|
*
|
|
* @param string $type
|
|
* @return string
|
|
*/
|
|
protected function _fixType($type)
|
|
{
|
|
if (isset($this->_typeMap[$type])) {
|
|
return $this->_typeMap[$type];
|
|
}
|
|
return 'void';
|
|
}
|
|
|
|
/**
|
|
* Handle an xmlrpc call (actual work)
|
|
*
|
|
* @param Zend_XmlRpc_Request $request
|
|
* @return Zend_XmlRpc_Response
|
|
* @throws Zend_XmlRpcServer_Exception|Exception
|
|
* Zend_XmlRpcServer_Exceptions are thrown for internal errors; otherwise,
|
|
* any other exception may be thrown by the callback
|
|
*/
|
|
protected function _handle(Zend_XmlRpc_Request $request)
|
|
{
|
|
$method = $request->getMethod();
|
|
|
|
// Check for valid method
|
|
if (!$this->_table->hasMethod($method)) {
|
|
require_once 'Zend/XmlRpc/Server/Exception.php';
|
|
throw new Zend_XmlRpc_Server_Exception('Method "' . $method . '" does not exist', 620);
|
|
}
|
|
|
|
$info = $this->_table->getMethod($method);
|
|
$params = $request->getParams();
|
|
$argv = $info->getInvokeArguments();
|
|
if (0 < count($argv)) {
|
|
$params = array_merge($params, $argv);
|
|
}
|
|
|
|
// Check calling parameters against signatures
|
|
$matched = false;
|
|
$sigCalled = $request->getTypes();
|
|
|
|
$sigLength = count($sigCalled);
|
|
$paramsLen = count($params);
|
|
if ($sigLength < $paramsLen) {
|
|
for ($i = $sigLength; $i < $paramsLen; ++$i) {
|
|
$xmlRpcValue = Zend_XmlRpc_Value::getXmlRpcValue($params[$i]);
|
|
$sigCalled[] = $xmlRpcValue->getType();
|
|
}
|
|
}
|
|
|
|
$signatures = $info->getPrototypes();
|
|
foreach ($signatures as $signature) {
|
|
$sigParams = $signature->getParameters();
|
|
if ($sigCalled === $sigParams) {
|
|
$matched = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!$matched) {
|
|
require_once 'Zend/XmlRpc/Server/Exception.php';
|
|
throw new Zend_XmlRpc_Server_Exception('Calling parameters do not match signature', 623);
|
|
}
|
|
|
|
$return = $this->_dispatch($info, $params);
|
|
$responseClass = $this->getResponseClass();
|
|
return new $responseClass($return);
|
|
}
|
|
|
|
/**
|
|
* Register system methods with the server
|
|
*
|
|
* @return void
|
|
*/
|
|
protected function _registerSystemMethods()
|
|
{
|
|
$system = new Zend_XmlRpc_Server_System($this);
|
|
$this->_system = $system;
|
|
$this->setClass($system, 'system');
|
|
}
|
|
}
|