Vendoring deprecated composer libs

This commit is contained in:
Frédéric Guillot
2018-06-21 14:13:41 -07:00
parent c73ac5f1f8
commit a491348d44
515 changed files with 5376 additions and 693 deletions

View File

@@ -0,0 +1,198 @@
<?php
namespace JsonRPC;
use Exception;
use JsonRPC\Request\RequestBuilder;
use JsonRPC\Response\ResponseParser;
/**
* JsonRPC client class
*
* @package JsonRPC
* @author Frederic Guillot
*/
class Client
{
/**
* If the only argument passed to a function is an array
* assume it contains named arguments
*
* @access private
* @var boolean
*/
private $isNamedArguments = true;
/**
* Do not immediately throw an exception on error. Return it instead.
*
* @access public
* @var boolean
*/
private $returnException = false;
/**
* True for a batch request
*
* @access private
* @var boolean
*/
private $isBatch = false;
/**
* Batch payload
*
* @access private
* @var array
*/
private $batch = array();
/**
* Http Client
*
* @access private
* @var HttpClient
*/
private $httpClient;
/**
* Constructor
*
* @access public
* @param string $url Server URL
* @param bool $returnException Return exceptions
* @param HttpClient $httpClient HTTP client object
*/
public function __construct($url = '', $returnException = false, HttpClient $httpClient = null)
{
$this->httpClient = $httpClient ?: new HttpClient($url);
$this->returnException = $returnException;
}
/**
* Arguments passed are always positional
*
* @access public
* @return $this
*/
public function withPositionalArguments()
{
$this->isNamedArguments = false;
return $this;
}
/**
* Get HTTP Client
*
* @access public
* @return HttpClient
*/
public function getHttpClient()
{
return $this->httpClient;
}
/**
* Set username and password
*
* @access public
* @param string $username
* @param string $password
* @return $this
*/
public function authentication($username, $password)
{
$this->httpClient
->withUsername($username)
->withPassword($password);
return $this;
}
/**
* Automatic mapping of procedures
*
* @access public
* @param string $method Procedure name
* @param array $params Procedure arguments
* @return mixed
*/
public function __call($method, array $params)
{
if ($this->isNamedArguments && count($params) === 1 && is_array($params[0])) {
$params = $params[0];
}
return $this->execute($method, $params);
}
/**
* Start a batch request
*
* @access public
* @return Client
*/
public function batch()
{
$this->isBatch = true;
$this->batch = array();
return $this;
}
/**
* Send a batch request
*
* @access public
* @return array
*/
public function send()
{
$this->isBatch = false;
return $this->sendPayload('['.implode(', ', $this->batch).']');
}
/**
* Execute a procedure
*
* @access public
* @param string $procedure Procedure name
* @param array $params Procedure arguments
* @param array $reqattrs
* @param string|null $requestId Request Id
* @param string[] $headers Headers for this request
* @return mixed
*/
public function execute($procedure, array $params = array(), array $reqattrs = array(), $requestId = null, array $headers = array())
{
$payload = RequestBuilder::create()
->withProcedure($procedure)
->withParams($params)
->withRequestAttributes($reqattrs)
->withId($requestId)
->build();
if ($this->isBatch) {
$this->batch[] = $payload;
return $this;
}
return $this->sendPayload($payload, $headers);
}
/**
* Send payload
*
* @access private
* @throws Exception
* @param string $payload
* @param string[] $headers
* @return Exception|Client
*/
private function sendPayload($payload, array $headers = array())
{
return ResponseParser::create()
->withReturnException($this->returnException)
->withPayload($this->httpClient->execute($payload, $headers))
->parse();
}
}

View File

@@ -0,0 +1,13 @@
<?php
namespace JsonRPC\Exception;
/**
* Class AccessDeniedException
*
* @package JsonRPC\Exception
* @author Frederic Guillot
*/
class AccessDeniedException extends RpcCallFailedException
{
}

View File

@@ -0,0 +1,13 @@
<?php
namespace JsonRPC\Exception;
/**
* Class AuthenticationFailureException
*
* @package JsonRPC\Exception
* @author Frederic Guillot
*/
class AuthenticationFailureException extends RpcCallFailedException
{
}

View File

@@ -0,0 +1,13 @@
<?php
namespace JsonRPC\Exception;
/**
* Class ConnectionFailureException
*
* @package JsonRPC\Exception
* @author Frederic Guillot
*/
class ConnectionFailureException extends RpcCallFailedException
{
}

View File

@@ -0,0 +1,13 @@
<?php
namespace JsonRPC\Exception;
/**
* Class InvalidJsonFormatException
*
* @package JsonRPC\Exception
* @author Frederic Guillot
*/
class InvalidJsonFormatException extends RpcCallFailedException
{
}

View File

@@ -0,0 +1,13 @@
<?php
namespace JsonRPC\Exception;
/**
* Class InvalidJsonRpcFormatException
*
* @package JsonRPC\Exception
* @author Frederic Guillot
*/
class InvalidJsonRpcFormatException extends RpcCallFailedException
{
}

View File

@@ -0,0 +1,13 @@
<?php
namespace JsonRPC\Exception;
/**
* Class ResponseEncodingFailureException
*
* @package JsonRPC\Exception
* @author Frederic Guillot
*/
class ResponseEncodingFailureException extends RpcCallFailedException
{
}

View File

