web/lib/Zend/XmlRpc/Server.php
changeset 64 162c1de6545a
parent 19 1c2f13fd785c
child 68 ecaf28ffe26e
equal deleted inserted replaced
63:5b37998e522e 64:162c1de6545a
       
     1 <?php
       
     2 /**
       
     3  * Zend Framework
       
     4  *
       
     5  * LICENSE
       
     6  *
       
     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.
       
    14  *
       
    15  * @category   Zend
       
    16  * @package    Zend_XmlRpc
       
    17  * @subpackage Server
       
    18  * @copyright  Copyright (c) 2005-2010 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 20096 2010-01-06 02:05:09Z bkarwin $
       
    21  */
       
    22 
       
    23 /**
       
    24  * Extends Zend_Server_Abstract
       
    25  */
       
    26 require_once 'Zend/Server/Abstract.php';
       
    27 
       
    28 /**
       
    29  * XMLRPC Request
       
    30  */
       
    31 require_once 'Zend/XmlRpc/Request.php';
       
    32 
       
    33 /**
       
    34  * XMLRPC Response
       
    35  */
       
    36 require_once 'Zend/XmlRpc/Response.php';
       
    37 
       
    38 /**
       
    39  * XMLRPC HTTP Response
       
    40  */
       
    41 require_once 'Zend/XmlRpc/Response/Http.php';
       
    42 
       
    43 /**
       
    44  * XMLRPC server fault class
       
    45  */
       
    46 require_once 'Zend/XmlRpc/Server/Fault.php';
       
    47 
       
    48 /**
       
    49  * XMLRPC server system methods class
       
    50  */
       
    51 require_once 'Zend/XmlRpc/Server/System.php';
       
    52 
       
    53 /**
       
    54  * Convert PHP to and from xmlrpc native types
       
    55  */
       
    56 require_once 'Zend/XmlRpc/Value.php';
       
    57 
       
    58 /**
       
    59  * Reflection API for function/method introspection
       
    60  */
       
    61 require_once 'Zend/Server/Reflection.php';
       
    62 
       
    63 /**
       
    64  * Zend_Server_Reflection_Function_Abstract
       
    65  */
       
    66 require_once 'Zend/Server/Reflection/Function/Abstract.php';
       
    67 
       
    68 /**
       
    69  * Specifically grab the Zend_Server_Reflection_Method for manually setting up
       
    70  * system.* methods and handling callbacks in {@link loadFunctions()}.
       
    71  */
       
    72 require_once 'Zend/Server/Reflection/Method.php';
       
    73 
       
    74 /**
       
    75  * An XML-RPC server implementation
       
    76  *
       
    77  * Example:
       
    78  * <code>
       
    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';
       
    84  *
       
    85  * // Instantiate server
       
    86  * $server = new Zend_XmlRpc_Server();
       
    87  *
       
    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');
       
    91  *
       
    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';
       
    96  *
       
    97  *     // Attach Some_Service_Class in 'some' namespace
       
    98  *     $server->setClass('Some_Service_Class', 'some');
       
    99  *
       
   100  *     // Attach Another_Service_Class in 'another' namespace
       
   101  *     $server->setClass('Another_Service_Class', 'another');
       
   102  *
       
   103  *     // Create dispatch table cache file
       
   104  *     Zend_XmlRpc_Server_Cache::save($filename, $server);
       
   105  * }
       
   106  *
       
   107  * $response = $server->handle();
       
   108  * echo $response;
       
   109  * </code>
       
   110  *
       
   111  * @category   Zend
       
   112  * @package    Zend_XmlRpc
       
   113  * @subpackage Server
       
   114  * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
       
   115  * @license    http://framework.zend.com/license/new-bsd     New BSD License
       
   116  */
       
   117 class Zend_XmlRpc_Server extends Zend_Server_Abstract
       
   118 {
       
   119     /**
       
   120      * Character encoding
       
   121      * @var string
       
   122      */
       
   123     protected $_encoding = 'UTF-8';
       
   124 
       
   125     /**
       
   126      * Request processed
       
   127      * @var null|Zend_XmlRpc_Request
       
   128      */
       
   129     protected $_request = null;
       
   130 
       
   131     /**
       
   132      * Class to use for responses; defaults to {@link Zend_XmlRpc_Response_Http}
       
   133      * @var string
       
   134      */
       
   135     protected $_responseClass = 'Zend_XmlRpc_Response_Http';
       
   136 
       
   137     /**
       
   138      * Dispatch table of name => method pairs
       
   139      * @var Zend_Server_Definition
       
   140      */
       
   141     protected $_table;
       
   142 
       
   143     /**
       
   144      * PHP types => XML-RPC types
       
   145      * @var array
       
   146      */
       
   147     protected $_typeMap = array(
       
   148         'i4'                         => 'i4',
       
   149         'int'                        => 'int',
       
   150         'integer'                    => 'int',
       
   151         'Zend_Crypt_Math_BigInteger' => 'i8',
       
   152         'i8'                         => 'i8',
       
   153         'ex:i8'                      => 'i8',
       
   154         'double'                     => 'double',
       
   155         'float'                      => 'double',
       
   156         'real'                       => 'double',
       
   157         'boolean'                    => 'boolean',
       
   158         'bool'                       => 'boolean',
       
   159         'true'                       => 'boolean',
       
   160         'false'                      => 'boolean',
       
   161         'string'                     => 'string',
       
   162         'str'                        => 'string',
       
   163         'base64'                     => 'base64',
       
   164         'dateTime.iso8601'           => 'dateTime.iso8601',
       
   165         'date'                       => 'dateTime.iso8601',
       
   166         'time'                       => 'dateTime.iso8601',
       
   167         'time'                       => 'dateTime.iso8601',
       
   168         'Zend_Date'                  => 'dateTime.iso8601',
       
   169         'DateTime'                   => 'dateTime.iso8601',
       
   170         'array'                      => 'array',
       
   171         'struct'                     => 'struct',
       
   172         'null'                       => 'nil',
       
   173         'nil'                        => 'nil',
       
   174         'ex:nil'                     => 'nil',
       
   175         'void'                       => 'void',
       
   176         'mixed'                      => 'struct',
       
   177     );
       
   178 
       
   179     /**
       
   180      * Send arguments to all methods or just constructor?
       
   181      *
       
   182      * @var bool
       
   183      */
       
   184     protected $_sendArgumentsToAllMethods = true;
       
   185 
       
   186     /**
       
   187      * Constructor
       
   188      *
       
   189      * Creates system.* methods.
       
   190      *
       
   191      * @return void
       
   192      */
       
   193     public function __construct()
       
   194     {
       
   195         $this->_table = new Zend_Server_Definition();
       
   196         $this->_registerSystemMethods();
       
   197     }
       
   198 
       
   199     /**
       
   200      * Proxy calls to system object
       
   201      *
       
   202      * @param  string $method
       
   203      * @param  array $params
       
   204      * @return mixed
       
   205      * @throws Zend_XmlRpc_Server_Exception
       
   206      */
       
   207     public function __call($method, $params)
       
   208     {
       
   209         $system = $this->getSystem();
       
   210         if (!method_exists($system, $method)) {
       
   211             require_once 'Zend/XmlRpc/Server/Exception.php';
       
   212             throw new Zend_XmlRpc_Server_Exception('Unknown instance method called on server: ' . $method);
       
   213         }
       
   214         return call_user_func_array(array($system, $method), $params);
       
   215     }
       
   216 
       
   217     /**
       
   218      * Attach a callback as an XMLRPC method
       
   219      *
       
   220      * Attaches a callback as an XMLRPC method, prefixing the XMLRPC method name
       
   221      * with $namespace, if provided. Reflection is done on the callback's
       
   222      * docblock to create the methodHelp for the XMLRPC method.
       
   223      *
       
   224      * Additional arguments to pass to the function at dispatch may be passed;
       
   225      * any arguments following the namespace will be aggregated and passed at
       
   226      * dispatch time.
       
   227      *
       
   228      * @param string|array $function Valid callback
       
   229      * @param string $namespace Optional namespace prefix
       
   230      * @return void
       
   231      * @throws Zend_XmlRpc_Server_Exception
       
   232      */
       
   233     public function addFunction($function, $namespace = '')
       
   234     {
       
   235         if (!is_string($function) && !is_array($function)) {
       
   236             require_once 'Zend/XmlRpc/Server/Exception.php';
       
   237             throw new Zend_XmlRpc_Server_Exception('Unable to attach function; invalid', 611);
       
   238         }
       
   239 
       
   240         $argv = null;
       
   241         if (2 < func_num_args()) {
       
   242             $argv = func_get_args();
       
   243             $argv = array_slice($argv, 2);
       
   244         }
       
   245 
       
   246         $function = (array) $function;
       
   247         foreach ($function as $func) {
       
   248             if (!is_string($func) || !function_exists($func)) {
       
   249                 require_once 'Zend/XmlRpc/Server/Exception.php';
       
   250                 throw new Zend_XmlRpc_Server_Exception('Unable to attach function; invalid', 611);
       
   251             }
       
   252             $reflection = Zend_Server_Reflection::reflectFunction($func, $argv, $namespace);
       
   253             $this->_buildSignature($reflection);
       
   254         }
       
   255     }
       
   256 
       
   257     /**
       
   258      * Attach class methods as XMLRPC method handlers
       
   259      *
       
   260      * $class may be either a class name or an object. Reflection is done on the
       
   261      * class or object to determine the available public methods, and each is
       
   262      * attached to the server as an available method; if a $namespace has been
       
   263      * provided, that namespace is used to prefix the XMLRPC method names.
       
   264      *
       
   265      * Any additional arguments beyond $namespace will be passed to a method at
       
   266      * invocation.
       
   267      *
       
   268      * @param string|object $class
       
   269      * @param string $namespace Optional
       
   270      * @param mixed $argv Optional arguments to pass to methods
       
   271      * @return void
       
   272      * @throws Zend_XmlRpc_Server_Exception on invalid input
       
   273      */
       
   274     public function setClass($class, $namespace = '', $argv = null)
       
   275     {
       
   276         if (is_string($class) && !class_exists($class)) {
       
   277             require_once 'Zend/XmlRpc/Server/Exception.php';
       
   278             throw new Zend_XmlRpc_Server_Exception('Invalid method class', 610);
       
   279         }
       
   280 
       
   281         $argv = null;
       
   282         if (2 < func_num_args()) {
       
   283             $argv = func_get_args();
       
   284             $argv = array_slice($argv, 2);
       
   285         }
       
   286 
       
   287         $dispatchable = Zend_Server_Reflection::reflectClass($class, $argv, $namespace);
       
   288         foreach ($dispatchable->getMethods() as $reflection) {
       
   289             $this->_buildSignature($reflection, $class);
       
   290         }
       
   291     }
       
   292 
       
   293     /**
       
   294      * Raise an xmlrpc server fault
       
   295      *
       
   296      * @param string|Exception $fault
       
   297      * @param int $code
       
   298      * @return Zend_XmlRpc_Server_Fault
       
   299      */
       
   300     public function fault($fault = null, $code = 404)
       
   301     {
       
   302         if (!$fault instanceof Exception) {
       
   303             $fault = (string) $fault;
       
   304             if (empty($fault)) {
       
   305                 $fault = 'Unknown Error';
       
   306             }
       
   307             require_once 'Zend/XmlRpc/Server/Exception.php';
       
   308             $fault = new Zend_XmlRpc_Server_Exception($fault, $code);
       
   309         }
       
   310 
       
   311         return Zend_XmlRpc_Server_Fault::getInstance($fault);
       
   312     }
       
   313 
       
   314     /**
       
   315      * Handle an xmlrpc call
       
   316      *
       
   317      * @param Zend_XmlRpc_Request $request Optional
       
   318      * @return Zend_XmlRpc_Response|Zend_XmlRpc_Fault
       
   319      */
       
   320     public function handle($request = false)
       
   321     {
       
   322         // Get request
       
   323         if ((!$request || !$request instanceof Zend_XmlRpc_Request)
       
   324             && (null === ($request = $this->getRequest()))
       
   325         ) {
       
   326             require_once 'Zend/XmlRpc/Request/Http.php';
       
   327             $request = new Zend_XmlRpc_Request_Http();
       
   328             $request->setEncoding($this->getEncoding());
       
   329         }
       
   330 
       
   331         $this->setRequest($request);
       
   332 
       
   333         if ($request->isFault()) {
       
   334             $response = $request->getFault();
       
   335         } else {
       
   336             try {
       
   337                 $response = $this->_handle($request);
       
   338             } catch (Exception $e) {
       
   339                 $response = $this->fault($e);
       
   340             }
       
   341         }
       
   342 
       
   343         // Set output encoding
       
   344         $response->setEncoding($this->getEncoding());
       
   345 
       
   346         return $response;
       
   347     }
       
   348 
       
   349     /**
       
   350      * Load methods as returned from {@link getFunctions}
       
   351      *
       
   352      * Typically, you will not use this method; it will be called using the
       
   353      * results pulled from {@link Zend_XmlRpc_Server_Cache::get()}.
       
   354      *
       
   355      * @param  array|Zend_Server_Definition $definition
       
   356      * @return void
       
   357      * @throws Zend_XmlRpc_Server_Exception on invalid input
       
   358      */
       
   359     public function loadFunctions($definition)
       
   360     {
       
   361         if (!is_array($definition) && (!$definition instanceof Zend_Server_Definition)) {
       
   362             if (is_object($definition)) {
       
   363                 $type = get_class($definition);
       
   364             } else {
       
   365                 $type = gettype($definition);
       
   366             }
       
   367             require_once 'Zend/XmlRpc/Server/Exception.php';
       
   368             throw new Zend_XmlRpc_Server_Exception('Unable to load server definition; must be an array or Zend_Server_Definition, received ' . $type, 612);
       
   369         }
       
   370 
       
   371         $this->_table->clearMethods();
       
   372         $this->_registerSystemMethods();
       
   373 
       
   374         if ($definition instanceof Zend_Server_Definition) {
       
   375             $definition = $definition->getMethods();
       
   376         }
       
   377 
       
   378         foreach ($definition as $key => $method) {
       
   379             if ('system.' == substr($key, 0, 7)) {
       
   380                 continue;
       
   381             }
       
   382             $this->_table->addMethod($method, $key);
       
   383         }
       
   384     }
       
   385 
       
   386     /**
       
   387      * Set encoding
       
   388      *
       
   389      * @param string $encoding
       
   390      * @return Zend_XmlRpc_Server
       
   391      */
       
   392     public function setEncoding($encoding)
       
   393     {
       
   394         $this->_encoding = $encoding;
       
   395         Zend_XmlRpc_Value::setEncoding($encoding);
       
   396         return $this;
       
   397     }
       
   398 
       
   399     /**
       
   400      * Retrieve current encoding
       
   401      *
       
   402      * @return string
       
   403      */
       
   404     public function getEncoding()
       
   405     {
       
   406         return $this->_encoding;
       
   407     }
       
   408 
       
   409     /**
       
   410      * Do nothing; persistence is handled via {@link Zend_XmlRpc_Server_Cache}
       
   411      *
       
   412      * @param  mixed $mode
       
   413      * @return void
       
   414      */
       
   415     public function setPersistence($mode)
       
   416     {
       
   417     }
       
   418 
       
   419     /**
       
   420      * Set the request object
       
   421      *
       
   422      * @param string|Zend_XmlRpc_Request $request
       
   423      * @return Zend_XmlRpc_Server
       
   424      * @throws Zend_XmlRpc_Server_Exception on invalid request class or object
       
   425      */
       
   426     public function setRequest($request)
       
   427     {
       
   428         if (is_string($request) && class_exists($request)) {
       
   429             $request = new $request();
       
   430             if (!$request instanceof Zend_XmlRpc_Request) {
       
   431                 require_once 'Zend/XmlRpc/Server/Exception.php';
       
   432                 throw new Zend_XmlRpc_Server_Exception('Invalid request class');
       
   433             }
       
   434             $request->setEncoding($this->getEncoding());
       
   435         } elseif (!$request instanceof Zend_XmlRpc_Request) {
       
   436             require_once 'Zend/XmlRpc/Server/Exception.php';
       
   437             throw new Zend_XmlRpc_Server_Exception('Invalid request object');
       
   438         }
       
   439 
       
   440         $this->_request = $request;
       
   441         return $this;
       
   442     }
       
   443 
       
   444     /**
       
   445      * Return currently registered request object
       
   446      *
       
   447      * @return null|Zend_XmlRpc_Request
       
   448      */
       
   449     public function getRequest()
       
   450     {
       
   451         return $this->_request;
       
   452     }
       
   453 
       
   454     /**
       
   455      * Set the class to use for the response
       
   456      *
       
   457      * @param string $class
       
   458      * @return boolean True if class was set, false if not
       
   459      */
       
   460     public function setResponseClass($class)
       
   461     {
       
   462         if (!class_exists($class) or
       
   463             ($c = new ReflectionClass($class) and !$c->isSubclassOf('Zend_XmlRpc_Response'))) {
       
   464 
       
   465             require_once 'Zend/XmlRpc/Server/Exception.php';
       
   466             throw new Zend_XmlRpc_Server_Exception('Invalid response class');
       
   467         }
       
   468         $this->_responseClass = $class;
       
   469         return true;
       
   470     }
       
   471 
       
   472     /**
       
   473      * Retrieve current response class
       
   474      *
       
   475      * @return string
       
   476      */
       
   477     public function getResponseClass()
       
   478     {
       
   479         return $this->_responseClass;
       
   480     }
       
   481 
       
   482     /**
       
   483      * Retrieve dispatch table
       
   484      *
       
   485      * @return array
       
   486      */
       
   487     public function getDispatchTable()
       
   488     {
       
   489         return $this->_table;
       
   490     }
       
   491 
       
   492     /**
       
   493      * Returns a list of registered methods
       
   494      *
       
   495      * Returns an array of dispatchables (Zend_Server_Reflection_Function,
       
   496      * _Method, and _Class items).
       
   497      *
       
   498      * @return array
       
   499      */
       
   500     public function getFunctions()
       
   501     {
       
   502         return $this->_table->toArray();
       
   503     }
       
   504 
       
   505     /**
       
   506      * Retrieve system object
       
   507      *
       
   508      * @return Zend_XmlRpc_Server_System
       
   509      */
       
   510     public function getSystem()
       
   511     {
       
   512         return $this->_system;
       
   513     }
       
   514 
       
   515     /**
       
   516      * Send arguments to all methods?
       
   517      *
       
   518      * If setClass() is used to add classes to the server, this flag defined
       
   519      * how to handle arguments. If set to true, all methods including constructor
       
   520      * will receive the arguments. If set to false, only constructor will receive the
       
   521      * arguments
       
   522      */
       
   523     public function sendArgumentsToAllMethods($flag = null)
       
   524     {
       
   525         if ($flag === null) {
       
   526             return $this->_sendArgumentsToAllMethods;
       
   527         }
       
   528 
       
   529         $this->_sendArgumentsToAllMethods = (bool)$flag;
       
   530         return $this;
       
   531     }
       
   532 
       
   533     /**
       
   534      * Map PHP type to XML-RPC type
       
   535      *
       
   536      * @param  string $type
       
   537      * @return string
       
   538      */
       
   539     protected function _fixType($type)
       
   540     {
       
   541         if (isset($this->_typeMap[$type])) {
       
   542             return $this->_typeMap[$type];
       
   543         }
       
   544         return 'void';
       
   545     }
       
   546 
       
   547     /**
       
   548      * Handle an xmlrpc call (actual work)
       
   549      *
       
   550      * @param Zend_XmlRpc_Request $request
       
   551      * @return Zend_XmlRpc_Response
       
   552      * @throws Zend_XmlRpcServer_Exception|Exception
       
   553      * Zend_XmlRpcServer_Exceptions are thrown for internal errors; otherwise,
       
   554      * any other exception may be thrown by the callback
       
   555      */
       
   556     protected function _handle(Zend_XmlRpc_Request $request)
       
   557     {
       
   558         $method = $request->getMethod();
       
   559 
       
   560         // Check for valid method
       
   561         if (!$this->_table->hasMethod($method)) {
       
   562             require_once 'Zend/XmlRpc/Server/Exception.php';
       
   563             throw new Zend_XmlRpc_Server_Exception('Method "' . $method . '" does not exist', 620);
       
   564         }
       
   565 
       
   566         $info     = $this->_table->getMethod($method);
       
   567         $params   = $request->getParams();
       
   568         $argv     = $info->getInvokeArguments();
       
   569         if (0 < count($argv) and $this->sendArgumentsToAllMethods()) {
       
   570             $params = array_merge($params, $argv);
       
   571         }
       
   572 
       
   573         // Check calling parameters against signatures
       
   574         $matched    = false;
       
   575         $sigCalled  = $request->getTypes();
       
   576 
       
   577         $sigLength  = count($sigCalled);
       
   578         $paramsLen  = count($params);
       
   579         if ($sigLength < $paramsLen) {
       
   580             for ($i = $sigLength; $i < $paramsLen; ++$i) {
       
   581                 $xmlRpcValue = Zend_XmlRpc_Value::getXmlRpcValue($params[$i]);
       
   582                 $sigCalled[] = $xmlRpcValue->getType();
       
   583             }
       
   584         }
       
   585 
       
   586         $signatures = $info->getPrototypes();
       
   587         foreach ($signatures as $signature) {
       
   588             $sigParams = $signature->getParameters();
       
   589             if ($sigCalled === $sigParams) {
       
   590                 $matched = true;
       
   591                 break;
       
   592             }
       
   593         }
       
   594         if (!$matched) {
       
   595             require_once 'Zend/XmlRpc/Server/Exception.php';
       
   596             throw new Zend_XmlRpc_Server_Exception('Calling parameters do not match signature', 623);
       
   597         }
       
   598 
       
   599         $return        = $this->_dispatch($info, $params);
       
   600         $responseClass = $this->getResponseClass();
       
   601         return new $responseClass($return);
       
   602     }
       
   603 
       
   604     /**
       
   605      * Register system methods with the server
       
   606      *
       
   607      * @return void
       
   608      */
       
   609     protected function _registerSystemMethods()
       
   610     {
       
   611         $system = new Zend_XmlRpc_Server_System($this);
       
   612         $this->_system = $system;
       
   613         $this->setClass($system, 'system');
       
   614     }
       
   615 }