7 * This source file is subject to the new BSD license that is bundled
8 * with this package in the file LICENSE.txt.
9 * It is also available through the world-wide-web at this URL:
10 * http://framework.zend.com/license/new-bsd
11 * If you did not receive a copy of the license and are unable to
12 * obtain it through the world-wide-web, please send an email
13 * to license@zend.com so we can send you a copy immediately.
16 * @package Zend_XmlRpc
18 * @copyright Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
19 * @license http://framework.zend.com/license/new-bsd New BSD License
20 * @version $Id: Server.php 17786 2009-08-23 22:26:33Z lars $
24 * Extends Zend_Server_Abstract
26 require_once 'Zend/Server/Abstract.php';
31 require_once 'Zend/XmlRpc/Request.php';
36 require_once 'Zend/XmlRpc/Response.php';
39 * XMLRPC HTTP Response
41 require_once 'Zend/XmlRpc/Response/Http.php';
44 * XMLRPC server fault class
46 require_once 'Zend/XmlRpc/Server/Fault.php';
49 * XMLRPC server system methods class
51 require_once 'Zend/XmlRpc/Server/System.php';
54 * Convert PHP to and from xmlrpc native types
56 require_once 'Zend/XmlRpc/Value.php';
59 * Reflection API for function/method introspection
61 require_once 'Zend/Server/Reflection.php';
64 * Zend_Server_Reflection_Function_Abstract
66 require_once 'Zend/Server/Reflection/Function/Abstract.php';
69 * Specifically grab the Zend_Server_Reflection_Method for manually setting up
70 * system.* methods and handling callbacks in {@link loadFunctions()}.
72 require_once 'Zend/Server/Reflection/Method.php';
75 * An XML-RPC server implementation
79 * require_once 'Zend/XmlRpc/Server.php';
80 * require_once 'Zend/XmlRpc/Server/Cache.php';
81 * require_once 'Zend/XmlRpc/Server/Fault.php';
82 * require_once 'My/Exception.php';
83 * require_once 'My/Fault/Observer.php';
85 * // Instantiate server
86 * $server = new Zend_XmlRpc_Server();
88 * // Allow some exceptions to report as fault responses:
89 * Zend_XmlRpc_Server_Fault::attachFaultException('My_Exception');
90 * Zend_XmlRpc_Server_Fault::attachObserver('My_Fault_Observer');
92 * // Get or build dispatch table:
93 * if (!Zend_XmlRpc_Server_Cache::get($filename, $server)) {
94 * require_once 'Some/Service/Class.php';
95 * require_once 'Another/Service/Class.php';
97 * // Attach Some_Service_Class in 'some' namespace
98 * $server->setClass('Some_Service_Class', 'some');
100 * // Attach Another_Service_Class in 'another' namespace
101 * $server->setClass('Another_Service_Class', 'another');
103 * // Create dispatch table cache file
104 * Zend_XmlRpc_Server_Cache::save($filename, $server);
107 * $response = $server->handle();
112 * @package Zend_XmlRpc
114 * @copyright Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
115 * @license http://framework.zend.com/license/new-bsd New BSD License
117 class Zend_XmlRpc_Server
extends Zend_Server_Abstract
123 protected $_encoding = 'UTF-8';
127 * @var null|Zend_XmlRpc_Request
129 protected $_request = null;
132 * Class to use for responses; defaults to {@link Zend_XmlRpc_Response_Http}
135 protected $_responseClass = 'Zend_XmlRpc_Response_Http';
138 * Dispatch table of name => method pairs
139 * @var Zend_Server_Definition
144 * PHP types => XML-RPC types
147 protected $_typeMap = array(
151 'double' => 'double',
154 'boolean' => 'boolean',
157 'false' => 'boolean',
158 'string' => 'string',
160 'base64' => 'base64',
161 'dateTime.iso8601' => 'dateTime.iso8601',
162 'date' => 'dateTime.iso8601',
163 'time' => 'dateTime.iso8601',
164 'time' => 'dateTime.iso8601',
166 'struct' => 'struct',
174 * Send arguments to all methods or just constructor?
178 protected $_sendArgumentsToAllMethods = true;
183 * Creates system.* methods.
187 public function __construct()
189 $this->_table
= new Zend_Server_Definition();
190 $this->_registerSystemMethods();
194 * Proxy calls to system object
196 * @param string $method
197 * @param array $params
199 * @throws Zend_XmlRpc_Server_Exception
201 public function __call($method, $params)
203 $system = $this->getSystem();
204 if (!method_exists($system, $method)) {
205 require_once 'Zend/XmlRpc/Server/Exception.php';
206 throw new Zend_XmlRpc_Server_Exception('Unknown instance method called on server: ' . $method);
208 return call_user_func_array(array($system, $method), $params);
212 * Attach a callback as an XMLRPC method
214 * Attaches a callback as an XMLRPC method, prefixing the XMLRPC method name
215 * with $namespace, if provided. Reflection is done on the callback's
216 * docblock to create the methodHelp for the XMLRPC method.
218 * Additional arguments to pass to the function at dispatch may be passed;
219 * any arguments following the namespace will be aggregated and passed at
222 * @param string|array $function Valid callback
223 * @param string $namespace Optional namespace prefix
225 * @throws Zend_XmlRpc_Server_Exception
227 public function addFunction($function, $namespace = '')
229 if (!is_string($function) && !is_array($function)) {
230 require_once 'Zend/XmlRpc/Server/Exception.php';
231 throw new Zend_XmlRpc_Server_Exception('Unable to attach function; invalid', 611);
235 if (2 < func_num_args()) {
236 $argv = func_get_args();
237 $argv = array_slice($argv, 2);
240 $function = (array) $function;
241 foreach ($function as $func) {
242 if (!is_string($func) ||
!function_exists($func)) {
243 require_once 'Zend/XmlRpc/Server/Exception.php';
244 throw new Zend_XmlRpc_Server_Exception('Unable to attach function; invalid', 611);
246 $reflection = Zend_Server_Reflection
::reflectFunction($func, $argv, $namespace);
247 $this->_buildSignature($reflection);
252 * Attach class methods as XMLRPC method handlers
254 * $class may be either a class name or an object. Reflection is done on the
255 * class or object to determine the available public methods, and each is
256 * attached to the server as an available method; if a $namespace has been
257 * provided, that namespace is used to prefix the XMLRPC method names.
259 * Any additional arguments beyond $namespace will be passed to a method at
262 * @param string|object $class
263 * @param string $namespace Optional
264 * @param mixed $argv Optional arguments to pass to methods
266 * @throws Zend_XmlRpc_Server_Exception on invalid input
268 public function setClass($class, $namespace = '', $argv = null)
270 if (is_string($class) && !class_exists($class)) {
271 require_once 'Zend/XmlRpc/Server/Exception.php';
272 throw new Zend_XmlRpc_Server_Exception('Invalid method class', 610);
276 if (2 < func_num_args()) {
277 $argv = func_get_args();
278 $argv = array_slice($argv, 2);
281 $dispatchable = Zend_Server_Reflection
::reflectClass($class, $argv, $namespace);
282 foreach ($dispatchable->getMethods() as $reflection) {
283 $this->_buildSignature($reflection, $class);
288 * Raise an xmlrpc server fault
290 * @param string|Exception $fault
292 * @return Zend_XmlRpc_Server_Fault
294 public function fault($fault = null, $code = 404)
296 if (!$fault instanceof Exception
) {
297 $fault = (string) $fault;
299 $fault = 'Unknown Error';
301 require_once 'Zend/XmlRpc/Server/Exception.php';
302 $fault = new Zend_XmlRpc_Server_Exception($fault, $code);
305 return Zend_XmlRpc_Server_Fault
::getInstance($fault);
309 * Handle an xmlrpc call
311 * @param Zend_XmlRpc_Request $request Optional
312 * @return Zend_XmlRpc_Response|Zend_XmlRpc_Fault
314 public function handle($request = false)
317 if ((!$request ||
!$request instanceof Zend_XmlRpc_Request
)
318 && (null === ($request = $this->getRequest()))
320 require_once 'Zend/XmlRpc/Request/Http.php';
321 $request = new Zend_XmlRpc_Request_Http();
322 $request->setEncoding($this->getEncoding());
325 $this->setRequest($request);
327 if ($request->isFault()) {
328 $response = $request->getFault();
331 $response = $this->_handle($request);
332 } catch (Exception
$e) {
333 $response = $this->fault($e);
337 // Set output encoding
338 $response->setEncoding($this->getEncoding());
344 * Load methods as returned from {@link getFunctions}
346 * Typically, you will not use this method; it will be called using the
347 * results pulled from {@link Zend_XmlRpc_Server_Cache::get()}.
349 * @param array|Zend_Server_Definition $definition
351 * @throws Zend_XmlRpc_Server_Exception on invalid input
353 public function loadFunctions($definition)
355 if (!is_array($definition) && (!$definition instanceof Zend_Server_Definition
)) {
356 if (is_object($definition)) {
357 $type = get_class($definition);
359 $type = gettype($definition);
361 require_once 'Zend/XmlRpc/Server/Exception.php';
362 throw new Zend_XmlRpc_Server_Exception('Unable to load server definition; must be an array or Zend_Server_Definition, received ' . $type, 612);
365 $this->_table
->clearMethods();
366 $this->_registerSystemMethods();
368 if ($definition instanceof Zend_Server_Definition
) {
369 $definition = $definition->getMethods();
372 foreach ($definition as $key => $method) {
373 if ('system.' == substr($key, 0, 7)) {
376 $this->_table
->addMethod($method, $key);
383 * @param string $encoding
384 * @return Zend_XmlRpc_Server
386 public function setEncoding($encoding)
388 $this->_encoding
= $encoding;
393 * Retrieve current encoding
397 public function getEncoding()
399 return $this->_encoding
;
403 * Do nothing; persistence is handled via {@link Zend_XmlRpc_Server_Cache}
408 public function setPersistence($mode)
413 * Set the request object
415 * @param string|Zend_XmlRpc_Request $request
416 * @return Zend_XmlRpc_Server
417 * @throws Zend_XmlRpc_Server_Exception on invalid request class or object
419 public function setRequest($request)
421 if (is_string($request) && class_exists($request)) {
422 $request = new $request();
423 if (!$request instanceof Zend_XmlRpc_Request
) {
424 require_once 'Zend/XmlRpc/Server/Exception.php';
425 throw new Zend_XmlRpc_Server_Exception('Invalid request class');
427 $request->setEncoding($this->getEncoding());
428 } elseif (!$request instanceof Zend_XmlRpc_Request
) {
429 require_once 'Zend/XmlRpc/Server/Exception.php';
430 throw new Zend_XmlRpc_Server_Exception('Invalid request object');
433 $this->_request
= $request;
438 * Return currently registered request object
440 * @return null|Zend_XmlRpc_Request
442 public function getRequest()
444 return $this->_request
;
448 * Set the class to use for the response
450 * @param string $class
451 * @return boolean True if class was set, false if not
453 public function setResponseClass($class)
455 if (!class_exists($class) or
456 ($c = new ReflectionClass($class) and !$c->isSubclassOf('Zend_XmlRpc_Response'))) {
458 require_once 'Zend/XmlRpc/Server/Exception.php';
459 throw new Zend_XmlRpc_Server_Exception('Invalid response class');
461 $this->_responseClass
= $class;
466 * Retrieve current response class
470 public function getResponseClass()
472 return $this->_responseClass
;
476 * Retrieve dispatch table
480 public function getDispatchTable()
482 return $this->_table
;
486 * Returns a list of registered methods
488 * Returns an array of dispatchables (Zend_Server_Reflection_Function,
489 * _Method, and _Class items).
493 public function getFunctions()
495 return $this->_table
->toArray();
499 * Retrieve system object
501 * @return Zend_XmlRpc_Server_System
503 public function getSystem()
505 return $this->_system
;
509 * Send arguments to all methods?
511 * If setClass() is used to add classes to the server, this flag defined
512 * how to handle arguments. If set to true, all methods including constructor
513 * will receive the arguments. If set to false, only constructor will receive the
516 public function sendArgumentsToAllMethods($flag = null)
518 if ($flag === null) {
519 return $this->_sendArgumentsToAllMethods
;
522 $this->_sendArgumentsToAllMethods
= (bool)$flag;
527 * Map PHP type to XML-RPC type
529 * @param string $type
532 protected function _fixType($type)
534 if (isset($this->_typeMap
[$type])) {
535 return $this->_typeMap
[$type];
541 * Handle an xmlrpc call (actual work)
543 * @param Zend_XmlRpc_Request $request
544 * @return Zend_XmlRpc_Response
545 * @throws Zend_XmlRpcServer_Exception|Exception
546 * Zend_XmlRpcServer_Exceptions are thrown for internal errors; otherwise,
547 * any other exception may be thrown by the callback
549 protected function _handle(Zend_XmlRpc_Request
$request)
551 $method = $request->getMethod();
553 // Check for valid method
554 if (!$this->_table
->hasMethod($method)) {
555 require_once 'Zend/XmlRpc/Server/Exception.php';
556 throw new Zend_XmlRpc_Server_Exception('Method "' . $method . '" does not exist', 620);
559 $info = $this->_table
->getMethod($method);
560 $params = $request->getParams();
561 $argv = $info->getInvokeArguments();
562 if (0 < count($argv) and $this->sendArgumentsToAllMethods()) {
563 $params = array_merge($params, $argv);
566 // Check calling parameters against signatures
568 $sigCalled = $request->getTypes();
570 $sigLength = count($sigCalled);
571 $paramsLen = count($params);
572 if ($sigLength < $paramsLen) {
573 for ($i = $sigLength; $i < $paramsLen; ++
$i) {
574 $xmlRpcValue = Zend_XmlRpc_Value
::getXmlRpcValue($params[$i]);
575 $sigCalled[] = $xmlRpcValue->getType();
579 $signatures = $info->getPrototypes();
580 foreach ($signatures as $signature) {
581 $sigParams = $signature->getParameters();
582 if ($sigCalled === $sigParams) {
588 require_once 'Zend/XmlRpc/Server/Exception.php';
589 throw new Zend_XmlRpc_Server_Exception('Calling parameters do not match signature', 623);
592 $return = $this->_dispatch($info, $params);
593 $responseClass = $this->getResponseClass();
594 return new $responseClass($return);
598 * Register system methods with the server
602 protected function _registerSystemMethods()
604 $system = new Zend_XmlRpc_Server_System($this);
605 $this->_system
= $system;
606 $this->setClass($system, 'system');