@@ -0,0 +1,62 @@
<?php
namespace JsonRPC\Exception;
use Exception;
/**
* Class ResponseException
*
* @package JsonRPC\Exception
* @author Frederic Guillot
*/
class ResponseException extends RpcCallFailedException
{
/**
* A value that contains additional information about the error.
*
* @access protected
* @link http://www.jsonrpc.org/specification#error_object
* @var mixed
*/
protected $data;
/**
* Constructor
*
* @access public
* @param string $message [optional] The Exception message to throw.
* @param int $code [optional] The Exception code.
* @param Exception $previous [optional] The previous exception used for the exception chaining. Since 5.3.0
* @param mixed $data [optional] A value that contains additional information about the error.
*/
public function __construct($message = '', $code = 0, Exception $previous = null, $data = null)
{
parent::__construct($message, $code, $previous);
$this->setData($data);
}
/**
* Attach additional information
*
* @access public
* @param mixed $data [optional] A value that contains additional information about the error.
* @return \JsonRPC\Exception\ResponseException
*/
public function setData($data = null)
{
$this->data = $data;
return $this;
}
/**
* Get additional information
*
* @access public
* @return mixed|null
*/
public function getData()
{
return $this->data;
}
}

View File

@@ -0,0 +1,15 @@
<?php
namespace JsonRPC\Exception;
use Exception;
/**
* Class RpcCallFailedException
*
* @package JsonRPC\Exception
* @author Frederic Guillot
*/
class RpcCallFailedException extends Exception
{
}

View File

@@ -0,0 +1,13 @@
<?php
namespace JsonRPC\Exception;
/**
* Class ServerErrorException
*
* @package JsonRPC\Exception
* @author Frederic Guillot
*/
class ServerErrorException extends RpcCallFailedException
{
}

View File

