Update JsonRPC library

This commit is contained in:
Frederic Guillot 2016-08-05 20:27:09 -04:00
parent 0b4ee5d347
commit caa91dbf06
No known key found for this signature in database
GPG Key ID: 92D77191BA7FBC99
15 changed files with 569 additions and 264 deletions

View File

@ -13,7 +13,7 @@
},
"require": {
"fguillot/simple-validator": "v1.0.0",
"fguillot/json-rpc": "v1.1.0",
"fguillot/json-rpc": "v1.2.1",
"fguillot/picodb": "v1.0.14 ",
"fguillot/picofeed": "v0.1.24",
"pda/pheanstalk": "v3.1.0"

View File

@ -11,13 +11,15 @@ $server->authentication(array(
Config\get('username') => Config\get('api_token')
));
$procedureHandler = $server->getProcedureHandler();
// Get version
$server->register('app.version', function () {
$procedureHandler->withCallback('app.version', function () {
return array('version' => APP_VERSION);
});
// Get all feeds
$server->register('feed.list', function () {
$procedureHandler->withCallback('feed.list', function () {
$feeds = Model\Feed\get_all();
if (empty($feeds)) {
return array();
@ -36,14 +38,14 @@ $server->register('feed.list', function () {
});
// Get one feed
$server->register('feed.info', function ($feed_id) {
$procedureHandler->withCallback('feed.info', function ($feed_id) {
$result = Model\Feed\get($feed_id);
$result['feed_group_ids'] = Model\Group\get_feed_group_ids($feed_id);
return $result;
});
// Add a new feed
$server->register('feed.create', function($url) {
$procedureHandler->withCallback('feed.create', function($url) {
try {
$result = Model\Feed\create($url);
} catch (Exception $e) {
@ -56,172 +58,172 @@ $server->register('feed.create', function($url) {
});
// Delete a feed
$server->register('feed.delete', function($feed_id) {
$procedureHandler->withCallback('feed.delete', function($feed_id) {
return Model\Feed\remove($feed_id);
});
// Delete all feeds
$server->register('feed.delete_all', function() {
$procedureHandler->withCallback('feed.delete_all', function() {
return Model\Feed\remove_all();
});
// Enable a feed
$server->register('feed.enable', function($feed_id) {
$procedureHandler->withCallback('feed.enable', function($feed_id) {
return Model\Feed\enable($feed_id);
});
// Disable a feed
$server->register('feed.disable', function($feed_id) {
$procedureHandler->withCallback('feed.disable', function($feed_id) {
return Model\Feed\disable($feed_id);
});
// Update a feed
$server->register('feed.update', function($feed_id) {
$procedureHandler->withCallback('feed.update', function($feed_id) {
return Model\Feed\refresh($feed_id);
});
// Get all groups
$server->register('group.list', function () {
$procedureHandler->withCallback('group.list', function () {
return Model\Group\get_all();
});
// Add a new group
$server->register('group.create', function($title) {
$procedureHandler->withCallback('group.create', function($title) {
return Model\Group\create($title);
});
// Get assoc array of group ids with assigned feeds ids
$server->register('group.map', function() {
$procedureHandler->withCallback('group.map', function() {
return Model\Group\get_map();
});
// Get the id of a group
$server->register('group.id', function($title) {
$procedureHandler->withCallback('group.id', function($title) {
return Model\Group\get_group_id($title);
});
// Get all feed ids assigned to a group
$server->register('group.feeds', function($group_id) {
$procedureHandler->withCallback('group.feeds', function($group_id) {
return Model\Group\get_feeds_by_group($group_id);
});
// Add groups to feed
$server->register('group.add', function($feed_id, $group_ids) {
$procedureHandler->withCallback('group.add', function($feed_id, $group_ids) {
return Model\Group\add($feed_id, $group_ids);
});
// Remove groups from feed
$server->register('group.remove', function($feed_id, $group_ids) {
$procedureHandler->withCallback('group.remove', function($feed_id, $group_ids) {
return Model\Group\remove($feed_id, $group_ids);
});
// Remove all groups from feed
$server->register('group.remove_all', function($feed_id) {
$procedureHandler->withCallback('group.remove_all', function($feed_id) {
return Model\Group\remove_all($feed_id);
});
// Update feed group associations
$server->register('group.update_feed_groups', function($feed_id, $group_ids, $create_group = '') {
$procedureHandler->withCallback('group.update_feed_groups', function($feed_id, $group_ids, $create_group = '') {
return Model\Group\update_feed_groups($feed_id, $group_ids, $create_group);
});
// Get all items for a specific feed
$server->register('item.feed.list', function ($feed_id, $offset = null, $limit = null) {
$procedureHandler->withCallback('item.feed.list', function ($feed_id, $offset = null, $limit = null) {
return Model\Item\get_all_by_feed($feed_id, $offset, $limit);
});
// Count all feed items
$server->register('item.feed.count', function ($feed_id) {
$procedureHandler->withCallback('item.feed.count', function ($feed_id) {
return Model\Item\count_by_feed($feed_id);
});
// Get all bookmark items
$server->register('item.bookmark.list', function ($offset = null, $limit = null) {
$procedureHandler->withCallback('item.bookmark.list', function ($offset = null, $limit = null) {
return Model\Item\get_bookmarks($offset, $limit);
});
// Count bookmarks
$server->register('item.bookmark.count', function () {
$procedureHandler->withCallback('item.bookmark.count', function () {
return Model\Item\count_bookmarks();
});
// Add a bookmark
$server->register('item.bookmark.create', function ($item_id) {
$procedureHandler->withCallback('item.bookmark.create', function ($item_id) {
return Model\Item\set_bookmark_value($item_id, 1);
});
// Remove a bookmark
$server->register('item.bookmark.delete', function ($item_id) {
$procedureHandler->withCallback('item.bookmark.delete', function ($item_id) {
return Model\Item\set_bookmark_value($item_id, 0);
});
// Get all unread items
$server->register('item.list_unread', function ($offset = null, $limit = null) {
$procedureHandler->withCallback('item.list_unread', function ($offset = null, $limit = null) {
return Model\Item\get_all_by_status('unread', array(), $offset, $limit);
});
// Count all unread items
$server->register('item.count_unread', function () {
$procedureHandler->withCallback('item.count_unread', function () {
return Model\Item\count_by_status('unread');
});
// Get all read items
$server->register('item.list_read', function ($offset = null, $limit = null) {
$procedureHandler->withCallback('item.list_read', function ($offset = null, $limit = null) {
return Model\Item\get_all_by_status('read', array(), $offset, $limit);
});
// Count all read items
$server->register('item.count_read', function () {
$procedureHandler->withCallback('item.count_read', function () {
return Model\Item\count_by_status('read');
});
// Get one item
$server->register('item.info', function ($item_id) {
$procedureHandler->withCallback('item.info', function ($item_id) {
return Model\Item\get($item_id);
});
// Delete an item
$server->register('item.delete', function($item_id) {
$procedureHandler->withCallback('item.delete', function($item_id) {
return Model\Item\set_removed($item_id);
});
// Mark item as read
$server->register('item.mark_as_read', function($item_id) {
$procedureHandler->withCallback('item.mark_as_read', function($item_id) {
return Model\Item\set_read($item_id);
});
// Mark item as unread
$server->register('item.mark_as_unread', function($item_id) {
$procedureHandler->withCallback('item.mark_as_unread', function($item_id) {
return Model\Item\set_unread($item_id);
});
// Change the status of list of items
$server->register('item.set_list_status', function($status, array $items) {
$procedureHandler->withCallback('item.set_list_status', function($status, array $items) {
return Model\Item\set_status($status, $items);
});
// Flush all read items
$server->register('item.flush', function() {
$procedureHandler->withCallback('item.flush', function() {
return Model\Item\mark_all_as_removed();
});
// Mark all unread items as read
$server->register('item.mark_all_as_read', function() {
$procedureHandler->withCallback('item.mark_all_as_read', function() {
return Model\Item\mark_all_as_read();
});
// Get all items with the content
$server->register('item.get_all', function() {
$procedureHandler->withCallback('item.get_all', function() {
return Model\Item\get_all();
});
// Get all items since a date
$server->register('item.get_all_since', function($timestamp) {
$procedureHandler->withCallback('item.get_all_since', function($timestamp) {
return Model\Item\get_all_since($timestamp);
});
// Get all items id and status
$server->register('item.get_all_status', function() {
$procedureHandler->withCallback('item.get_all_status', function() {
return Model\Item\get_all_status();
});

View File

@ -16,6 +16,8 @@ return array(
'JsonRPC\\Exception\\ResponseException' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/Exception/ResponseException.php',
'JsonRPC\\Exception\\ServerErrorException' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/Exception/ServerErrorException.php',
'JsonRPC\\HttpClient' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/HttpClient.php',
'JsonRPC\\MiddlewareHandler' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/MiddlewareHandler.php',
'JsonRPC\\MiddlewareInterface' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/MiddlewareInterface.php',
'JsonRPC\\ProcedureHandler' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/ProcedureHandler.php',
'JsonRPC\\Request\\BatchRequestParser' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/Request/BatchRequestParser.php',
'JsonRPC\\Request\\RequestBuilder' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/Request/RequestBuilder.php',

View File

@ -88,6 +88,8 @@ class ComposerStaticInitfd7e8d436e1dc450edc3153ac8bc31b4
'JsonRPC\\Exception\\ResponseException' => __DIR__ . '/..' . '/fguillot/json-rpc/src/JsonRPC/Exception/ResponseException.php',
'JsonRPC\\Exception\\ServerErrorException' => __DIR__ . '/..' . '/fguillot/json-rpc/src/JsonRPC/Exception/ServerErrorException.php',
'JsonRPC\\HttpClient' => __DIR__ . '/..' . '/fguillot/json-rpc/src/JsonRPC/HttpClient.php',
'JsonRPC\\MiddlewareHandler' => __DIR__ . '/..' . '/fguillot/json-rpc/src/JsonRPC/MiddlewareHandler.php',
'JsonRPC\\MiddlewareInterface' => __DIR__ . '/..' . '/fguillot/json-rpc/src/JsonRPC/MiddlewareInterface.php',
'JsonRPC\\ProcedureHandler' => __DIR__ . '/..' . '/fguillot/json-rpc/src/JsonRPC/ProcedureHandler.php',
'JsonRPC\\Request\\BatchRequestParser' => __DIR__ . '/..' . '/fguillot/json-rpc/src/JsonRPC/Request/BatchRequestParser.php',
'JsonRPC\\Request\\RequestBuilder' => __DIR__ . '/..' . '/fguillot/json-rpc/src/JsonRPC/Request/RequestBuilder.php',

View File

@ -136,44 +136,6 @@
"beanstalkd"
]
},
{
"name": "fguillot/json-rpc",
"version": "v1.1.0",
"version_normalized": "1.1.0.0",
"source": {
"type": "git",
"url": "https://github.com/fguillot/JsonRPC.git",
"reference": "e915dab71940e7ac251955c785570048f460d332"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/fguillot/JsonRPC/zipball/e915dab71940e7ac251955c785570048f460d332",
"reference": "e915dab71940e7ac251955c785570048f460d332",
"shasum": ""
},
"require": {
"php": ">=5.3.4"
},
"time": "2016-04-27 02:48:10",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-0": {
"JsonRPC": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Frédéric Guillot"
}
],
"description": "Simple Json-RPC client/server library that just works",
"homepage": "https://github.com/fguillot/JsonRPC"
},
{
"name": "fguillot/picofeed",
"version": "v0.1.24",
@ -265,5 +227,46 @@
],
"description": "Minimalist database query builder",
"homepage": "https://github.com/fguillot/picoDb"
},
{
"name": "fguillot/json-rpc",
"version": "v1.2.1",
"version_normalized": "1.2.1.0",
"source": {
"type": "git",
"url": "https://github.com/fguillot/JsonRPC.git",
"reference": "d491bb549bfa11aff4c37abcea2ffb28c9523f69"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/fguillot/JsonRPC/zipball/d491bb549bfa11aff4c37abcea2ffb28c9523f69",
"reference": "d491bb549bfa11aff4c37abcea2ffb28c9523f69",
"shasum": ""
},
"require": {
"php": ">=5.3.4"
},
"require-dev": {
"phpunit/phpunit": "4.8.*"
},
"time": "2016-06-25 23:11:10",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-0": {
"JsonRPC": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Frédéric Guillot"
}
],
"description": "Simple Json-RPC client/server library that just works",
"homepage": "https://github.com/fguillot/JsonRPC"
}
]

View File

@ -155,15 +155,17 @@ class Client
* Execute a procedure
*
* @access public
* @param string $procedure Procedure name
* @param array $params Procedure arguments
* @param string $procedure Procedure name
* @param array $params Procedure arguments
* @param array $reqattrs
* @return mixed
*/
public function execute($procedure, array $params = array())
public function execute($procedure, array $params = array(), array $reqattrs = array())
{
$payload = RequestBuilder::create()
->withProcedure($procedure)
->withParams($params)
->withRequestAttributes($reqattrs)
->build();
if ($this->isBatch) {
@ -184,18 +186,9 @@ class Client
*/
private function sendPayload($payload)
{
try {
return ResponseParser::create()
->withPayload($this->httpClient->execute($payload))
->parse();
} catch (Exception $e) {
if ($this->returnException) {
return $e;
}
throw $e;
}
return ResponseParser::create()
->withReturnException($this->returnException)
->withPayload($this->httpClient->execute($payload))
->parse();
}
}

View File

@ -257,7 +257,7 @@ class HttpClient
$response = json_decode(stream_get_contents($stream), true);
if ($this->debug) {
error_log('==> Request: '.PHP_EOL.json_encode($payload, JSON_PRETTY_PRINT));
error_log('==> Request: '.PHP_EOL.(is_string($payload) ? $payload : json_encode($payload, JSON_PRETTY_PRINT)));
error_log('==> Headers: '.PHP_EOL.var_export($headers, true));
error_log('==> Response: '.PHP_EOL.json_encode($response, JSON_PRETTY_PRINT));
}

View File

@ -0,0 +1,114 @@
<?php
namespace JsonRPC;
/**
* Class MiddlewareHandler
*
* @package JsonRPC
* @author Frederic Guillot
*/
class MiddlewareHandler
{
/**
* Procedure Name
*
* @access protected
* @var string
*/
protected $procedureName = '';
/**
* Username
*
* @access protected
* @var string
*/
protected $username = '';
/**
* Password
*
* @access protected
* @var string
*/
protected $password = '';
/**
* List of middleware to execute before to call the method
*
* @access protected
* @var MiddlewareInterface[]
*/
protected $middleware = array();
/**
* Set username
*
* @access public
* @param string $username
* @return $this
*/
public function withUsername($username)
{
if (! empty($username)) {
$this->username = $username;
}
return $this;
}
/**
* Set password
*
* @access public
* @param string $password
* @return $this
*/
public function withPassword($password)
{
if (! empty($password)) {
$this->password = $password;
}
return $this;
}
/**
* Set procedure name
*
* @access public
* @param string $procedureName
* @return $this
*/
public function withProcedure($procedureName)
{
$this->procedureName = $procedureName;
return $this;
}
/**
* Add a new middleware
*
* @access public
* @param MiddlewareInterface $middleware
* @return MiddlewareHandler
*/
public function withMiddleware(MiddlewareInterface $middleware)
{
$this->middleware[] = $middleware;
return $this;
}
/**
* Execute all middleware
*
* @access public
*/
public function execute()
{
foreach ($this->middleware as $middleware) {
$middleware->execute($this->username, $this->password, $this->procedureName);
}
}
}

View File

@ -0,0 +1,27 @@
<?php
namespace JsonRPC;
use JsonRPC\Exception\AccessDeniedException;
use JsonRPC\Exception\AuthenticationFailureException;
/**
* Interface MiddlewareInterface
*
* @package JsonRPC
* @author Frederic Guillot
*/
interface MiddlewareInterface
{
/**
* Execute Middleware
*
* @access public
* @param string $username
* @param string $password
* @param string $procedureName
* @throws AccessDeniedException
* @throws AuthenticationFailureException
*/
public function execute($username, $password, $procedureName);
}

View File

@ -19,76 +19,34 @@ class ProcedureHandler
/**
* List of procedures
*
* @access private
* @access protected
* @var array
*/
private $callbacks = array();
protected $callbacks = array();
/**
* List of classes
*
* @access private
* @access protected
* @var array
*/
private $classes = array();
protected $classes = array();
/**
* List of instances
*
* @access private
* @access protected
* @var array
*/
private $instances = array();
protected $instances = array();
/**
* Method name to execute before the procedure
* Before method name to call
*
* @access private
* @access protected
* @var string
*/
private $before = '';
/**
* Username
*
* @access private
* @var string
*/
private $username;
/**
* Password
*
* @access private
* @var string
*/
private $password;
/**
* Set username
*
* @access public
* @param string $username
* @return $this
*/
public function withUsername($username)
{
$this->username = $username;
return $this;
}
/**
* Set password
*
* @access public
* @param string $password
* @return $this
*/
public function withPassword($password)
{
$this->password = $password;
return $this;
}
protected $beforeMethodName = '';
/**
* Register a new procedure
@ -96,7 +54,7 @@ class ProcedureHandler
* @access public
* @param string $procedure Procedure name
* @param closure $callback Callback
* @return Server
* @return $this
*/
public function withCallback($procedure, Closure $callback)
{
@ -111,7 +69,7 @@ class ProcedureHandler
* @param string $procedure Procedure name
* @param mixed $class Class name or instance
* @param string $method Procedure name
* @return Server
* @return $this
*/
public function withClassAndMethod($procedure, $class, $method = '')
{
@ -128,7 +86,7 @@ class ProcedureHandler
*
* @access public
* @param mixed $instance
* @return Server
* @return $this
*/
public function withObject($instance)
{
@ -137,15 +95,15 @@ class ProcedureHandler
}
/**
* Attach a method that will be called before the procedure
* Set a before method to call
*
* @access public
* @param string $before
* @return Server
* @param string $methodName
* @return $this
*/
public function withBeforeMethod($before)
public function withBeforeMethod($methodName)
{
$this->before = $before;
$this->beforeMethodName = $methodName;
return $this;
}
@ -161,8 +119,7 @@ class ProcedureHandler
{
if (isset($this->callbacks[$procedure])) {
return $this->executeCallback($this->callbacks[$procedure], $params);
}
else if (isset($this->classes[$procedure]) && method_exists($this->classes[$procedure][0], $this->classes[$procedure][1])) {
} elseif (isset($this->classes[$procedure]) && method_exists($this->classes[$procedure][0], $this->classes[$procedure][1])) {
return $this->executeMethod($this->classes[$procedure][0], $this->classes[$procedure][1], $params);
}
@ -209,19 +166,10 @@ class ProcedureHandler
public function executeMethod($class, $method, $params)
{
$instance = is_string($class) ? new $class : $class;
// Execute before action
if (! empty($this->before)) {
if (is_callable($this->before)) {
call_user_func_array($this->before, array($this->username, $this->password, get_class($class), $method));
}
else if (method_exists($instance, $this->before)) {
$instance->{$this->before}($this->username, $this->password, get_class($class), $method);
}
}
$reflection = new ReflectionMethod($class, $method);
$this->executeBeforeMethod($instance, $method);
$arguments = $this->getArguments(
$params,
$reflection->getParameters(),
@ -232,33 +180,47 @@ class ProcedureHandler
return $reflection->invokeArgs($instance, $arguments);
}
/**
* Execute before method if defined
*
* @access public
* @param mixed $object
* @param string $method
*/
public function executeBeforeMethod($object, $method)
{
if ($this->beforeMethodName !== '' && method_exists($object, $this->beforeMethodName)) {
call_user_func_array(array($object, $this->beforeMethodName), array($method));
}
}
/**
* Get procedure arguments
*
* @access public
* @param array $request_params Incoming arguments
* @param array $method_params Procedure arguments
* @param integer $nb_required_params Number of required parameters
* @param integer $nb_max_params Maximum number of parameters
* @param array $requestParams Incoming arguments
* @param array $methodParams Procedure arguments
* @param integer $nbRequiredParams Number of required parameters
* @param integer $nbMaxParams Maximum number of parameters
* @return array
*/
public function getArguments(array $request_params, array $method_params, $nb_required_params, $nb_max_params)
public function getArguments(array $requestParams, array $methodParams, $nbRequiredParams, $nbMaxParams)
{
$nb_params = count($request_params);
$nbParams = count($requestParams);
if ($nb_params < $nb_required_params) {
if ($nbParams < $nbRequiredParams) {
throw new InvalidArgumentException('Wrong number of arguments');
}
if ($nb_params > $nb_max_params) {
if ($nbParams > $nbMaxParams) {
throw new InvalidArgumentException('Too many arguments');
}
if ($this->isPositionalArguments($request_params)) {
return $request_params;
if ($this->isPositionalArguments($requestParams)) {
return $requestParams;
}
return $this->getNamedArguments($request_params, $method_params);
return $this->getNamedArguments($requestParams, $methodParams);
}
/**
@ -277,24 +239,22 @@ class ProcedureHandler
* Get named arguments
*
* @access public
* @param array $request_params Incoming arguments
* @param array $method_params Procedure arguments
* @param array $requestParams Incoming arguments
* @param array $methodParams Procedure arguments
* @return array
*/
public function getNamedArguments(array $request_params, array $method_params)
public function getNamedArguments(array $requestParams, array $methodParams)
{
$params = array();
foreach ($method_params as $p) {
foreach ($methodParams as $p) {
$name = $p->getName();
if (isset($request_params[$name])) {
$params[$name] = $request_params[$name];
}
else if ($p->isDefaultValueAvailable()) {
if (isset($requestParams[$name])) {
$params[$name] = $requestParams[$name];
} elseif ($p->isDefaultValueAvailable()) {
$params[$name] = $p->getDefaultValue();
}
else {
} else {
throw new InvalidArgumentException('Missing argument: '.$name);
}
}

View File

@ -24,6 +24,8 @@ class BatchRequestParser extends RequestParser
$responses[] = RequestParser::create()
->withPayload($payload)
->withProcedureHandler($this->procedureHandler)
->withMiddlewareHandler($this->middlewareHandler)
->withLocalException($this->localExceptions)
->parse();
}
@ -34,6 +36,13 @@ class BatchRequestParser extends RequestParser
/**
* Return true if we have a batch request
*
* ex : [
* 0 => '...',
* 1 => '...',
* 2 => '...',
* 3 => '...',
* ]
*
* @static
* @access public
* @param array $payload
@ -41,6 +50,6 @@ class BatchRequestParser extends RequestParser
*/
public static function isBatchRequest(array $payload)
{
return is_array($payload) && array_keys($payload) === range(0, count($payload) - 1);
return array_keys($payload) === range(0, count($payload) - 1);
}
}

View File

@ -34,6 +34,14 @@ class RequestBuilder
*/
private $params = array();
/**
* Additional request attributes
*
* @access private
* @var array
*/
private $reqattrs = array();
/**
* Get new object instance
*
@ -85,6 +93,19 @@ class RequestBuilder
return $this;
}
/**
* Set additional request attributes
*
* @access public
* @param array $reqattrs
* @return RequestBuilder
*/
public function withRequestAttributes(array $reqattrs)
{
$this->reqattrs = $reqattrs;
return $this;
}
/**
* Build the payload
*
@ -93,11 +114,11 @@ class RequestBuilder
*/
public function build()
{
$payload = array(
$payload = array_merge_recursive($this->reqattrs, array(
'jsonrpc' => '2.0',
'method' => $this->procedure,
'id' => $this->id ?: mt_rand(),
);
));
if (! empty($this->params)) {
$payload['params'] = $this->params;

View File

@ -6,6 +6,7 @@ use Exception;
use JsonRPC\Exception\AccessDeniedException;
use JsonRPC\Exception\AuthenticationFailureException;
use JsonRPC\Exception\InvalidJsonRpcFormatException;
use JsonRPC\MiddlewareHandler;
use JsonRPC\ProcedureHandler;
use JsonRPC\Response\ResponseBuilder;
use JsonRPC\Validator\JsonFormatValidator;
@ -27,6 +28,17 @@ class RequestParser
*/
protected $payload;
/**
* List of exceptions that should not be relayed to the client
*
* @access protected
* @var array()
*/
protected $localExceptions = array(
'JsonRPC\Exception\AuthenticationFailureException',
'JsonRPC\Exception\AccessDeniedException',
);
/**
* ProcedureHandler
*
@ -35,6 +47,14 @@ class RequestParser
*/
protected $procedureHandler;
/**
* MiddlewareHandler
*
* @access protected
* @var MiddlewareHandler
*/
protected $middlewareHandler;
/**
* Get new object instance
*
@ -60,6 +80,24 @@ class RequestParser
return $this;
}
/**
* Exception classes that should not be relayed to the client
*
* @access public
* @param mixed $exception
* @return $this
*/
public function withLocalException($exception)
{
if (is_array($exception)) {
$this->localExceptions = array_merge($this->localExceptions, $exception);
} else {
$this->localExceptions[] = $exception;
}
return $this;
}
/**
* Set procedure handler
*
@ -73,6 +111,19 @@ class RequestParser
return $this;
}
/**
* Set middleware handler
*
* @access public
* @param MiddlewareHandler $middlewareHandler
* @return $this
*/
public function withMiddlewareHandler(MiddlewareHandler $middlewareHandler)
{
$this->middlewareHandler = $middlewareHandler;
return $this;
}
/**
* Parse incoming request
*
@ -88,6 +139,10 @@ class RequestParser
JsonFormatValidator::validate($this->payload);
RpcFormatValidator::validate($this->payload);
$this->middlewareHandler
->withProcedure($this->payload['method'])
->execute();
$result = $this->procedureHandler->executeProcedure(
$this->payload['method'],
empty($this->payload['params']) ? array() : $this->payload['params']
@ -100,17 +155,33 @@ class RequestParser
->build();
}
} catch (Exception $e) {
return $this->handleExceptions($e);
}
if ($e instanceof AccessDeniedException || $e instanceof AuthenticationFailureException) {
return '';
}
/**
* Handle exceptions
*
* @access protected
* @param Exception $e
* @return string
* @throws Exception
*/
protected function handleExceptions(Exception $e)
{
foreach ($this->localExceptions as $exception) {
if ($e instanceof $exception) {
throw $e;
}
}
if ($e instanceof InvalidJsonRpcFormatException || ! $this->isNotification()) {
return ResponseBuilder::create()
->withId(isset($this->payload['id']) ? $this->payload['id'] : null)
->withException($e)
->build();
}
if ($e instanceof InvalidJsonRpcFormatException || ! $this->isNotification()) {
return ResponseBuilder::create()
->withId(isset($this->payload['id']) ? $this->payload['id'] : null)
->withException($e)
->build();
}
return '';
@ -119,10 +190,10 @@ class RequestParser
/**
* Return true if the message is a notification
*
* @access private
* @access protected
* @return bool
*/
private function isNotification()
protected function isNotification()
{
return is_array($this->payload) && !isset($this->payload['id']);
}

View File

@ -4,6 +4,7 @@ namespace JsonRPC\Response;
use BadFunctionCallException;
use InvalidArgumentException;
use Exception;
use JsonRPC\Exception\InvalidJsonFormatException;
use JsonRPC\Exception\InvalidJsonRpcFormatException;
use JsonRPC\Exception\ResponseException;
@ -25,6 +26,13 @@ class ResponseParser
*/
private $payload;
/**
* Do not immediately throw an exception on error. Return it instead.
*
* @var bool
*/
private $returnException = false;
/**
* Get new object instance
*
@ -37,6 +45,18 @@ class ResponseParser
return new static();
}
/**
* Set Return Exception Or Throw It
*
* @param $returnException
* @return ResponseParser
*/
public function withReturnException($returnException)
{
$this->returnException = $returnException;
return $this;
}
/**
* Set payload
*
@ -53,11 +73,13 @@ class ResponseParser
/**
* Parse response
*
* @access public
* @return array|Exception|null
* @throws InvalidJsonFormatException
* @throws BadFunctionCallException
* @throws InvalidJsonRpcFormatException
* @throws InvalidArgumentException
* @throws Exception
* @throws ResponseException
* @return mixed
*/
public function parse()
{
@ -68,6 +90,7 @@ class ResponseParser
foreach ($this->payload as $response) {
$results[] = self::create()
->withReturnException($this->returnException)
->withPayload($response)
->parse();
}
@ -76,7 +99,14 @@ class ResponseParser
}
if (isset($this->payload['error']['code'])) {
$this->handleExceptions();
try {
$this->handleExceptions();
} catch (Exception $e) {
if ($this->returnException) {
return $e;
}
throw $e;
}
}
return isset($this->payload['result']) ? $this->payload['result'] : null;

View File

@ -22,76 +22,121 @@ class Server
/**
* Allowed hosts
*
* @access private
* @access protected
* @var array
*/
private $hosts = array();
protected $hosts = array();
/**
* Data received from the client
*
* @access private
* @access protected
* @var array
*/
private $payload = array();
protected $payload = array();
/**
* List of exception classes that should be relayed to client
* List of exceptions that should not be relayed to the client
*
* @access private
* @var array
* @access protected
* @var array()
*/
private $exceptions = array();
protected $localExceptions = array();
/**
* Username
*
* @access private
* @access protected
* @var string
*/
private $username = '';
protected $username = '';
/**
* Password
*
* @access private
* @access protected
* @var string
*/
private $password = '';
protected $password = '';
/**
* Allowed users
*
* @access private
* @access protected
* @var array
*/
private $users = array();
protected $users = array();
/**
* $_SERVER
*
* @access private
* @access protected
* @var array
*/
private $serverVariable;
protected $serverVariable;
/**
* ProcedureHandler object
*
* @access private
* @access protected
* @var ProcedureHandler
*/
private $procedureHandler;
protected $procedureHandler;
/**
* MiddlewareHandler object
*
* @access protected
* @var MiddlewareHandler
*/
protected $middlewareHandler;
/**
* Response builder
*
* @access protected
* @var ResponseBuilder
*/
protected $responseBuilder;
/**
* Response builder
*
* @access protected
* @var RequestParser
*/
protected $requestParser;
/**
*
* Batch request parser
*
* @access protected
* @var BatchRequestParser
*/
protected $batchRequestParser;
/**
* Constructor
*
* @access public
* @param string $request
* @param array $server
* @param string $request
* @param array $server
* @param ResponseBuilder $responseBuilder
* @param RequestParser $requestParser
* @param BatchRequestParser $batchRequestParser
* @param ProcedureHandler $procedureHandler
* @param MiddlewareHandler $middlewareHandler
*/
public function __construct($request = '', array $server = array())
{
public function __construct(
$request = '',
array $server = array(),
ResponseBuilder $responseBuilder = null,
RequestParser $requestParser = null,
BatchRequestParser $batchRequestParser = null,
ProcedureHandler $procedureHandler = null,
MiddlewareHandler $middlewareHandler = null
) {
if ($request !== '') {
$this->payload = json_decode($request, true);
} else {
@ -99,7 +144,11 @@ class Server
}
$this->serverVariable = $server ?: $_SERVER;
$this->procedureHandler = new ProcedureHandler();
$this->responseBuilder = $responseBuilder ?: ResponseBuilder::create();
$this->requestParser = $requestParser ?: RequestParser::create();
$this->batchRequestParser = $batchRequestParser ?: BatchRequestParser::create();
$this->procedureHandler = $procedureHandler ?: new ProcedureHandler();
$this->middlewareHandler = $middlewareHandler ?: new MiddlewareHandler();
}
/**
@ -107,7 +156,7 @@ class Server
*
* @access public
* @param string $header Header name
* @return Server
* @return $this
*/
public function setAuthenticationHeader($header)
{
@ -134,6 +183,17 @@ class Server
return $this->procedureHandler;
}
/**
* Get MiddlewareHandler
*
* @access public
* @return MiddlewareHandler
*/
public function getMiddlewareHandler()
{
return $this->middlewareHandler;
}
/**
* Get username
*
@ -161,7 +221,7 @@ class Server
*
* @access public
* @param array $hosts List of hosts
* @return Server
* @return $this
*/
public function allowHosts(array $hosts)
{
@ -174,7 +234,7 @@ class Server
*
* @access public
* @param array $users Dictionary of username/password
* @return Server
* @return $this
*/
public function authentication(array $users)
{
@ -186,9 +246,10 @@ class Server
* Register a new procedure
*
* @access public
* @deprecated Use $server->getProcedureHandler()->withCallback($procedure, $callback)
* @param string $procedure Procedure name
* @param closure $callback Callback
* @return Server
* @return $this
*/
public function register($procedure, Closure $callback)
{
@ -200,10 +261,11 @@ class Server
* Bind a procedure to a class
*
* @access public
* @deprecated Use $server->getProcedureHandler()->withClassAndMethod($procedure, $class, $method);
* @param string $procedure Procedure name
* @param mixed $class Class name or instance
* @param string $method Procedure name
* @return Server
* @return $this
*/
public function bind($procedure, $class, $method = '')
{
@ -215,8 +277,9 @@ class Server
* Bind a class instance
*
* @access public
* @deprecated Use $server->getProcedureHandler()->withObject($instance);
* @param mixed $instance Instance name
* @return Server
* @return $this
*/
public function attach($instance)
{
@ -225,29 +288,15 @@ class Server
}
/**
* Bind an exception
* If this exception occurs it is relayed to the client as JSON-RPC error
* Exception classes that should not be relayed to the client
*
* @access public
* @param mixed $exception Exception class. Defaults to all.
* @return Server
* @param Exception|string $exception
* @return $this
*/
public function attachException($exception = 'Exception')
public function withLocalException($exception)
{
$this->exceptions[] = $exception;
return $this;
}
/**
* Attach a method that will be called before the procedure
*
* @access public
* @param string $before
* @return Server
*/
public function before($before)
{
$this->procedureHandler->withBeforeMethod($before);
$this->localExceptions[] = $exception;
return $this;
}
@ -259,56 +308,78 @@ class Server
*/
public function execute()
{
$responseBuilder = ResponseBuilder::create();
try {
$this->procedureHandler
->withUsername($this->getUsername())
->withPassword($this->getPassword());
JsonFormatValidator::validate($this->payload);
HostValidator::validate($this->hosts, $this->getServerVariable('REMOTE_ADDR'));
UserValidator::validate($this->users, $this->getUsername(), $this->getPassword());
$this->middlewareHandler
->withUsername($this->getUsername())
->withPassword($this->getPassword())
;
$response = $this->parseRequest();
} catch (Exception $e) {
$response = $responseBuilder->withException($e)->build();
$response = $this->handleExceptions($e);
}
$responseBuilder->sendHeaders();
$this->responseBuilder->sendHeaders();
return $response;
}
/**
* Handle exceptions
*
* @access protected
* @param Exception $e
* @return string
* @throws Exception
*/
protected function handleExceptions(Exception $e)
{
foreach ($this->localExceptions as $exception) {
if ($e instanceof $exception) {
throw $e;
}
}
return $this->responseBuilder->withException($e)->build();
}
/**
* Parse incoming request
*
* @access private
* @access protected
* @return string
*/
private function parseRequest()
protected function parseRequest()
{
if (BatchRequestParser::isBatchRequest($this->payload)) {
return BatchRequestParser::create()
return $this->batchRequestParser
->withPayload($this->payload)
->withProcedureHandler($this->procedureHandler)
->withMiddlewareHandler($this->middlewareHandler)
->withLocalException($this->localExceptions)
->parse();
}
return RequestParser::create()
return $this->requestParser
->withPayload($this->payload)
->withProcedureHandler($this->procedureHandler)
->withMiddlewareHandler($this->middlewareHandler)
->withLocalException($this->localExceptions)
->parse();
}
/**
* Check existence and get value of server variable
*
* @access private
* @access protected
* @param string $variable
* @return string|null
*/
private function getServerVariable($variable)
protected function getServerVariable($variable)
{
return isset($this->serverVariable[$variable]) ? $this->serverVariable[$variable] : null;
}