web/lib/Zend/Json/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_Json
       
    17  * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
       
    18  * @license    http://framework.zend.com/license/new-bsd     New BSD License
       
    19  * @version    $Id: Server.php 22237 2010-05-21 23:58:00Z andyfowler $
       
    20  */
       
    21 
       
    22 /**
       
    23  * @see Zend_Server_Abstract
       
    24  */
       
    25 require_once 'Zend/Server/Abstract.php';
       
    26 
       
    27 /**
       
    28  * @category   Zend
       
    29  * @package    Zend_Json
       
    30  * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
       
    31  * @license    http://framework.zend.com/license/new-bsd     New BSD License
       
    32  */
       
    33 class Zend_Json_Server extends Zend_Server_Abstract
       
    34 {
       
    35     /**#@+
       
    36      * Version Constants
       
    37      */
       
    38     const VERSION_1 = '1.0';
       
    39     const VERSION_2 = '2.0';
       
    40     /**#@-*/
       
    41 
       
    42     /**
       
    43      * Flag: whether or not to auto-emit the response
       
    44      * @var bool
       
    45      */
       
    46     protected $_autoEmitResponse = true;
       
    47 
       
    48     /**
       
    49      * @var bool Flag; allow overwriting existing methods when creating server definition
       
    50      */
       
    51     protected $_overwriteExistingMethods = true;
       
    52 
       
    53     /**
       
    54      * Request object
       
    55      * @var Zend_Json_Server_Request
       
    56      */
       
    57     protected $_request;
       
    58 
       
    59     /**
       
    60      * Response object
       
    61      * @var Zend_Json_Server_Response
       
    62      */
       
    63     protected $_response;
       
    64 
       
    65     /**
       
    66      * SMD object
       
    67      * @var Zend_Json_Server_Smd
       
    68      */
       
    69     protected $_serviceMap;
       
    70 
       
    71     /**
       
    72      * SMD class accessors
       
    73      * @var array
       
    74      */
       
    75     protected $_smdMethods;
       
    76 
       
    77     /**
       
    78      * @var Zend_Server_Description
       
    79      */
       
    80     protected $_table;
       
    81 
       
    82     /**
       
    83      * Attach a function or callback to the server
       
    84      *
       
    85      * @param  string|array $function Valid PHP callback
       
    86      * @param  string $namespace  Ignored
       
    87      * @return Zend_Json_Server
       
    88      */
       
    89     public function addFunction($function, $namespace = '')
       
    90     {
       
    91         if (!is_string($function) && (!is_array($function) || (2 > count($function)))) {
       
    92             require_once 'Zend/Json/Server/Exception.php';
       
    93             throw new Zend_Json_Server_Exception('Unable to attach function; invalid');
       
    94         }
       
    95 
       
    96         if (!is_callable($function)) {
       
    97             require_once 'Zend/Json/Server/Exception.php';
       
    98             throw new Zend_Json_Server_Exception('Unable to attach function; does not exist');
       
    99         }
       
   100 
       
   101         $argv = null;
       
   102         if (2 < func_num_args()) {
       
   103             $argv = func_get_args();
       
   104             $argv = array_slice($argv, 2);
       
   105         }
       
   106 
       
   107         require_once 'Zend/Server/Reflection.php';
       
   108         if (is_string($function)) {
       
   109             $method = Zend_Server_Reflection::reflectFunction($function, $argv, $namespace);
       
   110         } else {
       
   111             $class  = array_shift($function);
       
   112             $action = array_shift($function);
       
   113             $reflection = Zend_Server_Reflection::reflectClass($class, $argv, $namespace);
       
   114             $methods = $reflection->getMethods();
       
   115             $found   = false;
       
   116             foreach ($methods as $method) {
       
   117                 if ($action == $method->getName()) {
       
   118                     $found = true;
       
   119                     break;
       
   120                 }
       
   121             }
       
   122             if (!$found) {
       
   123                 $this->fault('Method not found', -32601);
       
   124                 return $this;
       
   125             }
       
   126         }
       
   127 
       
   128         $definition = $this->_buildSignature($method);
       
   129         $this->_addMethodServiceMap($definition);
       
   130 
       
   131         return $this;
       
   132     }
       
   133 
       
   134     /**
       
   135      * Register a class with the server
       
   136      *
       
   137      * @param  string $class
       
   138      * @param  string $namespace Ignored
       
   139      * @param  mixed $argv Ignored
       
   140      * @return Zend_Json_Server
       
   141      */
       
   142     public function setClass($class, $namespace = '', $argv = null)
       
   143     {
       
   144         $argv = null;
       
   145         if (3 < func_num_args()) {
       
   146             $argv = func_get_args();
       
   147             $argv = array_slice($argv, 3);
       
   148         }
       
   149 
       
   150         require_once 'Zend/Server/Reflection.php';
       
   151         $reflection = Zend_Server_Reflection::reflectClass($class, $argv, $namespace);
       
   152 
       
   153         foreach ($reflection->getMethods() as $method) {
       
   154             $definition = $this->_buildSignature($method, $class);
       
   155             $this->_addMethodServiceMap($definition);
       
   156         }
       
   157         return $this;
       
   158     }
       
   159 
       
   160     /**
       
   161      * Indicate fault response
       
   162      *
       
   163      * @param  string $fault
       
   164      * @param  int $code
       
   165      * @return false
       
   166      */
       
   167     public function fault($fault = null, $code = 404, $data = null)
       
   168     {
       
   169         require_once 'Zend/Json/Server/Error.php';
       
   170         $error = new Zend_Json_Server_Error($fault, $code, $data);
       
   171         $this->getResponse()->setError($error);
       
   172         return $error;
       
   173     }
       
   174 
       
   175     /**
       
   176      * Handle request
       
   177      *
       
   178      * @param  Zend_Json_Server_Request $request
       
   179      * @return null|Zend_Json_Server_Response
       
   180      */
       
   181     public function handle($request = false)
       
   182     {
       
   183         if ((false !== $request) && (!$request instanceof Zend_Json_Server_Request)) {
       
   184             require_once 'Zend/Json/Server/Exception.php';
       
   185             throw new Zend_Json_Server_Exception('Invalid request type provided; cannot handle');
       
   186         } elseif ($request) {
       
   187             $this->setRequest($request);
       
   188         }
       
   189 
       
   190         // Handle request
       
   191         $this->_handle();
       
   192 
       
   193         // Get response
       
   194         $response = $this->_getReadyResponse();
       
   195 
       
   196         // Emit response?
       
   197         if ($this->autoEmitResponse()) {
       
   198             echo $response;
       
   199             return;
       
   200         }
       
   201 
       
   202         // or return it?
       
   203         return $response;
       
   204     }
       
   205 
       
   206     /**
       
   207      * Load function definitions
       
   208      *
       
   209      * @param  array|Zend_Server_Definition $definition
       
   210      * @return void
       
   211      */
       
   212     public function loadFunctions($definition)
       
   213     {
       
   214         if (!is_array($definition) && (!$definition instanceof Zend_Server_Definition)) {
       
   215             require_once 'Zend/Json/Server/Exception.php';
       
   216             throw new Zend_Json_Server_Exception('Invalid definition provided to loadFunctions()');
       
   217         }
       
   218 
       
   219         foreach ($definition as $key => $method) {
       
   220             $this->_table->addMethod($method, $key);
       
   221             $this->_addMethodServiceMap($method);
       
   222         }
       
   223     }
       
   224 
       
   225     public function setPersistence($mode)
       
   226     {
       
   227     }
       
   228 
       
   229     /**
       
   230      * Set request object
       
   231      *
       
   232      * @param  Zend_Json_Server_Request $request
       
   233      * @return Zend_Json_Server
       
   234      */
       
   235     public function setRequest(Zend_Json_Server_Request $request)
       
   236     {
       
   237         $this->_request = $request;
       
   238         return $this;
       
   239     }
       
   240 
       
   241     /**
       
   242      * Get JSON-RPC request object
       
   243      *
       
   244      * @return Zend_Json_Server_Request
       
   245      */
       
   246     public function getRequest()
       
   247     {
       
   248         if (null === ($request = $this->_request)) {
       
   249             require_once 'Zend/Json/Server/Request/Http.php';
       
   250             $this->setRequest(new Zend_Json_Server_Request_Http());
       
   251         }
       
   252         return $this->_request;
       
   253     }
       
   254 
       
   255     /**
       
   256      * Set response object
       
   257      *
       
   258      * @param  Zend_Json_Server_Response $response
       
   259      * @return Zend_Json_Server
       
   260      */
       
   261     public function setResponse(Zend_Json_Server_Response $response)
       
   262     {
       
   263         $this->_response = $response;
       
   264         return $this;
       
   265     }
       
   266 
       
   267     /**
       
   268      * Get response object
       
   269      *
       
   270      * @return Zend_Json_Server_Response
       
   271      */
       
   272     public function getResponse()
       
   273     {
       
   274         if (null === ($response = $this->_response)) {
       
   275             require_once 'Zend/Json/Server/Response/Http.php';
       
   276             $this->setResponse(new Zend_Json_Server_Response_Http());
       
   277         }
       
   278         return $this->_response;
       
   279     }
       
   280 
       
   281     /**
       
   282      * Set flag indicating whether or not to auto-emit response
       
   283      *
       
   284      * @param  bool $flag
       
   285      * @return Zend_Json_Server
       
   286      */
       
   287     public function setAutoEmitResponse($flag)
       
   288     {
       
   289         $this->_autoEmitResponse = (bool) $flag;
       
   290         return $this;
       
   291     }
       
   292 
       
   293     /**
       
   294      * Will we auto-emit the response?
       
   295      *
       
   296      * @return bool
       
   297      */
       
   298     public function autoEmitResponse()
       
   299     {
       
   300         return $this->_autoEmitResponse;
       
   301     }
       
   302 
       
   303     // overloading for SMD metadata
       
   304     /**
       
   305      * Overload to accessors of SMD object
       
   306      *
       
   307      * @param  string $method
       
   308      * @param  array $args
       
   309      * @return mixed
       
   310      */
       
   311     public function __call($method, $args)
       
   312     {
       
   313         if (preg_match('/^(set|get)/', $method, $matches)) {
       
   314             if (in_array($method, $this->_getSmdMethods())) {
       
   315                 if ('set' == $matches[1]) {
       
   316                     $value = array_shift($args);
       
   317                     $this->getServiceMap()->$method($value);
       
   318                     return $this;
       
   319                 } else {
       
   320                     return $this->getServiceMap()->$method();
       
   321                 }
       
   322             }
       
   323         }
       
   324         return null;
       
   325     }
       
   326 
       
   327     /**
       
   328      * Retrieve SMD object
       
   329      *
       
   330      * @return Zend_Json_Server_Smd
       
   331      */
       
   332     public function getServiceMap()
       
   333     {
       
   334         if (null === $this->_serviceMap) {
       
   335             require_once 'Zend/Json/Server/Smd.php';
       
   336             $this->_serviceMap = new Zend_Json_Server_Smd();
       
   337         }
       
   338         return $this->_serviceMap;
       
   339     }
       
   340 
       
   341     /**
       
   342      * Add service method to service map
       
   343      *
       
   344      * @param  Zend_Server_Reflection_Function $method
       
   345      * @return void
       
   346      */
       
   347     protected function _addMethodServiceMap(Zend_Server_Method_Definition $method)
       
   348     {
       
   349         $serviceInfo = array(
       
   350             'name'   => $method->getName(),
       
   351             'return' => $this->_getReturnType($method),
       
   352         );
       
   353         $params = $this->_getParams($method);
       
   354         $serviceInfo['params'] = $params;
       
   355         $serviceMap = $this->getServiceMap();
       
   356         if (false !== $serviceMap->getService($serviceInfo['name'])) {
       
   357             $serviceMap->removeService($serviceInfo['name']);
       
   358         }
       
   359         $serviceMap->addService($serviceInfo);
       
   360     }
       
   361 
       
   362     /**
       
   363      * Translate PHP type to JSON type
       
   364      *
       
   365      * @param  string $type
       
   366      * @return string
       
   367      */
       
   368     protected function _fixType($type)
       
   369     {
       
   370         return $type;
       
   371     }
       
   372 
       
   373     /**
       
   374      * Get default params from signature
       
   375      *
       
   376      * @param  array $args
       
   377      * @param  array $params
       
   378      * @return array
       
   379      */
       
   380     protected function _getDefaultParams(array $args, array $params)
       
   381     {
       
   382         $defaultParams = array_slice($params, count($args));
       
   383         foreach ($defaultParams as $param) {
       
   384             $value = null;
       
   385             if (array_key_exists('default', $param)) {
       
   386                 $value = $param['default'];
       
   387             }
       
   388             array_push($args, $value);
       
   389         }
       
   390         return $args;
       
   391     }
       
   392 
       
   393     /**
       
   394      * Get method param type
       
   395      *
       
   396      * @param  Zend_Server_Reflection_Function_Abstract $method
       
   397      * @return string|array
       
   398      */
       
   399     protected function _getParams(Zend_Server_Method_Definition $method)
       
   400     {
       
   401         $params = array();
       
   402         foreach ($method->getPrototypes() as $prototype) {
       
   403             foreach ($prototype->getParameterObjects() as $key => $parameter) {
       
   404                 if (!isset($params[$key])) {
       
   405                     $params[$key] = array(
       
   406                         'type'     => $parameter->getType(),
       
   407                         'name'     => $parameter->getName(),
       
   408                         'optional' => $parameter->isOptional(),
       
   409                     );
       
   410                     if (null !== ($default = $parameter->getDefaultValue())) {
       
   411                         $params[$key]['default'] = $default;
       
   412                     }
       
   413                     $description = $parameter->getDescription();
       
   414                     if (!empty($description)) {
       
   415                         $params[$key]['description'] = $description;
       
   416                     }
       
   417                     continue;
       
   418                 }
       
   419                 $newType = $parameter->getType();
       
   420                 if (!is_array($params[$key]['type'])) {
       
   421                     if ($params[$key]['type'] == $newType) {
       
   422                         continue;
       
   423                     }
       
   424                     $params[$key]['type'] = (array) $params[$key]['type'];
       
   425                 } elseif (in_array($newType, $params[$key]['type'])) {
       
   426                     continue;
       
   427                 }
       
   428                 array_push($params[$key]['type'], $parameter->getType());
       
   429             }
       
   430         }
       
   431         return $params;
       
   432     }
       
   433 
       
   434     /**
       
   435      * Set response state
       
   436      *
       
   437      * @return Zend_Json_Server_Response
       
   438      */
       
   439     protected function _getReadyResponse()
       
   440     {
       
   441         $request  = $this->getRequest();
       
   442         $response = $this->getResponse();
       
   443 
       
   444         $response->setServiceMap($this->getServiceMap());
       
   445         if (null !== ($id = $request->getId())) {
       
   446             $response->setId($id);
       
   447         }
       
   448         if (null !== ($version = $request->getVersion())) {
       
   449             $response->setVersion($version);
       
   450         }
       
   451 
       
   452         return $response;
       
   453     }
       
   454 
       
   455     /**
       
   456      * Get method return type
       
   457      *
       
   458      * @param  Zend_Server_Reflection_Function_Abstract $method
       
   459      * @return string|array
       
   460      */
       
   461     protected function _getReturnType(Zend_Server_Method_Definition $method)
       
   462     {
       
   463         $return = array();
       
   464         foreach ($method->getPrototypes() as $prototype) {
       
   465             $return[] = $prototype->getReturnType();
       
   466         }
       
   467         if (1 == count($return)) {
       
   468             return $return[0];
       
   469         }
       
   470         return $return;
       
   471     }
       
   472 
       
   473     /**
       
   474      * Retrieve list of allowed SMD methods for proxying
       
   475      *
       
   476      * @return array
       
   477      */
       
   478     protected function _getSmdMethods()
       
   479     {
       
   480         if (null === $this->_smdMethods) {
       
   481             $this->_smdMethods = array();
       
   482             require_once 'Zend/Json/Server/Smd.php';
       
   483             $methods = get_class_methods('Zend_Json_Server_Smd');
       
   484             foreach ($methods as $key => $method) {
       
   485                 if (!preg_match('/^(set|get)/', $method)) {
       
   486                     continue;
       
   487                 }
       
   488                 if (strstr($method, 'Service')) {
       
   489                     continue;
       
   490                 }
       
   491                 $this->_smdMethods[] = $method;
       
   492             }
       
   493         }
       
   494         return $this->_smdMethods;
       
   495     }
       
   496 
       
   497     /**
       
   498      * Internal method for handling request
       
   499      *
       
   500      * @return void
       
   501      */
       
   502     protected function _handle()
       
   503     {
       
   504         $request = $this->getRequest();
       
   505 
       
   506         if (!$request->isMethodError() && (null === $request->getMethod())) {
       
   507             return $this->fault('Invalid Request', -32600);
       
   508         }
       
   509 
       
   510         if ($request->isMethodError()) {
       
   511             return $this->fault('Invalid Request', -32600);
       
   512         }
       
   513 
       
   514         $method = $request->getMethod();
       
   515         if (!$this->_table->hasMethod($method)) {
       
   516             return $this->fault('Method not found', -32601);
       
   517         }
       
   518 
       
   519         $params        = $request->getParams();
       
   520         $invocable     = $this->_table->getMethod($method);
       
   521         $serviceMap    = $this->getServiceMap();
       
   522         $service       = $serviceMap->getService($method);
       
   523         $serviceParams = $service->getParams();
       
   524 
       
   525         if (count($params) < count($serviceParams)) {
       
   526             $params = $this->_getDefaultParams($params, $serviceParams);
       
   527         }
       
   528 
       
   529         //Make sure named parameters are passed in correct order
       
   530         if ( is_string( key( $params ) ) ) {
       
   531 
       
   532             $callback = $invocable->getCallback();
       
   533             if ('function' == $callback->getType()) {
       
   534                 $reflection = new ReflectionFunction( $callback->getFunction() );
       
   535                 $refParams  = $reflection->getParameters();
       
   536             } else {
       
   537                 
       
   538                 $reflection = new ReflectionMethod( 
       
   539                     $callback->getClass(),
       
   540                     $callback->getMethod()
       
   541                 );
       
   542                 $refParams = $reflection->getParameters();
       
   543             }
       
   544 
       
   545             $orderedParams = array();
       
   546             foreach( $reflection->getParameters() as $refParam ) {
       
   547                 if( isset( $params[ $refParam->getName() ] ) ) {
       
   548                     $orderedParams[ $refParam->getName() ] = $params[ $refParam->getName() ];
       
   549                 } elseif( $refParam->isOptional() ) {
       
   550                     $orderedParams[ $refParam->getName() ] = null;
       
   551                 } else {
       
   552                     throw new Zend_Server_Exception( 
       
   553                         'Missing required parameter: ' . $refParam->getName() 
       
   554                     ); 
       
   555                 }
       
   556             }
       
   557             $params = $orderedParams;
       
   558         }
       
   559 
       
   560         try {
       
   561             $result = $this->_dispatch($invocable, $params);
       
   562         } catch (Exception $e) {
       
   563             return $this->fault($e->getMessage(), $e->getCode(), $e);
       
   564         }
       
   565 
       
   566         $this->getResponse()->setResult($result);
       
   567     }
       
   568 }