@@ -0,0 +1,449 @@
<?php
namespace JsonRPC;
use Closure;
use JsonRPC\Exception\AccessDeniedException;
use JsonRPC\Exception\ConnectionFailureException;
use JsonRPC\Exception\ServerErrorException;
/**
* Class HttpClient
*
* @package JsonRPC
* @author Frederic Guillot
*/
class HttpClient
{
/**
* URL of the server
*
* @access protected
* @var string
*/
protected $url;
/**
* HTTP client timeout
*
* @access protected
* @var integer
*/
protected $timeout = 5;
/**
* Default HTTP headers to send to the server
*
* @access protected
* @var array
*/
protected $headers = array(
'User-Agent: JSON-RPC PHP Client <https://github.com/fguillot/JsonRPC>',
'Content-Type: application/json',
'Accept: application/json',
'Connection: close',
);
/**
* Username for authentication
*
* @access protected
* @var string
*/
protected $username;
/**
* Password for authentication
*
* @access protected
* @var string
*/
protected $password;
/**
* Enable debug output to the php error log
*
* @access protected
* @var boolean
*/
protected $debug = false;
/**
* Cookies
*
* @access protected
* @var array
*/
protected $cookies = array();
/**
* SSL certificates verification
*
* @access protected
* @var boolean
*/
protected $verifySslCertificate = true;
/**
* SSL client certificate
*
* @access protected
* @var string
*/
protected $sslLocalCert;
/**
* Callback called before the doing the request
*
* @access protected
* @var Closure
*/
protected $beforeRequest;
/**
* HttpClient constructor
*
* @access public
* @param string $url
*/
public function __construct($url = '')
{
$this->url = $url;
}
/**
* Set URL
*
* @access public
* @param string $url
* @return $this
*/
public function withUrl($url)
{
$this->url = $url;
return $this;
}
/**
* 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;
}
/**
* Set timeout
*
* @access public
* @param integer $timeout
* @return $this
*/
public function withTimeout($timeout)
{
$this->timeout = $timeout;
return $this;
}
/**
* Set headers
*
* @access public
* @param array $headers
* @return $this
*/
public function withHeaders(array $headers)
{
$this->headers = array_merge($this->headers, $headers);
return $this;
}
/**
* Set cookies
*
* @access public
* @param array $cookies
* @param boolean $replace
*/
public function withCookies(array $cookies, $replace = false)
{
if ($replace) {
$this->cookies = $cookies;
} else {
$this->cookies = array_merge($this->cookies, $cookies);
}
}
/**
* Enable debug mode
*
* @access public
* @return $this
*/
public function withDebug()
{
$this->debug = true;
return $this;
}
/**
* Disable SSL verification
*
* @access public
* @return $this
*/
public function withoutSslVerification()
{
$this->verifySslCertificate = false;
return $this;
}
/**
* Assign a certificate to use TLS
*
* @access public
* @return $this
*/
public function withSslLocalCert($path)
{
$this->sslLocalCert = $path;
return $this;
}
/**
* Assign a callback before the request
*
* @access public
* @param Closure $closure
* @return $this
*/
public function withBeforeRequestCallback(Closure $closure)
{
$this->beforeRequest = $closure;
return $this;
}
/**
* Get cookies
*
* @access public
* @return array
*/
public function getCookies()
{
return $this->cookies;
}
/**
* Do the HTTP request
*
* @access public
* @throws ConnectionFailureException
* @param string $payload
* @param string[] $headers Headers for this request
* @return array
*/
public function execute($payload, array $headers = array())
{
if (is_callable($this->beforeRequest)) {
call_user_func_array($this->beforeRequest, array($this, $payload, $headers));
}
if ($this->isCurlLoaded()) {
$ch = curl_init();
$requestHeaders = $this->buildHeaders($headers);
$headers = array();
curl_setopt_array($ch, array(
CURLOPT_URL => trim($this->url),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CONNECTTIMEOUT => $this->timeout,
CURLOPT_MAXREDIRS => 2,
CURLOPT_SSL_VERIFYPEER => $this->verifySslCertificate,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $payload,
CURLOPT_HTTPHEADER => $requestHeaders,
CURLOPT_HEADERFUNCTION => function ($curl, $header) use (&$headers) {
$headers[] = $header;
return strlen($header);
}
));
if ($this->sslLocalCert !== null) {
curl_setopt($ch, CURLOPT_CAINFO, $this->sslLocalCert);
}
$response = curl_exec($ch);
curl_close($ch);
if ($response !== false) {
$response = json_decode($response, true);
} else {
throw new ConnectionFailureException('Unable to establish a connection');
}
} else {
$stream = fopen(trim($this->url), 'r', false, $this->buildContext($payload, $headers));
if (! is_resource($stream)) {
throw new ConnectionFailureException('Unable to establish a connection');
}
$metadata = stream_get_meta_data($stream);
$headers = $metadata['wrapper_data'];
$response = json_decode(stream_get_contents($stream), true);
fclose($stream);
}
if ($this->debug) {
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));
}
$this->handleExceptions($headers);
$this->parseCookies($headers);
return $response;
}
/**
* Prepare stream context
*
* @access protected
* @param string $payload
* @param string[] $headers
* @return resource
*/
protected function buildContext($payload, array $headers = array())
{
$headers = $this->buildHeaders($headers);
$options = array(
'http' => array(
'method' => 'POST',
'protocol_version' => 1.1,
'timeout' => $this->timeout,
'max_redirects' => 2,
'header' => implode("\r\n", $headers),
'content' => $payload,
'ignore_errors' => true,
),
'ssl' => array(
'verify_peer' => $this->verifySslCertificate,
'verify_peer_name' => $this->verifySslCertificate,
)
);
if ($this->sslLocalCert !== null) {
$options['ssl']['local_cert'] = $this->sslLocalCert;
}
return stream_context_create($options);
}
/**
* Parse cookies from response
*
* @access protected
* @param array $headers
*/
protected function parseCookies(array $headers)
{
foreach ($headers as $header) {
$pos = stripos($header, 'Set-Cookie:');
if ($pos !== false) {
$cookies = explode(';', substr($header, $pos + 11));
foreach ($cookies as $cookie) {
$item = explode('=', $cookie);
if (count($item) === 2) {
$name = trim($item[0]);
$value = $item[1];
$this->cookies[$name] = $value;
}
}
}
}
}
/**
* Throw an exception according the HTTP response
*
* @access public
* @param array $headers
* @throws AccessDeniedException
* @throws ServerErrorException
*/
public function handleExceptions(array $headers)
{
$exceptions = array(
'401' => '\JsonRPC\Exception\AccessDeniedException',
'403' => '\JsonRPC\Exception\AccessDeniedException',
'404' => '\JsonRPC\Exception\ConnectionFailureException',
'500' => '\JsonRPC\Exception\ServerErrorException',
);
foreach ($headers as $header) {
foreach ($exceptions as $code => $exception) {
if (strpos($header, 'HTTP/1.0 '.$code) !== false || strpos($header, 'HTTP/1.1 '.$code) !== false) {
throw new $exception('Response: '.$header);
}
}
}
}
/**
* Tests if the curl extension is loaded
*
* @access protected
* @return bool
*/
protected function isCurlLoaded()
{
return extension_loaded('curl');
}
/**
* Prepare Headers
*
* @access protected
* @param array $headers
* @return array
*/
protected function buildHeaders(array $headers)
{
$headers = array_merge($this->headers, $headers);
if (!empty($this->username) && !empty($this->password)) {
$headers[] = 'Authorization: Basic ' . base64_encode($this->username . ':' . $this->password);
}
if (!empty($this->cookies)) {
$cookies = array();
foreach ($this->cookies as $key => $value) {
$cookies[] = $key . '=' . $value;
}
$headers[] = 'Cookie: ' . implode('; ', $cookies);
}
return $headers;
}
}

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

