File indexing completed on 2024-12-22 05:37:14
0001 <?php 0002 /** 0003 * Zend Framework 0004 * 0005 * LICENSE 0006 * 0007 * This source file is subject to the new BSD license that is bundled 0008 * with this package in the file LICENSE.txt. 0009 * It is also available through the world-wide-web at this URL: 0010 * http://framework.zend.com/license/new-bsd 0011 * If you did not receive a copy of the license and are unable to 0012 * obtain it through the world-wide-web, please send an email 0013 * to license@zend.com so we can send you a copy immediately. 0014 * 0015 * @category Zend 0016 * @package Zend_XmlRpc 0017 * @subpackage Server 0018 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0019 * @license http://framework.zend.com/license/new-bsd New BSD License 0020 * @version $Id$ 0021 */ 0022 0023 /** 0024 * Extends Zend_Server_Abstract 0025 */ 0026 // require_once 'Zend/Server/Abstract.php'; 0027 0028 /** 0029 * XMLRPC Request 0030 */ 0031 // require_once 'Zend/XmlRpc/Request.php'; 0032 0033 /** 0034 * XMLRPC Response 0035 */ 0036 // require_once 'Zend/XmlRpc/Response.php'; 0037 0038 /** 0039 * XMLRPC HTTP Response 0040 */ 0041 // require_once 'Zend/XmlRpc/Response/Http.php'; 0042 0043 /** 0044 * XMLRPC server fault class 0045 */ 0046 // require_once 'Zend/XmlRpc/Server/Fault.php'; 0047 0048 /** 0049 * XMLRPC server system methods class 0050 */ 0051 // require_once 'Zend/XmlRpc/Server/System.php'; 0052 0053 /** 0054 * Convert PHP to and from xmlrpc native types 0055 */ 0056 // require_once 'Zend/XmlRpc/Value.php'; 0057 0058 /** 0059 * Reflection API for function/method introspection 0060 */ 0061 // require_once 'Zend/Server/Reflection.php'; 0062 0063 /** 0064 * Zend_Server_Reflection_Function_Abstract 0065 */ 0066 // require_once 'Zend/Server/Reflection/Function/Abstract.php'; 0067 0068 /** 0069 * Specifically grab the Zend_Server_Reflection_Method for manually setting up 0070 * system.* methods and handling callbacks in {@link loadFunctions()}. 0071 */ 0072 // require_once 'Zend/Server/Reflection/Method.php'; 0073 0074 /** 0075 * An XML-RPC server implementation 0076 * 0077 * Example: 0078 * <code> 0079 * // require_once 'Zend/XmlRpc/Server.php'; 0080 * // require_once 'Zend/XmlRpc/Server/Cache.php'; 0081 * // require_once 'Zend/XmlRpc/Server/Fault.php'; 0082 * // require_once 'My/Exception.php'; 0083 * // require_once 'My/Fault/Observer.php'; 0084 * 0085 * // Instantiate server 0086 * $server = new Zend_XmlRpc_Server(); 0087 * 0088 * // Allow some exceptions to report as fault responses: 0089 * Zend_XmlRpc_Server_Fault::attachFaultException('My_Exception'); 0090 * Zend_XmlRpc_Server_Fault::attachObserver('My_Fault_Observer'); 0091 * 0092 * // Get or build dispatch table: 0093 * if (!Zend_XmlRpc_Server_Cache::get($filename, $server)) { 0094 * // require_once 'Some/Service/Class.php'; 0095 * // require_once 'Another/Service/Class.php'; 0096 * 0097 * // Attach Some_Service_Class in 'some' namespace 0098 * $server->setClass('Some_Service_Class', 'some'); 0099 * 0100 * // Attach Another_Service_Class in 'another' namespace 0101 * $server->setClass('Another_Service_Class', 'another'); 0102 * 0103 * // Create dispatch table cache file 0104 * Zend_XmlRpc_Server_Cache::save($filename, $server); 0105 * } 0106 * 0107 * $response = $server->handle(); 0108 * echo $response; 0109 * </code> 0110 * 0111 * @category Zend 0112 * @package Zend_XmlRpc 0113 * @subpackage Server 0114 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0115 * @license http://framework.zend.com/license/new-bsd New BSD License 0116 */ 0117 class Zend_XmlRpc_Server extends Zend_Server_Abstract 0118 { 0119 /** 0120 * Character encoding 0121 * @var string 0122 */ 0123 protected $_encoding = 'UTF-8'; 0124 0125 /** 0126 * Request processed 0127 * @var null|Zend_XmlRpc_Request 0128 */ 0129 protected $_request = null; 0130 0131 /** 0132 * Class to use for responses; defaults to {@link Zend_XmlRpc_Response_Http} 0133 * @var string 0134 */ 0135 protected $_responseClass = 'Zend_XmlRpc_Response_Http'; 0136 0137 /** 0138 * Dispatch table of name => method pairs 0139 * @var Zend_Server_Definition 0140 */ 0141 protected $_table; 0142 0143 /** 0144 * PHP types => XML-RPC types 0145 * @var array 0146 */ 0147 protected $_typeMap = array( 0148 'i4' => 'i4', 0149 'int' => 'int', 0150 'integer' => 'int', 0151 'Zend_Crypt_Math_BigInteger' => 'i8', 0152 'i8' => 'i8', 0153 'ex:i8' => 'i8', 0154 'double' => 'double', 0155 'float' => 'double', 0156 'real' => 'double', 0157 'boolean' => 'boolean', 0158 'bool' => 'boolean', 0159 'true' => 'boolean', 0160 'false' => 'boolean', 0161 'string' => 'string', 0162 'str' => 'string', 0163 'base64' => 'base64', 0164 'dateTime.iso8601' => 'dateTime.iso8601', 0165 'date' => 'dateTime.iso8601', 0166 'time' => 'dateTime.iso8601', 0167 'time' => 'dateTime.iso8601', 0168 'Zend_Date' => 'dateTime.iso8601', 0169 'DateTime' => 'dateTime.iso8601', 0170 'array' => 'array', 0171 'struct' => 'struct', 0172 'null' => 'nil', 0173 'nil' => 'nil', 0174 'ex:nil' => 'nil', 0175 'void' => 'void', 0176 'mixed' => 'struct', 0177 ); 0178 0179 /** 0180 * Send arguments to all methods or just constructor? 0181 * 0182 * @var bool 0183 */ 0184 protected $_sendArgumentsToAllMethods = true; 0185 0186 /** 0187 * Constructor 0188 * 0189 * Creates system.* methods. 0190 * 0191 * @return void 0192 */ 0193 public function __construct() 0194 { 0195 $this->_table = new Zend_Server_Definition(); 0196 $this->_registerSystemMethods(); 0197 } 0198 0199 /** 0200 * Proxy calls to system object 0201 * 0202 * @param string $method 0203 * @param array $params 0204 * @return mixed 0205 * @throws Zend_XmlRpc_Server_Exception 0206 */ 0207 public function __call($method, $params) 0208 { 0209 $system = $this->getSystem(); 0210 if (!method_exists($system, $method)) { 0211 // require_once 'Zend/XmlRpc/Server/Exception.php'; 0212 throw new Zend_XmlRpc_Server_Exception('Unknown instance method called on server: ' . $method); 0213 } 0214 return call_user_func_array(array($system, $method), $params); 0215 } 0216 0217 /** 0218 * Attach a callback as an XMLRPC method 0219 * 0220 * Attaches a callback as an XMLRPC method, prefixing the XMLRPC method name 0221 * with $namespace, if provided. Reflection is done on the callback's 0222 * docblock to create the methodHelp for the XMLRPC method. 0223 * 0224 * Additional arguments to pass to the function at dispatch may be passed; 0225 * any arguments following the namespace will be aggregated and passed at 0226 * dispatch time. 0227 * 0228 * @param string|array $function Valid callback 0229 * @param string $namespace Optional namespace prefix 0230 * @return void 0231 * @throws Zend_XmlRpc_Server_Exception 0232 */ 0233 public function addFunction($function, $namespace = '') 0234 { 0235 if (!is_string($function) && !is_array($function)) { 0236 // require_once 'Zend/XmlRpc/Server/Exception.php'; 0237 throw new Zend_XmlRpc_Server_Exception('Unable to attach function; invalid', 611); 0238 } 0239 0240 $argv = null; 0241 if (2 < func_num_args()) { 0242 $argv = func_get_args(); 0243 $argv = array_slice($argv, 2); 0244 } 0245 0246 $function = (array) $function; 0247 foreach ($function as $func) { 0248 if (!is_string($func) || !function_exists($func)) { 0249 // require_once 'Zend/XmlRpc/Server/Exception.php'; 0250 throw new Zend_XmlRpc_Server_Exception('Unable to attach function; invalid', 611); 0251 } 0252 $reflection = Zend_Server_Reflection::reflectFunction($func, $argv, $namespace); 0253 $this->_buildSignature($reflection); 0254 } 0255 } 0256 0257 /** 0258 * Attach class methods as XMLRPC method handlers 0259 * 0260 * $class may be either a class name or an object. Reflection is done on the 0261 * class or object to determine the available public methods, and each is 0262 * attached to the server as an available method; if a $namespace has been 0263 * provided, that namespace is used to prefix the XMLRPC method names. 0264 * 0265 * Any additional arguments beyond $namespace will be passed to a method at 0266 * invocation. 0267 * 0268 * @param string|object $class 0269 * @param string $namespace Optional 0270 * @param mixed $argv Optional arguments to pass to methods 0271 * @return void 0272 * @throws Zend_XmlRpc_Server_Exception on invalid input 0273 */ 0274 public function setClass($class, $namespace = '', $argv = null) 0275 { 0276 if (is_string($class) && !class_exists($class)) { 0277 // require_once 'Zend/XmlRpc/Server/Exception.php'; 0278 throw new Zend_XmlRpc_Server_Exception('Invalid method class', 610); 0279 } 0280 0281 $args = null; 0282 if (2 < func_num_args()) { 0283 $args = func_get_args(); 0284 $args = array_slice($args, 2); 0285 } 0286 0287 $dispatchable = Zend_Server_Reflection::reflectClass($class, $args, $namespace); 0288 foreach ($dispatchable->getMethods() as $reflection) { 0289 $this->_buildSignature($reflection, $class); 0290 } 0291 } 0292 0293 /** 0294 * Raise an xmlrpc server fault 0295 * 0296 * @param string|Exception $fault 0297 * @param int $code 0298 * @return Zend_XmlRpc_Server_Fault 0299 */ 0300 public function fault($fault = null, $code = 404) 0301 { 0302 if (!$fault instanceof Exception) { 0303 $fault = (string) $fault; 0304 if (empty($fault)) { 0305 $fault = 'Unknown Error'; 0306 } 0307 // require_once 'Zend/XmlRpc/Server/Exception.php'; 0308 $fault = new Zend_XmlRpc_Server_Exception($fault, $code); 0309 } 0310 0311 return Zend_XmlRpc_Server_Fault::getInstance($fault); 0312 } 0313 0314 /** 0315 * Handle an xmlrpc call 0316 * 0317 * @param Zend_XmlRpc_Request $request Optional 0318 * @return Zend_XmlRpc_Response|Zend_XmlRpc_Fault 0319 */ 0320 public function handle($request = false) 0321 { 0322 // Get request 0323 if ((!$request || !$request instanceof Zend_XmlRpc_Request) 0324 && (null === ($request = $this->getRequest())) 0325 ) { 0326 // require_once 'Zend/XmlRpc/Request/Http.php'; 0327 $request = new Zend_XmlRpc_Request_Http(); 0328 $request->setEncoding($this->getEncoding()); 0329 } 0330 0331 $this->setRequest($request); 0332 0333 if ($request->isFault()) { 0334 $response = $request->getFault(); 0335 } else { 0336 try { 0337 $response = $this->_handle($request); 0338 } catch (Exception $e) { 0339 $response = $this->fault($e); 0340 } 0341 } 0342 0343 // Set output encoding 0344 $response->setEncoding($this->getEncoding()); 0345 0346 return $response; 0347 } 0348 0349 /** 0350 * Load methods as returned from {@link getFunctions} 0351 * 0352 * Typically, you will not use this method; it will be called using the 0353 * results pulled from {@link Zend_XmlRpc_Server_Cache::get()}. 0354 * 0355 * @param array|Zend_Server_Definition $definition 0356 * @return void 0357 * @throws Zend_XmlRpc_Server_Exception on invalid input 0358 */ 0359 public function loadFunctions($definition) 0360 { 0361 if (!is_array($definition) && (!$definition instanceof Zend_Server_Definition)) { 0362 if (is_object($definition)) { 0363 $type = get_class($definition); 0364 } else { 0365 $type = gettype($definition); 0366 } 0367 // require_once 'Zend/XmlRpc/Server/Exception.php'; 0368 throw new Zend_XmlRpc_Server_Exception('Unable to load server definition; must be an array or Zend_Server_Definition, received ' . $type, 612); 0369 } 0370 0371 $this->_table->clearMethods(); 0372 $this->_registerSystemMethods(); 0373 0374 if ($definition instanceof Zend_Server_Definition) { 0375 $definition = $definition->getMethods(); 0376 } 0377 0378 foreach ($definition as $key => $method) { 0379 if ('system.' == substr($key, 0, 7)) { 0380 continue; 0381 } 0382 $this->_table->addMethod($method, $key); 0383 } 0384 } 0385 0386 /** 0387 * Set encoding 0388 * 0389 * @param string $encoding 0390 * @return Zend_XmlRpc_Server 0391 */ 0392 public function setEncoding($encoding) 0393 { 0394 $this->_encoding = $encoding; 0395 Zend_XmlRpc_Value::setEncoding($encoding); 0396 return $this; 0397 } 0398 0399 /** 0400 * Retrieve current encoding 0401 * 0402 * @return string 0403 */ 0404 public function getEncoding() 0405 { 0406 return $this->_encoding; 0407 } 0408 0409 /** 0410 * Do nothing; persistence is handled via {@link Zend_XmlRpc_Server_Cache} 0411 * 0412 * @param mixed $mode 0413 * @return void 0414 */ 0415 public function setPersistence($mode) 0416 { 0417 } 0418 0419 /** 0420 * Set the request object 0421 * 0422 * @param string|Zend_XmlRpc_Request $request 0423 * @return Zend_XmlRpc_Server 0424 * @throws Zend_XmlRpc_Server_Exception on invalid request class or object 0425 */ 0426 public function setRequest($request) 0427 { 0428 if (is_string($request) && class_exists($request)) { 0429 $request = new $request(); 0430 if (!$request instanceof Zend_XmlRpc_Request) { 0431 // require_once 'Zend/XmlRpc/Server/Exception.php'; 0432 throw new Zend_XmlRpc_Server_Exception('Invalid request class'); 0433 } 0434 $request->setEncoding($this->getEncoding()); 0435 } elseif (!$request instanceof Zend_XmlRpc_Request) { 0436 // require_once 'Zend/XmlRpc/Server/Exception.php'; 0437 throw new Zend_XmlRpc_Server_Exception('Invalid request object'); 0438 } 0439 0440 $this->_request = $request; 0441 return $this; 0442 } 0443 0444 /** 0445 * Return currently registered request object 0446 * 0447 * @return null|Zend_XmlRpc_Request 0448 */ 0449 public function getRequest() 0450 { 0451 return $this->_request; 0452 } 0453 0454 /** 0455 * Set the class to use for the response 0456 * 0457 * @param string $class 0458 * @return boolean True if class was set, false if not 0459 */ 0460 public function setResponseClass($class) 0461 { 0462 if (!class_exists($class) or 0463 ($c = new ReflectionClass($class) and !$c->isSubclassOf('Zend_XmlRpc_Response'))) { 0464 0465 // require_once 'Zend/XmlRpc/Server/Exception.php'; 0466 throw new Zend_XmlRpc_Server_Exception('Invalid response class'); 0467 } 0468 $this->_responseClass = $class; 0469 return true; 0470 } 0471 0472 /** 0473 * Retrieve current response class 0474 * 0475 * @return string 0476 */ 0477 public function getResponseClass() 0478 { 0479 return $this->_responseClass; 0480 } 0481 0482 /** 0483 * Retrieve dispatch table 0484 * 0485 * @return array 0486 */ 0487 public function getDispatchTable() 0488 { 0489 return $this->_table; 0490 } 0491 0492 /** 0493 * Returns a list of registered methods 0494 * 0495 * Returns an array of dispatchables (Zend_Server_Reflection_Function, 0496 * _Method, and _Class items). 0497 * 0498 * @return array 0499 */ 0500 public function getFunctions() 0501 { 0502 return $this->_table->toArray(); 0503 } 0504 0505 /** 0506 * Retrieve system object 0507 * 0508 * @return Zend_XmlRpc_Server_System 0509 */ 0510 public function getSystem() 0511 { 0512 return $this->_system; 0513 } 0514 0515 /** 0516 * Send arguments to all methods? 0517 * 0518 * If setClass() is used to add classes to the server, this flag defined 0519 * how to handle arguments. If set to true, all methods including constructor 0520 * will receive the arguments. If set to false, only constructor will receive the 0521 * arguments 0522 */ 0523 public function sendArgumentsToAllMethods($flag = null) 0524 { 0525 if ($flag === null) { 0526 return $this->_sendArgumentsToAllMethods; 0527 } 0528 0529 $this->_sendArgumentsToAllMethods = (bool)$flag; 0530 return $this; 0531 } 0532 0533 /** 0534 * Map PHP type to XML-RPC type 0535 * 0536 * @param string $type 0537 * @return string 0538 */ 0539 protected function _fixType($type) 0540 { 0541 if (isset($this->_typeMap[$type])) { 0542 return $this->_typeMap[$type]; 0543 } 0544 return 'void'; 0545 } 0546 0547 /** 0548 * Handle an xmlrpc call (actual work) 0549 * 0550 * @param Zend_XmlRpc_Request $request 0551 * @return Zend_XmlRpc_Response 0552 * @throws Zend_XmlRpcServer_Exception|Exception 0553 * Zend_XmlRpcServer_Exceptions are thrown for internal errors; otherwise, 0554 * any other exception may be thrown by the callback 0555 */ 0556 protected function _handle(Zend_XmlRpc_Request $request) 0557 { 0558 $method = $request->getMethod(); 0559 0560 // Check for valid method 0561 if (!$this->_table->hasMethod($method)) { 0562 // require_once 'Zend/XmlRpc/Server/Exception.php'; 0563 throw new Zend_XmlRpc_Server_Exception('Method "' . $method . '" does not exist', 620); 0564 } 0565 0566 $info = $this->_table->getMethod($method); 0567 $params = $request->getParams(); 0568 $argv = $info->getInvokeArguments(); 0569 if (0 < count($argv) and $this->sendArgumentsToAllMethods()) { 0570 $params = array_merge($params, $argv); 0571 } 0572 0573 // Check calling parameters against signatures 0574 $matched = false; 0575 $sigCalled = $request->getTypes(); 0576 0577 $sigLength = count($sigCalled); 0578 $paramsLen = count($params); 0579 if ($sigLength < $paramsLen) { 0580 for ($i = $sigLength; $i < $paramsLen; ++$i) { 0581 $xmlRpcValue = Zend_XmlRpc_Value::getXmlRpcValue($params[$i]); 0582 $sigCalled[] = $xmlRpcValue->getType(); 0583 } 0584 } 0585 0586 $signatures = $info->getPrototypes(); 0587 foreach ($signatures as $signature) { 0588 $sigParams = $signature->getParameters(); 0589 if ($sigCalled === $sigParams) { 0590 $matched = true; 0591 break; 0592 } 0593 } 0594 if (!$matched) { 0595 // require_once 'Zend/XmlRpc/Server/Exception.php'; 0596 throw new Zend_XmlRpc_Server_Exception('Calling parameters do not match signature', 623); 0597 } 0598 0599 $return = $this->_dispatch($info, $params); 0600 $responseClass = $this->getResponseClass(); 0601 return new $responseClass($return); 0602 } 0603 0604 /** 0605 * Register system methods with the server 0606 * 0607 * @return void 0608 */ 0609 protected function _registerSystemMethods() 0610 { 0611 $system = new Zend_XmlRpc_Server_System($this); 0612 $this->_system = $system; 0613 $this->setClass($system, 'system'); 0614 } 0615 }