3 * Zend Framework (http://framework.zend.com/)
5 * @link http://github.com/zendframework/zf2 for the canonical source repository
6 * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com)
7 * @license http://framework.zend.com/license/new-bsd New BSD License
10 namespace Zend\Json\Server
;
12 use ReflectionFunction
;
14 use Zend\Server\AbstractServer
;
15 use Zend\Server\Definition
;
16 use Zend\Server\Method
;
17 use Zend\Server\Reflection
;
19 class Server
extends AbstractServer
24 const VERSION_1
= '1.0';
25 const VERSION_2
= '2.0';
29 * Flag: whether or not to auto-emit the response
32 protected $returnResponse = false;
35 * Inherited from Zend\Server\AbstractServer
37 * @var bool Flag; allow overwriting existing methods when creating server definition
39 protected $overwriteExistingMethods = true;
57 protected $serviceMap;
63 protected $smdMethods;
66 * Attach a function or callback to the server
68 * @param string|array|callable $function Valid PHP callback
69 * @param string $namespace Ignored
70 * @throws Exception\InvalidArgumentException if function invalid or not callable
73 public function addFunction($function, $namespace = '')
75 if (!is_string($function) && (!is_array($function) ||
(2 > count($function)))) {
76 throw new Exception\
InvalidArgumentException('Unable to attach function; invalid');
79 if (!is_callable($function)) {
80 throw new Exception\
InvalidArgumentException('Unable to attach function; does not exist');
84 if (2 < func_num_args()) {
85 $argv = func_get_args();
86 $argv = array_slice($argv, 2);
90 if (is_string($function)) {
91 $method = Reflection
::reflectFunction($function, $argv, $namespace);
93 $class = array_shift($function);
94 $action = array_shift($function);
95 $reflection = Reflection
::reflectClass($class, $argv, $namespace);
96 $methods = $reflection->getMethods();
98 foreach ($methods as $method) {
99 if ($action == $method->getName()) {
105 $this->fault('Method not found', Error
::ERROR_INVALID_METHOD
);
110 $definition = $this->_buildSignature($method, $class);
111 $this->_addMethodServiceMap($definition);
117 * Register a class with the server
119 * @param string $class
120 * @param string $namespace Ignored
121 * @param mixed $argv Ignored
124 public function setClass($class, $namespace = '', $argv = null)
126 if (2 < func_num_args()) {
127 $argv = func_get_args();
128 $argv = array_slice($argv, 2);
131 $reflection = Reflection
::reflectClass($class, $argv, $namespace);
133 foreach ($reflection->getMethods() as $method) {
134 $definition = $this->_buildSignature($method, $class);
135 $this->_addMethodServiceMap($definition);
141 * Indicate fault response
143 * @param string $fault
148 public function fault($fault = null, $code = 404, $data = null)
150 $error = new Error($fault, $code, $data);
151 $this->getResponse()->setError($error);
158 * @param Request $request
159 * @return null|Response
160 * @throws Exception\InvalidArgumentException
162 public function handle($request = false)
164 if ((false !== $request) && (!$request instanceof Request
)) {
165 throw new Exception\
InvalidArgumentException('Invalid request type provided; cannot handle');
166 } elseif ($request) {
167 $this->setRequest($request);
174 $response = $this->_getReadyResponse();
177 if (!$this->returnResponse
) {
187 * Load function definitions
189 * @param array|Definition $definition
190 * @throws Exception\InvalidArgumentException
193 public function loadFunctions($definition)
195 if (!is_array($definition) && (!$definition instanceof Definition
)) {
196 throw new Exception\
InvalidArgumentException('Invalid definition provided to loadFunctions()');
199 foreach ($definition as $key => $method) {
200 $this->table
->addMethod($method, $key);
201 $this->_addMethodServiceMap($method);
205 public function setPersistence($mode)
212 * @param Request $request
215 public function setRequest(Request
$request)
217 $this->request
= $request;
222 * Get JSON-RPC request object
226 public function getRequest()
228 if (null === ($request = $this->request
)) {
229 $this->setRequest(new Request\
Http());
231 return $this->request
;
235 * Set response object
237 * @param Response $response
240 public function setResponse(Response
$response)
242 $this->response
= $response;
247 * Get response object
251 public function getResponse()
253 if (null === ($response = $this->response
)) {
254 $this->setResponse(new Response\
Http());
256 return $this->response
;
260 * Set return response flag
262 * If true, {@link handle()} will return the response instead of
263 * automatically sending it back to the requesting client.
265 * The response is always available via {@link getResponse()}.
270 public function setReturnResponse($flag = true)
272 $this->returnResponse
= ($flag) ?
true : false;
277 * Retrieve return response flag
281 public function getReturnResponse()
283 return $this->returnResponse
;
286 // overloading for SMD metadata
288 * Overload to accessors of SMD object
290 * @param string $method
294 public function __call($method, $args)
296 if (preg_match('/^(set|get)/', $method, $matches)) {
297 if (in_array($method, $this->_getSmdMethods())) {
298 if ('set' == $matches[1]) {
299 $value = array_shift($args);
300 $this->getServiceMap()->$method($value);
303 return $this->getServiceMap()->$method();
311 * Retrieve SMD object
315 public function getServiceMap()
317 if (null === $this->serviceMap
) {
318 $this->serviceMap
= new Smd();
320 return $this->serviceMap
;
324 * Add service method to service map
326 * @param Method\Definition $method
329 protected function _addMethodServiceMap(Method\Definition
$method)
331 $serviceInfo = array(
332 'name' => $method->getName(),
333 'return' => $this->_getReturnType($method),
335 $params = $this->_getParams($method);
336 $serviceInfo['params'] = $params;
337 $serviceMap = $this->getServiceMap();
338 if (false !== $serviceMap->getService($serviceInfo['name'])) {
339 $serviceMap->removeService($serviceInfo['name']);
341 $serviceMap->addService($serviceInfo);
345 * Translate PHP type to JSON type
347 * @param string $type
350 protected function _fixType($type)
356 * Get default params from signature
359 * @param array $params
362 protected function _getDefaultParams(array $args, array $params)
364 $defaultParams = array_slice($params, count($args));
365 foreach ($defaultParams as $param) {
367 if (array_key_exists('default', $param)) {
368 $value = $param['default'];
370 $args[$param['name']] = $value;
376 * Get method param type
378 * @param Method\Definition $method
379 * @return string|array
381 protected function _getParams(Method\Definition
$method)
384 foreach ($method->getPrototypes() as $prototype) {
385 foreach ($prototype->getParameterObjects() as $key => $parameter) {
386 if (!isset($params[$key])) {
387 $params[$key] = array(
388 'type' => $parameter->getType(),
389 'name' => $parameter->getName(),
390 'optional' => $parameter->isOptional(),
392 if (null !== ($default = $parameter->getDefaultValue())) {
393 $params[$key]['default'] = $default;
395 $description = $parameter->getDescription();
396 if (!empty($description)) {
397 $params[$key]['description'] = $description;
401 $newType = $parameter->getType();
402 if (!is_array($params[$key]['type'])) {
403 if ($params[$key]['type'] == $newType) {
406 $params[$key]['type'] = (array) $params[$key]['type'];
407 } elseif (in_array($newType, $params[$key]['type'])) {
410 array_push($params[$key]['type'], $parameter->getType());
421 protected function _getReadyResponse()
423 $request = $this->getRequest();
424 $response = $this->getResponse();
426 $response->setServiceMap($this->getServiceMap());
427 if (null !== ($id = $request->getId())) {
428 $response->setId($id);
430 if (null !== ($version = $request->getVersion())) {
431 $response->setVersion($version);
438 * Get method return type
440 * @param Method\Definition $method
441 * @return string|array
443 protected function _getReturnType(Method\Definition
$method)
446 foreach ($method->getPrototypes() as $prototype) {
447 $return[] = $prototype->getReturnType();
449 if (1 == count($return)) {
456 * Retrieve list of allowed SMD methods for proxying
460 protected function _getSmdMethods()
462 if (null === $this->smdMethods
) {
463 $this->smdMethods
= array();
464 $methods = get_class_methods('Zend\\Json\\Server\\Smd');
465 foreach ($methods as $method) {
466 if (!preg_match('/^(set|get)/', $method)) {
469 if (strstr($method, 'Service')) {
472 $this->smdMethods
[] = $method;
475 return $this->smdMethods
;
479 * Internal method for handling request
483 protected function _handle()
485 $request = $this->getRequest();
487 if($request->isParseError()){
488 return $this->fault('Parse error', Error
::ERROR_PARSE
);
491 if (!$request->isMethodError() && (null === $request->getMethod())) {
492 return $this->fault('Invalid Request', Error
::ERROR_INVALID_REQUEST
);
495 if ($request->isMethodError()) {
496 return $this->fault('Invalid Request', Error
::ERROR_INVALID_REQUEST
);
499 $method = $request->getMethod();
500 if (!$this->table
->hasMethod($method)) {
501 return $this->fault('Method not found', Error
::ERROR_INVALID_METHOD
);
504 $params = $request->getParams();
505 $invokable = $this->table
->getMethod($method);
506 $serviceMap = $this->getServiceMap();
507 $service = $serviceMap->getService($method);
508 $serviceParams = $service->getParams();
510 if (count($params) < count($serviceParams)) {
511 $params = $this->_getDefaultParams($params, $serviceParams);
514 //Make sure named parameters are passed in correct order
515 if (is_string(key($params))) {
516 $callback = $invokable->getCallback();
517 if ('function' == $callback->getType()) {
518 $reflection = new ReflectionFunction($callback->getFunction());
521 $reflection = new ReflectionMethod(
522 $callback->getClass(),
523 $callback->getMethod()
527 $orderedParams = array();
528 foreach ($reflection->getParameters() as $refParam) {
529 if (array_key_exists($refParam->getName(), $params)) {
530 $orderedParams[$refParam->getName()] = $params[$refParam->getName()];
531 } elseif ($refParam->isOptional()) {
532 $orderedParams[$refParam->getName()] = null;
534 return $this->fault('Invalid params', Error
::ERROR_INVALID_PARAMS
);
537 $params = $orderedParams;
541 $result = $this->_dispatch($invokable, $params);
542 } catch (\Exception
$e) {
543 return $this->fault($e->getMessage(), $e->getCode(), $e);
546 $this->getResponse()->setResult($result);