@@ -0,0 +1,296 @@
<?php
namespace JsonRPC;
use BadFunctionCallException;
use Closure;
use InvalidArgumentException;
use ReflectionFunction;
use ReflectionMethod;
/**
* Class ProcedureHandler
*
* @package JsonRPC
* @author Frederic Guillot
*/
class ProcedureHandler
{
/**
* List of procedures
*
* @access protected
* @var array
*/
protected $callbacks = array();
/**
* List of classes
*
* @access protected
* @var array
*/
protected $classes = array();
/**
* List of instances
*
* @access protected
* @var array
*/
protected $instances = array();
/**
* Before method name to call
*
* @access protected
* @var string
*/
protected $beforeMethodName = '';
/**
* Register a new procedure
*
* @access public
* @param string $procedure Procedure name
* @param closure $callback Callback
* @return $this
*/
public function withCallback($procedure, Closure $callback)
{
$this->callbacks[$procedure] = $callback;
return $this;
}
/**
* Bind a procedure to a class
*
* @access public
* @param string $procedure Procedure name
* @param mixed $class Class name or instance
* @param string $method Procedure name
* @return $this
*/
public function withClassAndMethod($procedure, $class, $method = '')
{
if ($method === '') {
$method = $procedure;
}
$this->classes[$procedure] = array($class, $method);
return $this;
}
/**
* Bind a class instance
*
* @access public
* @param mixed $instance
* @return $this
*/
public function withObject($instance)
{
$this->instances[] = $instance;
return $this;
}
/**
* Set a before method to call
*
* @access public
* @param string $methodName
* @return $this
*/
public function withBeforeMethod($methodName)
{
$this->beforeMethodName = $methodName;
return $this;
}
/**
* Register multiple procedures from array
*
* @access public
* @param array $callbacks Array with procedure names (array keys) and callbacks (array values)
* @return $this
*/
public function withCallbackArray($callbacks)
{
foreach ($callbacks as $procedure => $callback) {
$this->withCallback($procedure, $callback);
}
return $this;
}
/**
* Bind multiple procedures to classes from array
*
* @access public
* @param array $callbacks Array with procedure names (array keys) and class and method names (array values)
* @return $this
*/
public function withClassAndMethodArray($callbacks)
{
foreach ($callbacks as $procedure => $callback) {
$this->withClassAndMethod($procedure, $callback[0], $callback[1]);
}
return $this;
}
/**
* Execute the procedure
*
* @access public
* @param string $procedure Procedure name
* @param array $params Procedure params
* @return mixed
*/
public function executeProcedure($procedure, array $params = array())
{
if (isset($this->callbacks[$procedure])) {
return $this->executeCallback($this->callbacks[$procedure], $params);
} 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);
}
foreach ($this->instances as $instance) {
if (method_exists($instance, $procedure)) {
return $this->executeMethod($instance, $procedure, $params);
}
}
throw new BadFunctionCallException('Unable to find the procedure');
}
/**
* Execute a callback
*
* @access public
* @param Closure $callback Callback
* @param array $params Procedure params
* @return mixed
*/
public function executeCallback(Closure $callback, $params)
{
$reflection = new ReflectionFunction($callback);
$arguments = $this->getArguments(
$params,
$reflection->getParameters(),
$reflection->getNumberOfRequiredParameters(),
$reflection->getNumberOfParameters()
);
return $reflection->invokeArgs($arguments);
}
/**
* Execute a method
*
* @access public
* @param mixed $class Class name or instance
* @param string $method Method name
* @param array $params Procedure params
* @return mixed
*/
public function executeMethod($class, $method, $params)
{
$instance = is_string($class) ? new $class : $class;
$reflection = new ReflectionMethod($class, $method);
$this->executeBeforeMethod($instance, $method);
$arguments = $this->getArguments(
$params,
$reflection->getParameters(),
$reflection->getNumberOfRequiredParameters(),
$reflection->getNumberOfParameters()
);
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 $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 $requestParams, array $methodParams, $nbRequiredParams, $nbMaxParams)
{
$nbParams = count($requestParams);
if ($nbParams < $nbRequiredParams) {
throw new InvalidArgumentException('Wrong number of arguments');
}
if ($nbParams > $nbMaxParams) {
throw new InvalidArgumentException('Too many arguments');
}
if ($this->isPositionalArguments($requestParams)) {
return $requestParams;
}
return $this->getNamedArguments($requestParams, $methodParams);
}
/**
* Return true if we have positional parameters
*
* @access public
* @param array $request_params Incoming arguments
* @return bool
*/
public function isPositionalArguments(array $request_params)
{
return array_keys($request_params) === range(0, count($request_params) - 1);
}
/**
* Get named arguments
*
* @access public
* @param array $requestParams Incoming arguments
* @param array $methodParams Procedure arguments
* @return array
*/
public function getNamedArguments(array $requestParams, array $methodParams)
{
$params = array();
foreach ($methodParams as $p) {
$name = $p->getName();
if (array_key_exists($name, $requestParams)) {
$params[$name] = $requestParams[$name];
} elseif ($p->isDefaultValueAvailable()) {
$params[$name] = $p->getDefaultValue();
} else {
throw new InvalidArgumentException('Missing argument: '.$name);
}
}
return $params;
}
}

View File

@@ -0,0 +1,55 @@
<?php
namespace JsonRPC\Request;
/**
* Class BatchRequestParser
*
* @package JsonRPC\Request
* @author Frederic Guillot
*/
class BatchRequestParser extends RequestParser
{
/**
* Parse incoming request
*
* @access public
* @return string
*/
public function parse()
{
$responses = array();
foreach ($this->payload as $payload) {
$responses[] = RequestParser::create()
->withPayload($payload)
->withProcedureHandler($this->procedureHandler)
->withMiddlewareHandler($this->middlewareHandler)
->withLocalException($this->localExceptions)
->parse();
}
$responses = array_filter($responses);
return empty($responses) ? '' : '['.implode(',', $responses).']';
}
/**
* Return true if we have a batch request
*
* ex : [
* 0 => '...',
* 1 => '...',
* 2 => '...',
* 3 => '...',
* ]
*
* @static
* @access public
* @param array $payload
* @return bool
*/
public static function isBatchRequest(array $payload)
{
return array_keys($payload) === range(0, count($payload) - 1);
}
}

