File indexing completed on 2024-12-22 05:36:48

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_Json
0017  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0018  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0019  * @version    $Id$
0020  */
0021 
0022 /**
0023  * @see Zend_Server_Abstract
0024  */
0025 // require_once 'Zend/Server/Abstract.php';
0026 
0027 /**
0028  * @category   Zend
0029  * @package    Zend_Json
0030  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0031  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0032  */
0033 class Zend_Json_Server extends Zend_Server_Abstract
0034 {
0035     /**#@+
0036      * Version Constants
0037      */
0038     const VERSION_1 = '1.0';
0039     const VERSION_2 = '2.0';
0040     /**#@-*/
0041 
0042     /**
0043      * Flag: whether or not to auto-emit the response
0044      * @var bool
0045      */
0046     protected $_autoEmitResponse = true;
0047 
0048     /**
0049      * @var bool Flag; allow overwriting existing methods when creating server definition
0050      */
0051     protected $_overwriteExistingMethods = true;
0052 
0053     /**
0054      * Request object
0055      * @var Zend_Json_Server_Request
0056      */
0057     protected $_request;
0058 
0059     /**
0060      * Response object
0061      * @var Zend_Json_Server_Response
0062      */
0063     protected $_response;
0064 
0065     /**
0066      * SMD object
0067      * @var Zend_Json_Server_Smd
0068      */
0069     protected $_serviceMap;
0070 
0071     /**
0072      * SMD class accessors
0073      * @var array
0074      */
0075     protected $_smdMethods;
0076 
0077     /**
0078      * @var Zend_Server_Description
0079      */
0080     protected $_table;
0081 
0082     /**
0083      * Attach a function or callback to the server
0084      *
0085      * @param  string|array $function Valid PHP callback
0086      * @param  string $namespace  Ignored
0087      * @return Zend_Json_Server
0088      */
0089     public function addFunction($function, $namespace = '')
0090     {
0091         if (!is_string($function) && (!is_array($function) || (2 > count($function)))) {
0092             // require_once 'Zend/Json/Server/Exception.php';
0093             throw new Zend_Json_Server_Exception('Unable to attach function; invalid');
0094         }
0095 
0096         if (!is_callable($function)) {
0097             // require_once 'Zend/Json/Server/Exception.php';
0098             throw new Zend_Json_Server_Exception('Unable to attach function; does not exist');
0099         }
0100 
0101         $argv = null;
0102         if (2 < func_num_args()) {
0103             $argv = func_get_args();
0104             $argv = array_slice($argv, 2);
0105         }
0106 
0107         // require_once 'Zend/Server/Reflection.php';
0108         if (is_string($function)) {
0109             $method = Zend_Server_Reflection::reflectFunction($function, $argv, $namespace);
0110         } else {
0111             $class  = array_shift($function);
0112             $action = array_shift($function);
0113             $reflection = Zend_Server_Reflection::reflectClass($class, $argv, $namespace);
0114             $methods = $reflection->getMethods();
0115             $found   = false;
0116             foreach ($methods as $method) {
0117                 if ($action == $method->getName()) {
0118                     $found = true;
0119                     break;
0120                 }
0121             }
0122             if (!$found) {
0123                 $this->fault('Method not found', -32601);
0124                 return $this;
0125             }
0126         }
0127 
0128         $definition = $this->_buildSignature($method);
0129         $this->_addMethodServiceMap($definition);
0130 
0131         return $this;
0132     }
0133 
0134     /**
0135      * Register a class with the server
0136      *
0137      * @param  string $class
0138      * @param  string $namespace Ignored
0139      * @param  mixed $argv Ignored
0140      * @return Zend_Json_Server
0141      */
0142     public function setClass($class, $namespace = '', $argv = null)
0143     {
0144         $argv = null;
0145         if (3 < func_num_args()) {
0146             $argv = func_get_args();
0147             $argv = array_slice($argv, 3);
0148         }
0149 
0150         // require_once 'Zend/Server/Reflection.php';
0151         $reflection = Zend_Server_Reflection::reflectClass($class, $argv, $namespace);
0152 
0153         foreach ($reflection->getMethods() as $method) {
0154             $definition = $this->_buildSignature($method, $class);
0155             $this->_addMethodServiceMap($definition);
0156         }
0157         return $this;
0158     }
0159 
0160     /**
0161      * Indicate fault response
0162      *
0163      * @param  string $fault
0164      * @param  int $code
0165      * @return false
0166      */
0167     public function fault($fault = null, $code = 404, $data = null)
0168     {
0169         // require_once 'Zend/Json/Server/Error.php';
0170         $error = new Zend_Json_Server_Error($fault, $code, $data);
0171         $this->getResponse()->setError($error);
0172         return $error;
0173     }
0174 
0175     /**
0176      * Handle request
0177      *
0178      * @param  Zend_Json_Server_Request $request
0179      * @return null|Zend_Json_Server_Response
0180      */
0181     public function handle($request = false)
0182     {
0183         if ((false !== $request) && (!$request instanceof Zend_Json_Server_Request)) {
0184             // require_once 'Zend/Json/Server/Exception.php';
0185             throw new Zend_Json_Server_Exception('Invalid request type provided; cannot handle');
0186         } elseif ($request) {
0187             $this->setRequest($request);
0188         }
0189 
0190         // Handle request
0191         $this->_handle();
0192 
0193         // Get response
0194         $response = $this->_getReadyResponse();
0195 
0196         // Emit response?
0197         if ($this->autoEmitResponse()) {
0198             echo $response;
0199             return;
0200         }
0201 
0202         // or return it?
0203         return $response;
0204     }
0205 
0206     /**
0207      * Load function definitions
0208      *
0209      * @param  array|Zend_Server_Definition $definition
0210      * @return void
0211      */
0212     public function loadFunctions($definition)
0213     {
0214         if (!is_array($definition) && (!$definition instanceof Zend_Server_Definition)) {
0215             // require_once 'Zend/Json/Server/Exception.php';
0216             throw new Zend_Json_Server_Exception('Invalid definition provided to loadFunctions()');
0217         }
0218 
0219         foreach ($definition as $key => $method) {
0220             $this->_table->addMethod($method, $key);
0221             $this->_addMethodServiceMap($method);
0222         }
0223     }
0224 
0225     public function setPersistence($mode)
0226     {
0227     }
0228 
0229     /**
0230      * Set request object
0231      *
0232      * @param  Zend_Json_Server_Request $request
0233      * @return Zend_Json_Server
0234      */
0235     public function setRequest(Zend_Json_Server_Request $request)
0236     {
0237         $this->_request = $request;
0238         return $this;
0239     }
0240 
0241     /**
0242      * Get JSON-RPC request object
0243      *
0244      * @return Zend_Json_Server_Request
0245      */
0246     public function getRequest()
0247     {
0248         if (null === ($request = $this->_request)) {
0249             // require_once 'Zend/Json/Server/Request/Http.php';
0250             $this->setRequest(new Zend_Json_Server_Request_Http());
0251         }
0252         return $this->_request;
0253     }
0254 
0255     /**
0256      * Set response object
0257      *
0258      * @param  Zend_Json_Server_Response $response
0259      * @return Zend_Json_Server
0260      */
0261     public function setResponse(Zend_Json_Server_Response $response)
0262     {
0263         $this->_response = $response;
0264         return $this;
0265     }
0266 
0267     /**
0268      * Get response object
0269      *
0270      * @return Zend_Json_Server_Response
0271      */
0272     public function getResponse()
0273     {
0274         if (null === ($response = $this->_response)) {
0275             // require_once 'Zend/Json/Server/Response/Http.php';
0276             $this->setResponse(new Zend_Json_Server_Response_Http());
0277         }
0278         return $this->_response;
0279     }
0280 
0281     /**
0282      * Set flag indicating whether or not to auto-emit response
0283      *
0284      * @param  bool $flag
0285      * @return Zend_Json_Server
0286      */
0287     public function setAutoEmitResponse($flag)
0288     {
0289         $this->_autoEmitResponse = (bool) $flag;
0290         return $this;
0291     }
0292 
0293     /**
0294      * Will we auto-emit the response?
0295      *
0296      * @return bool
0297      */
0298     public function autoEmitResponse()
0299     {
0300         return $this->_autoEmitResponse;
0301     }
0302 
0303     // overloading for SMD metadata
0304     /**
0305      * Overload to accessors of SMD object
0306      *
0307      * @param  string $method
0308      * @param  array $args
0309      * @return mixed
0310      */
0311     public function __call($method, $args)
0312     {
0313         if (preg_match('/^(set|get)/', $method, $matches)) {
0314             if (in_array($method, $this->_getSmdMethods())) {
0315                 if ('set' == $matches[1]) {
0316                     $value = array_shift($args);
0317                     $this->getServiceMap()->$method($value);
0318                     return $this;
0319                 } else {
0320                     return $this->getServiceMap()->$method();
0321                 }
0322             }
0323         }
0324         return null;
0325     }
0326 
0327     /**
0328      * Retrieve SMD object
0329      *
0330      * @return Zend_Json_Server_Smd
0331      */
0332     public function getServiceMap()
0333     {
0334         if (null === $this->_serviceMap) {
0335             // require_once 'Zend/Json/Server/Smd.php';
0336             $this->_serviceMap = new Zend_Json_Server_Smd();
0337         }
0338         return $this->_serviceMap;
0339     }
0340 
0341     /**
0342      * Add service method to service map
0343      *
0344      * @param  Zend_Server_Reflection_Function $method
0345      * @return void
0346      */
0347     protected function _addMethodServiceMap(Zend_Server_Method_Definition $method)
0348     {
0349         $serviceInfo = array(
0350             'name'   => $method->getName(),
0351             'return' => $this->_getReturnType($method),
0352         );
0353         $params = $this->_getParams($method);
0354         $serviceInfo['params'] = $params;
0355         $serviceMap = $this->getServiceMap();
0356         if (false !== $serviceMap->getService($serviceInfo['name'])) {
0357             $serviceMap->removeService($serviceInfo['name']);
0358         }
0359         $serviceMap->addService($serviceInfo);
0360     }
0361 
0362     /**
0363      * Translate PHP type to JSON type
0364      *
0365      * @param  string $type
0366      * @return string
0367      */
0368     protected function _fixType($type)
0369     {
0370         return $type;
0371     }
0372 
0373     /**
0374      * Get default params from signature
0375      *
0376      * @param  array $args
0377      * @param  array $params
0378      * @return array
0379      */
0380     protected function _getDefaultParams(array $args, array $params)
0381     {
0382         $defaultParams = array_slice($params, count($args));
0383         foreach ($defaultParams as $param) {
0384             $value = null;
0385             if (array_key_exists('default', $param)) {
0386                 $value = $param['default'];
0387             }
0388             array_push($args, $value);
0389         }
0390         return $args;
0391     }
0392 
0393     /**
0394      * Get method param type
0395      *
0396      * @param  Zend_Server_Reflection_Function_Abstract $method
0397      * @return string|array
0398      */
0399     protected function _getParams(Zend_Server_Method_Definition $method)
0400     {
0401         $params = array();
0402         foreach ($method->getPrototypes() as $prototype) {
0403             foreach ($prototype->getParameterObjects() as $key => $parameter) {
0404                 if (!isset($params[$key])) {
0405                     $params[$key] = array(
0406                         'type'     => $parameter->getType(),
0407                         'name'     => $parameter->getName(),
0408                         'optional' => $parameter->isOptional(),
0409                     );
0410                     if (null !== ($default = $parameter->getDefaultValue())) {
0411                         $params[$key]['default'] = $default;
0412                     }
0413                     $description = $parameter->getDescription();
0414                     if (!empty($description)) {
0415                         $params[$key]['description'] = $description;
0416                     }
0417                     continue;
0418                 }
0419                 $newType = $parameter->getType();
0420                 if (!is_array($params[$key]['type'])) {
0421                     if ($params[$key]['type'] == $newType) {
0422                         continue;
0423                     }
0424                     $params[$key]['type'] = (array) $params[$key]['type'];
0425                 } elseif (in_array($newType, $params[$key]['type'])) {
0426                     continue;
0427                 }
0428                 array_push($params[$key]['type'], $parameter->getType());
0429             }
0430         }
0431         return $params;
0432     }
0433 
0434     /**
0435      * Set response state
0436      *
0437      * @return Zend_Json_Server_Response
0438      */
0439     protected function _getReadyResponse()
0440     {
0441         $request  = $this->getRequest();
0442         $response = $this->getResponse();
0443 
0444         $response->setServiceMap($this->getServiceMap());
0445         if (null !== ($id = $request->getId())) {
0446             $response->setId($id);
0447         }
0448         if (null !== ($version = $request->getVersion())) {
0449             $response->setVersion($version);
0450         }
0451 
0452         return $response;
0453     }
0454 
0455     /**
0456      * Get method return type
0457      *
0458      * @param  Zend_Server_Reflection_Function_Abstract $method
0459      * @return string|array
0460      */
0461     protected function _getReturnType(Zend_Server_Method_Definition $method)
0462     {
0463         $return = array();
0464         foreach ($method->getPrototypes() as $prototype) {
0465             $return[] = $prototype->getReturnType();
0466         }
0467         if (1 == count($return)) {
0468             return $return[0];
0469         }
0470         return $return;
0471     }
0472 
0473     /**
0474      * Retrieve list of allowed SMD methods for proxying
0475      *
0476      * @return array
0477      */
0478     protected function _getSmdMethods()
0479     {
0480         if (null === $this->_smdMethods) {
0481             $this->_smdMethods = array();
0482             // require_once 'Zend/Json/Server/Smd.php';
0483             $methods = get_class_methods('Zend_Json_Server_Smd');
0484             foreach ($methods as $key => $method) {
0485                 if (!preg_match('/^(set|get)/', $method)) {
0486                     continue;
0487                 }
0488                 if (strstr($method, 'Service')) {
0489                     continue;
0490                 }
0491                 $this->_smdMethods[] = $method;
0492             }
0493         }
0494         return $this->_smdMethods;
0495     }
0496 
0497     /**
0498      * Internal method for handling request
0499      *
0500      * @return void
0501      */
0502     protected function _handle()
0503     {
0504         $request = $this->getRequest();
0505 
0506         if (!$request->isMethodError() && (null === $request->getMethod())) {
0507             return $this->fault('Invalid Request', -32600);
0508         }
0509 
0510         if ($request->isMethodError()) {
0511             return $this->fault('Invalid Request', -32600);
0512         }
0513 
0514         $method = $request->getMethod();
0515         if (!$this->_table->hasMethod($method)) {
0516             return $this->fault('Method not found', -32601);
0517         }
0518 
0519         $params        = $request->getParams();
0520         $invocable     = $this->_table->getMethod($method);
0521         $serviceMap    = $this->getServiceMap();
0522         $service       = $serviceMap->getService($method);
0523         $serviceParams = $service->getParams();
0524 
0525         if (count($params) < count($serviceParams)) {
0526             $params = $this->_getDefaultParams($params, $serviceParams);
0527         }
0528 
0529         //Make sure named parameters are passed in correct order
0530         if ( is_string( key( $params ) ) ) {
0531 
0532             $callback = $invocable->getCallback();
0533             if ('function' == $callback->getType()) {
0534                 $reflection = new ReflectionFunction( $callback->getFunction() );
0535                 $refParams  = $reflection->getParameters();
0536             } else {
0537 
0538                 $reflection = new ReflectionMethod(
0539                     $callback->getClass(),
0540                     $callback->getMethod()
0541                 );
0542                 $refParams = $reflection->getParameters();
0543             }
0544 
0545             $orderedParams = array();
0546             foreach( $reflection->getParameters() as $refParam ) {
0547                 if( isset( $params[ $refParam->getName() ] ) ) {
0548                     $orderedParams[ $refParam->getName() ] = $params[ $refParam->getName() ];
0549                 } elseif( $refParam->isOptional() ) {
0550                     $orderedParams[ $refParam->getName() ] = $refParam->getDefaultValue();
0551                 } else {
0552                     throw new Zend_Server_Exception(
0553                         'Method ' . $request->getMethod() .  ' is missing required parameter: ' . $refParam->getName()
0554                     );
0555                 }
0556             }
0557             $params = $orderedParams;
0558         }
0559 
0560         try {
0561             $result = $this->_dispatch($invocable, $params);
0562         } catch (Exception $e) {
0563             return $this->fault($e->getMessage(), $e->getCode(), $e);
0564         }
0565 
0566         $this->getResponse()->setResult($result);
0567     }
0568 }