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 }