View File

@@ -0,0 +1,129 @@
<?php
namespace JsonRPC\Request;
/**
* Class RequestBuilder
*
* @package JsonRPC\Request
* @author Frederic Guillot
*/
class RequestBuilder
{
/**
* Request ID
*
* @access private
* @var mixed
*/
private $id = null;
/**
* Method name
*
* @access private
* @var string
*/
private $procedure = '';
/**
* Method arguments
*
* @access private
* @var array
*/
private $params = array();
/**
* Additional request attributes
*
* @access private
* @var array
*/
private $reqattrs = array();
/**
* Get new object instance
*
* @static
* @access public
* @return RequestBuilder
*/
public static function create()
{
return new static();
}
/**
* Set id
*
* @access public
* @param null $id
* @return RequestBuilder
*/
public function withId($id)
{
$this->id = $id;
return $this;
}
/**
* Set method
*
* @access public
* @param string $procedure
* @return RequestBuilder
*/
public function withProcedure($procedure)
{
$this->procedure = $procedure;
return $this;
}
/**
* Set parameters
*
* @access public
* @param array $params
* @return RequestBuilder
*/
public function withParams(array $params)
{
$this->params = $params;
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
*
* @access public
* @return string
*/
public function build()
{
$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;
}
return json_encode($payload);
}
}

View File

@@ -0,0 +1,200 @@
<?php
namespace JsonRPC\Request;
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;
use JsonRPC\Validator\RpcFormatValidator;
/**
* Class RequestParser
*
* @package JsonRPC
* @author Frederic Guillot
*/
class RequestParser
{
/**
* Request payload
*
* @access protected
* @var mixed
*/
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
*
* @access protected
* @var ProcedureHandler
*/
protected $procedureHandler;
/**
* MiddlewareHandler
*
* @access protected
* @var MiddlewareHandler
*/
protected $middlewareHandler;
/**
* Get new object instance
*
* @static
* @access public
* @return RequestParser
*/
public static function create()
{
return new static();
}
/**
* Set payload
*
* @access public
* @param mixed $payload
* @return $this
*/
public function withPayload($payload)
{
$this->payload = $payload;
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
*
* @access public
* @param ProcedureHandler $procedureHandler
* @return $this
*/
public function withProcedureHandler(ProcedureHandler $procedureHandler)
{
$this->procedureHandler = $procedureHandler;
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
*
* @access public
* @return string
* @throws AccessDeniedException
* @throws AuthenticationFailureException
*/
public function parse()
{
try {
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']
);
if (! $this->isNotification()) {
return ResponseBuilder::create()
->withId($this->payload['id'])
->withResult($result)
->build();
}
} catch (Exception $e) {
return $this->handleExceptions($e);
}
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();
}
return '';
}
/**
* Return true if the message is a notification
*
* @access protected
* @return bool
*/
protected function isNotification()
{
return is_array($this->payload) && !isset($this->payload['id']);
}
}

View File

