diff -r 000000000000 -r 4eba9c11703f web/Zend/XmlRpc/Server.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/Zend/XmlRpc/Server.php Mon Dec 13 18:29:26 2010 +0100 @@ -0,0 +1,615 @@ + + * 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; + * + * + * @category Zend + * @package Zend_XmlRpc + * @subpackage Server + * @copyright Copyright (c) 2005-2010 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_Server_Definition + */ + protected $_table; + + /** + * PHP types => XML-RPC types + * @var array + */ + protected $_typeMap = array( + 'i4' => 'i4', + 'int' => 'int', + 'integer' => 'int', + 'Zend_Crypt_Math_BigInteger' => 'i8', + 'i8' => 'i8', + 'ex:i8' => 'i8', + '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', + 'Zend_Date' => 'dateTime.iso8601', + 'DateTime' => 'dateTime.iso8601', + 'array' => 'array', + 'struct' => 'struct', + 'null' => 'nil', + 'nil' => 'nil', + 'ex:nil' => 'nil', + 'void' => 'void', + 'mixed' => 'struct', + ); + + /** + * Send arguments to all methods or just constructor? + * + * @var bool + */ + protected $_sendArgumentsToAllMethods = true; + + /** + * 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)) { + require_once 'Zend/XmlRpc/Server/Exception.php'; + throw new Zend_XmlRpc_Server_Exception('Invalid method class', 610); + } + + $argv = null; + if (2 < func_num_args()) { + $argv = func_get_args(); + $argv = array_slice($argv, 2); + } + + $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; + Zend_XmlRpc_Value::setEncoding($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) or + ($c = new ReflectionClass($class) and !$c->isSubclassOf('Zend_XmlRpc_Response'))) { + + require_once 'Zend/XmlRpc/Server/Exception.php'; + throw new Zend_XmlRpc_Server_Exception('Invalid response class'); + } + $this->_responseClass = $class; + return true; + } + + /** + * 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; + } + + /** + * Send arguments to all methods? + * + * If setClass() is used to add classes to the server, this flag defined + * how to handle arguments. If set to true, all methods including constructor + * will receive the arguments. If set to false, only constructor will receive the + * arguments + */ + public function sendArgumentsToAllMethods($flag = null) + { + if ($flag === null) { + return $this->_sendArgumentsToAllMethods; + } + + $this->_sendArgumentsToAllMethods = (bool)$flag; + return $this; + } + + /** + * 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) and $this->sendArgumentsToAllMethods()) { + $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'); + } +}