@@ -0,0 +1,336 @@
<?php
namespace JsonRPC\Response;
use BadFunctionCallException;
use Exception;
use InvalidArgumentException;
use JsonRPC\Exception\AccessDeniedException;
use JsonRPC\Exception\AuthenticationFailureException;
use JsonRPC\Exception\InvalidJsonFormatException;
use JsonRPC\Exception\InvalidJsonRpcFormatException;
use JsonRPC\Exception\ResponseEncodingFailureException;
use JsonRPC\Exception\ResponseException;
use JsonRPC\Validator\JsonEncodingValidator;
/**
* Class ResponseBuilder
*
* @package JsonRPC
* @author Frederic Guillot
*/
class ResponseBuilder
{
/**
* Payload ID
*
* @access protected
* @var mixed
*/
protected $id;
/**
* Payload ID
*
* @access protected
* @var mixed
*/
protected $result;
/**
* Payload error code
*
* @access protected
* @var integer
*/
protected $errorCode;
/**
* Payload error message
*
* @access private
* @var string
*/
protected $errorMessage;
/**
* Payload error data
*
* @access protected
* @var mixed
*/
protected $errorData;
/**
* HTTP Headers
*
* @access protected
* @var array
*/
protected $headers = array(
'Content-Type' => 'application/json',
);
/**
* HTTP status
*
* @access protected
* @var string
*/
protected $status;
/**
* Exception
*
* @access protected
* @var ResponseException
*/
protected $exception;
/**
* Get new object instance
*
* @static
* @access public
* @return ResponseBuilder
*/
public static function create()
{
return new static();
}
/**
* Set id
*
* @access public
* @param mixed $id
* @return $this
*/
public function withId($id)
{
$this->id = $id;
return $this;
}
/**
* Set result
*
* @access public
* @param mixed $result
* @return $this
*/
public function withResult($result)
{
$this->result = $result;
return $this;
}
/**
* Set error
*
* @access public
* @param integer $code
* @param string $message
* @param string $data
* @return $this
*/
public function withError($code, $message, $data = '')
{
$this->errorCode = $code;
$this->errorMessage = $message;
$this->errorData = $data;
return $this;
}
/**
* Set exception
*
* @access public
* @param Exception $exception
* @return $this
*/
public function withException(Exception $exception)
{
$this->exception = $exception;
return $this;
}
/**
* Add HTTP header
*
* @access public
* @param string $name
* @param string $value
* @return $this
*/
public function withHeader($name, $value)
{
$this->headers[$name] = $value;
return $this;
}
/**
* Add HTTP Status
*
* @access public
* @param string $status
* @return $this
*/
public function withStatus($status)
{
$this->status = $status;
return $this;
}
/**
* Get status
*
* @access public
* @return string
*/
public function getStatus()
{
return $this->status;
}
/**
* Get headers
*
* @access public
* @return string[]
*/
public function getHeaders()
{
return $this->headers;
}
/**
* Build response
*
* @access public
* @return string
*/
public function build()
{
$options = 0;
if (defined('JSON_UNESCAPED_SLASHES')) {
$options |= JSON_UNESCAPED_SLASHES;
}
if (defined('JSON_UNESCAPED_UNICODE')) {
$options |= JSON_UNESCAPED_UNICODE;
}
$encodedResponse = json_encode($this->buildResponse(), $options);
JsonEncodingValidator::validate();
return $encodedResponse;
}
/**
* Send HTTP headers
*
* @access public
* @return $this
*/
public function sendHeaders()
{
if (! empty($this->status)) {
header($this->status);
}
foreach ($this->headers as $name => $value) {
header($name.': '.$value);
}
return $this;
}
/**
* Build response payload
*
* @access protected
* @return array
*/
protected function buildResponse()
{
$response = array('jsonrpc' => '2.0');
$this->handleExceptions();
if (! empty($this->errorMessage)) {
$response['error'] = $this->buildErrorResponse();
} else {
$response['result'] = $this->result;
}
$response['id'] = $this->id;
return $response;
}
/**
* Build response error payload
*
* @access protected
* @return array
*/
protected function buildErrorResponse()
{
$response = array(
'code' => $this->errorCode,
'message' => $this->errorMessage,
);
if (! empty($this->errorData)) {
$response['data'] = $this->errorData;
}
return $response;
}
/**
* Transform exceptions to JSON-RPC errors
*
* @access protected
*/
protected function handleExceptions()
{
try {
if ($this->exception instanceof Exception) {
throw $this->exception;
}
} catch (InvalidJsonFormatException $e) {
$this->errorCode = -32700;
$this->errorMessage = 'Parse error';
$this->id = null;
} catch (InvalidJsonRpcFormatException $e) {
$this->errorCode = -32600;
$this->errorMessage = 'Invalid Request';
$this->id = null;
} catch (BadFunctionCallException $e) {
$this->errorCode = -32601;
$this->errorMessage = 'Method not found';
} catch (InvalidArgumentException $e) {
$this->errorCode = -32602;
$this->errorMessage = 'Invalid params';
$this->errorData = $this->exception->getMessage();
} catch (ResponseEncodingFailureException $e) {
$this->errorCode = -32603;
$this->errorMessage = 'Internal error';
$this->errorData = $this->exception->getMessage();
} catch (AuthenticationFailureException $e) {
$this->errorCode = 401;
$this->errorMessage = 'Unauthorized';
$this->status = 'HTTP/1.0 401 Unauthorized';
$this->withHeader('WWW-Authenticate', 'Basic realm="JsonRPC"');
} catch (AccessDeniedException $e) {
$this->errorCode = 403;
$this->errorMessage = 'Forbidden';
$this->status = 'HTTP/1.0 403 Forbidden';
} catch (ResponseException $e) {
$this->errorCode = $this->exception->getCode();
$this->errorMessage = $this->exception->getMessage();
$this->errorData = $this->exception->getData();
} catch (Exception $e) {
$this->errorCode = $this->exception->getCode();
$this->errorMessage = $this->exception->getMessage();
}
}
}

View File

@@ -0,0 +1,154 @@
<?php
namespace JsonRPC\Response;
use BadFunctionCallException;
use InvalidArgumentException;
use Exception;
use JsonRPC\Exception\InvalidJsonFormatException;
use JsonRPC\Exception\InvalidJsonRpcFormatException;
use JsonRPC\Exception\ResponseException;
use JsonRPC\Validator\JsonFormatValidator;
/**
* Class ResponseParser
*
* @package JsonRPC\Request
* @author Frederic Guillot
*/
class ResponseParser
{
/**
* Payload
*
* @access private
* @var mixed
*/
private $payload;
/**
* Do not immediately throw an exception on error. Return it instead.
*
* @var bool
*/
private $returnException = false;
/**
* Get new object instance
*
* @static
* @access public
* @return ResponseParser
*/
public static function create()
{
return new static();
}
/**
* Set Return Exception Or Throw It
*
* @param $returnException
* @return ResponseParser
*/
public function withReturnException($returnException)
{
$this->returnException = $returnException;
return $this;
}
/**
* Set payload
*
* @access public
* @param mixed $payload
* @return $this
*/
public function withPayload($payload)
{
$this->payload = $payload;
return $this;
}
/**
* Parse response
*
* @return array|Exception|null
* @throws InvalidJsonFormatException
* @throws BadFunctionCallException
* @throws InvalidJsonRpcFormatException
* @throws InvalidArgumentException
* @throws Exception
* @throws ResponseException
*/
public function parse()
{
JsonFormatValidator::validate($this->payload);
if ($this->isBatchResponse()) {
$results = array();
foreach ($this->payload as $response) {
$results[] = self::create()
->withReturnException($this->returnException)
->withPayload($response)
->parse();
}
return $results;
}
if (isset($this->payload['error']['code'])) {
try {
$this->handleExceptions();
} catch (Exception $e) {
if ($this->returnException) {
return $e;
}
throw $e;
}
}
return isset($this->payload['result']) ? $this->payload['result'] : null;
}
/**
* Handle exceptions
*
* @access private
* @throws InvalidJsonFormatException
* @throws InvalidJsonRpcFormatException
* @throws ResponseException
*/
private function handleExceptions()
{
switch ($this->payload['error']['code']) {
case -32700:
throw new InvalidJsonFormatException('Parse error: '.$this->payload['error']['message']);
case -32600:
throw new InvalidJsonRpcFormatException('Invalid Request: '.$this->payload['error']['message']);
case -32601:
throw new BadFunctionCallException('Procedure not found: '.$this->payload['error']['message']);
case -32602:
throw new InvalidArgumentException('Invalid arguments: '.$this->payload['error']['message']);
default:
throw new ResponseException(
$this->payload['error']['message'],
$this->payload['error']['code'],
null,
isset($this->payload['error']['data']) ? $this->payload['error']['data'] : null
);
}
}
/**
* Return true if we have a batch response
*
* @access private
* @return boolean
*/
private function isBatchResponse()
{
return array_keys($this->payload) === range(0, count($this->payload) - 1);
}
}

View File

@@ -0,0 +1,386 @@
<?php
namespace JsonRPC;
use Closure;
use Exception;
use JsonRPC\Request\BatchRequestParser;
use JsonRPC\Request\RequestParser;
use JsonRPC\Response\ResponseBuilder;
use JsonRPC\Validator\HostValidator;
use JsonRPC\Validator\JsonFormatValidator;
use JsonRPC\Validator\UserValidator;
/**
* JsonRPC server class
*
* @package JsonRPC
* @author Frederic Guillot
*/
class Server
{
/**
* Allowed hosts
*
* @access protected
* @var array
*/
protected $hosts = array();
/**
* Data received from the client
*
* @access protected
* @var array
*/
protected $payload = array();
/**
* List of exceptions that should not be relayed to the client
*
* @access protected
* @var array()
*/
protected $localExceptions = array();
/**
* Username
*
* @access protected
* @var string
*/
protected $username = '';
/**
* Password
*
* @access protected
* @var string
*/
protected $password = '';
/**
* Allowed users
*
* @access protected
* @var array
*/
protected $users = array();
/**
* $_SERVER
*
* @access protected
* @var array
*/
protected $serverVariable;
/**
* ProcedureHandler object
*
* @access protected
* @var 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 ResponseBuilder $responseBuilder
* @param RequestParser $requestParser
* @param BatchRequestParser $batchRequestParser
* @param ProcedureHandler $procedureHandler
* @param MiddlewareHandler $middlewareHandler
*/
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 {
$this->payload = json_decode(file_get_contents('php://input'), true);
}
$this->serverVariable = $server ?: $_SERVER;
$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();
}
/**
* Define alternative authentication header
*
* @access public
* @param string $header Header name
* @return $this
*/
public function setAuthenticationHeader($header)
{
if (! empty($header)) {
$header = 'HTTP_'.str_replace('-', '_', strtoupper($header));
$value = $this->getServerVariable($header);
if (! empty($value)) {
list($this->username, $this->password) = explode(':', base64_decode($value));
}
}
return $this;
}
/**
* Get ProcedureHandler
*
* @access public
* @return ProcedureHandler
*/
public function getProcedureHandler()
{
return $this->procedureHandler;
}
/**
* Get MiddlewareHandler
*
* @access public
* @return MiddlewareHandler
*/
public function getMiddlewareHandler()
{
return $this->middlewareHandler;
}
/**
* Get username
*
* @access public
* @return string
*/
public function getUsername()
{
return $this->username ?: $this->getServerVariable('PHP_AUTH_USER');
}
/**
* Get password
*
* @access public
* @return string
*/
public function getPassword()
{
return $this->password ?: $this->getServerVariable('PHP_AUTH_PW');
}
/**
* IP based client restrictions
*
* @access public
* @param array $hosts List of hosts
* @return $this
*/
public function allowHosts(array $hosts)
{
$this->hosts = $hosts;
return $this;
}
/**
* HTTP Basic authentication
*
* @access public
* @param array $users Dictionary of username/password
* @return $this
*/
public function authentication(array $users)
{
$this->users = $users;
return $this;
}
/**
* Register a new procedure
*
* @access public
* @deprecated Use $server->getProcedureHandler()->withCallback($procedure, $callback)
* @param string $procedure Procedure name
* @param closure $callback Callback
* @return $this
*/
public function register($procedure, Closure $callback)
{
$this->procedureHandler->withCallback($procedure, $callback);
return $this;
}
/**
* 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 $this
*/
public function bind($procedure, $class, $method = '')
{
$this->procedureHandler->withClassAndMethod($procedure, $class, $method);
return $this;
}
/**
* Bind a class instance
*
* @access public
* @deprecated Use $server->getProcedureHandler()->withObject($instance);
* @param mixed $instance Instance name
* @return $this
*/
public function attach($instance)
{
$this->procedureHandler->withObject($instance);
return $this;
}
/**
* Exception classes that should not be relayed to the client
*
* @access public
* @param Exception|string $exception
* @return $this
*/
public function withLocalException($exception)
{
$this->localExceptions[] = $exception;
return $this;
}
/**
* Parse incoming requests
*
* @access public
* @return string
*/
public function execute()
{
try {
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 = $this->handleExceptions($e);
}
$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 protected
* @return string
*/
protected function parseRequest()
{
if (BatchRequestParser::isBatchRequest($this->payload)) {
return $this->batchRequestParser
->withPayload($this->payload)
->withProcedureHandler($this->procedureHandler)
->withMiddlewareHandler($this->middlewareHandler)
->withLocalException($this->localExceptions)
->parse();
}
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 protected
* @param string $variable
* @return string|null
*/
protected function getServerVariable($variable)
{
return isset($this->serverVariable[$variable]) ? $this->serverVariable[$variable] : null;
}
}

View File

@@ -0,0 +1,73 @@
<?php
namespace JsonRPC\Validator;
use JsonRPC\Exception\AccessDeniedException;
/**
* Class HostValidator
*
* @package JsonRPC\Validator
* @author Frederic Guillot
*/
class HostValidator
{
/**
* Validate
*
* @static
* @access public
* @param array $hosts
* @param string $remoteAddress
* @throws AccessDeniedException
*/
public static function validate(array $hosts, $remoteAddress)
{
if (!empty($hosts)) {
foreach ($hosts as $host) {
if (self::ipMatch($remoteAddress, $host)) {
return;
}
}
throw new AccessDeniedException('Access Forbidden');
}
}
/**
* Validate remoteAddress match host
* @param $remoteAddress
* @param $host
* @return bool
*/
public static function ipMatch($remoteAddress, $host)
{
$host = trim($host);
if (strpos($host, '/') !== false) {
list($network, $mask) = explode('/', $host);
if (self::netMatch($remoteAddress, $network, $mask)) {
return true;
}
}
if ($host === $remoteAddress) {
return true;
}
return false;
}
/**
* validate the ipAddress in network
* 192.168.1.1/24
* @param $clientIp
* @param $networkIp
* @param $mask
*
* @return bool
*/
public static function netMatch($clientIp, $networkIp, $mask)
{
$mask1 = 32 - $mask;
return ((ip2long($clientIp) >> $mask1) == (ip2long($networkIp) >> $mask1));
}
}

View File

@@ -0,0 +1,44 @@
<?php
namespace JsonRPC\Validator;
use JsonRPC\Exception\ResponseEncodingFailureException;
/**
* Class JsonEncodingValidator
*
* @package JsonRPC\Validator
* @author Frederic Guillot
*/
class JsonEncodingValidator
{
public static function validate()
{
$jsonError = json_last_error();
if ($jsonError !== JSON_ERROR_NONE) {
switch ($jsonError) {
case JSON_ERROR_DEPTH:
$errorMessage = 'Maximum stack depth exceeded';
break;
case JSON_ERROR_STATE_MISMATCH:
$errorMessage = 'Underflow or the modes mismatch';
break;
case JSON_ERROR_CTRL_CHAR:
$errorMessage = 'Unexpected control character found';
break;
case JSON_ERROR_SYNTAX:
$errorMessage = 'Syntax error, malformed JSON';
break;
case JSON_ERROR_UTF8:
$errorMessage = 'Malformed UTF-8 characters, possibly incorrectly encoded';
break;
default:
$errorMessage = 'Unknown error';
break;
}
throw new ResponseEncodingFailureException($errorMessage, $jsonError);
}
}
}

View File

@@ -0,0 +1,30 @@
<?php
namespace JsonRPC\Validator;
use JsonRPC\Exception\InvalidJsonFormatException;
/**
* Class JsonFormatValidator
*
* @package JsonRPC\Validator
* @author Frederic Guillot
*/
class JsonFormatValidator
{
/**
* Validate
*
* @static
* @access public
* @param mixed $payload
* @throws InvalidJsonFormatException
*/
public static function validate($payload)
{
if (! is_array($payload)) {
throw new InvalidJsonFormatException('Malformed payload');
}
}
}

View File

@@ -0,0 +1,35 @@
<?php
namespace JsonRPC\Validator;
use JsonRPC\Exception\InvalidJsonRpcFormatException;
/**
* Class RpcFormatValidator
*
* @package JsonRPC\Validator
* @author Frederic Guillot
*/
class RpcFormatValidator
{
/**
* Validate
*
* @static
* @access public
* @param array $payload
* @throws InvalidJsonRpcFormatException
*/
public static function validate(array $payload)
{
if (! isset($payload['jsonrpc']) ||
! isset($payload['method']) ||
! is_string($payload['method']) ||
$payload['jsonrpc'] !== '2.0' ||
(isset($payload['params']) && ! is_array($payload['params']))) {
throw new InvalidJsonRpcFormatException('Invalid JSON RPC payload');
}
}
}

View File

@@ -0,0 +1,21 @@
<?php
namespace JsonRPC\Validator;
use JsonRPC\Exception\AuthenticationFailureException;
/**
* Class UserValidator
*
* @package JsonRPC\Validator
* @author Frederic Guillot
*/
class UserValidator
{
public static function validate(array $users, $username, $password)
{
if (! empty($users) && (! isset($users[$username]) || $users[$username] !== $password)) {
throw new AuthenticationFailureException('Access not allowed');
}
}
}