diff options
author | Frédéric Guillot <fred@kanboard.net> | 2014-06-20 15:41:05 -0300 |
---|---|---|
committer | Frédéric Guillot <fred@kanboard.net> | 2014-06-20 15:41:05 -0300 |
commit | 7c5b900bd83b6b9bdb5656eb169381ff46f8106a (patch) | |
tree | 39481ff2ee73d7479369655ba86d343f302e1499 /vendor/JsonRPC | |
parent | efdc959c555872677e599d2ff12e1263d719f3f2 (diff) |
First API implementation
Diffstat (limited to 'vendor/JsonRPC')
-rw-r--r-- | vendor/JsonRPC/Client.php | 174 | ||||
-rw-r--r-- | vendor/JsonRPC/Server.php | 301 |
2 files changed, 475 insertions, 0 deletions
diff --git a/vendor/JsonRPC/Client.php b/vendor/JsonRPC/Client.php new file mode 100644 index 00000000..bbdb7200 --- /dev/null +++ b/vendor/JsonRPC/Client.php @@ -0,0 +1,174 @@ +<?php + +namespace JsonRPC; + +/** + * JsonRPC client class + * + * @package JsonRPC + * @author Frderic Guillot + * @license Unlicense http://unlicense.org/ + */ +class Client +{ + /** + * URL of the server + * + * @access private + * @var string + */ + private $url; + + /** + * HTTP client timeout + * + * @access private + * @var integer + */ + private $timeout; + + /** + * Debug flag + * + * @access private + * @var bool + */ + private $debug; + + /** + * Username for authentication + * + * @access private + * @var string + */ + private $username; + + /** + * Password for authentication + * + * @access private + * @var string + */ + private $password; + + /** + * Default HTTP headers to send to the server + * + * @access private + * @var array + */ + private $headers = array( + 'Connection: close', + 'Content-Type: application/json', + 'Accept: application/json' + ); + + /** + * Constructor + * + * @access public + * @param string $url Server URL + * @param integer $timeout Server URL + * @param bool $debug Debug flag + * @param array $headers Custom HTTP headers + */ + public function __construct($url, $timeout = 5, $debug = false, $headers = array()) + { + $this->url = $url; + $this->timeout = $timeout; + $this->debug = $debug; + $this->headers = array_merge($this->headers, $headers); + } + + /** + * Automatic mapping of procedures + * + * @access public + * @param string $method Procedure name + * @param array $params Procedure arguments + * @return mixed + */ + public function __call($method, $params) + { + return $this->execute($method, $params); + } + + /** + * Set authentication parameters + * + * @access public + * @param string $username Username + * @param string $password Password + */ + public function authentication($username, $password) + { + $this->username = $username; + $this->password = $password; + } + + /** + * Execute + * + * @access public + * @param string $procedure Procedure name + * @param array $params Procedure arguments + * @return mixed + */ + public function execute($procedure, array $params = array()) + { + $id = mt_rand(); + + $payload = array( + 'jsonrpc' => '2.0', + 'method' => $procedure, + 'id' => $id + ); + + if (! empty($params)) { + $payload['params'] = $params; + } + + $result = $this->doRequest($payload); + + if (isset($result['id']) && $result['id'] == $id && array_key_exists('result', $result)) { + return $result['result']; + } + else if ($this->debug && isset($result['error'])) { + print_r($result['error']); + } + + return null; + } + + /** + * Do the HTTP request + * + * @access public + * @param string $payload Data to send + */ + public function doRequest($payload) + { + $ch = curl_init(); + + curl_setopt($ch, CURLOPT_URL, $this->url); + curl_setopt($ch, CURLOPT_HEADER, false); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $this->timeout); + curl_setopt($ch, CURLOPT_USERAGENT, 'JSON-RPC PHP Client'); + curl_setopt($ch, CURLOPT_HTTPHEADER, $this->headers); + curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false); + curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST'); + curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload)); + + if ($this->username && $this->password) { + curl_setopt($ch, CURLOPT_USERPWD, $this->username.':'.$this->password); + } + + $result = curl_exec($ch); + $response = json_decode($result, true); + + curl_close($ch); + + return is_array($response) ? $response : array(); + } +} diff --git a/vendor/JsonRPC/Server.php b/vendor/JsonRPC/Server.php new file mode 100644 index 00000000..93d46cdb --- /dev/null +++ b/vendor/JsonRPC/Server.php @@ -0,0 +1,301 @@ +<?php + +namespace JsonRPC; + +use ReflectionFunction; +use Closure; + +/** + * JsonRPC server class + * + * @package JsonRPC + * @author Frderic Guillot + * @license Unlicense http://unlicense.org/ + */ +class Server +{ + /** + * Data received from the client + * + * @access private + * @var string + */ + private $payload; + + /** + * List of procedures + * + * @static + * @access private + * @var array + */ + static private $procedures = array(); + + /** + * Constructor + * + * @access public + * @param string $payload Client data + */ + public function __construct($payload = '') + { + $this->payload = $payload; + } + + /** + * IP based client restrictions + * + * Return an HTTP error 403 if the client is not allowed + * + * @access public + * @param array $hosts List of hosts + */ + public function allowHosts(array $hosts) { + + if (! in_array($_SERVER['REMOTE_ADDR'], $hosts)) { + + header('Content-Type: application/json'); + header('HTTP/1.0 403 Forbidden'); + echo '["Access Forbidden"]'; + exit; + } + } + + /** + * HTTP Basic authentication + * + * Return an HTTP error 401 if the client is not allowed + * + * @access public + * @param array $users Map of username/password + */ + public function authentication(array $users) + { + // OVH workaround + if (isset($_SERVER['REMOTE_USER'])) { + list($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']) = explode(':', base64_decode(substr($_SERVER['REMOTE_USER'], 6))); + } + + if (! isset($_SERVER['PHP_AUTH_USER']) || + ! isset($users[$_SERVER['PHP_AUTH_USER']]) || + $users[$_SERVER['PHP_AUTH_USER']] !== $_SERVER['PHP_AUTH_PW']) { + + header('WWW-Authenticate: Basic realm="JsonRPC"'); + header('Content-Type: application/json'); + header('HTTP/1.0 401 Unauthorized'); + echo '["Authentication failed"]'; + exit; + } + } + + /** + * Register a new procedure + * + * @access public + * @param string $name Procedure name + * @param closure $callback Callback + */ + public function register($name, Closure $callback) + { + self::$procedures[$name] = $callback; + } + + /** + * Unregister a procedure + * + * @access public + * @param string $name Procedure name + */ + public function unregister($name) + { + if (isset(self::$procedures[$name])) { + unset(self::$procedures[$name]); + } + } + + /** + * Unregister all procedures + * + * @access public + */ + public function unregisterAll() + { + self::$procedures = array(); + } + + /** + * Return the response to the client + * + * @access public + * @param array $data Data to send to the client + * @param array $payload Incoming data + * @return string + */ + public function getResponse(array $data, array $payload = array()) + { + if (! array_key_exists('id', $payload)) { + return ''; + } + + $response = array( + 'jsonrpc' => '2.0', + 'id' => $payload['id'] + ); + + $response = array_merge($response, $data); + + @header('Content-Type: application/json'); + return json_encode($response); + } + + /** + * Map arguments to the procedure + * + * @access public + * @param array $request_params Incoming arguments + * @param array $method_params Procedure arguments + * @param array $params Arguments to pass to the callback + * @return bool + */ + public function mapParameters(array $request_params, array $method_params, array &$params) + { + // Positional parameters + if (array_keys($request_params) === range(0, count($request_params) - 1)) { + + if (count($request_params) !== count($method_params)) return false; + $params = $request_params; + + return true; + } + + // Named parameters + foreach ($method_params as $p) { + + $name = $p->getName(); + + if (isset($request_params[$name])) { + $params[$name] = $request_params[$name]; + } + else if ($p->isDefaultValueAvailable()) { + continue; + } + else { + return false; + } + } + + return true; + } + + /** + * Parse incoming requests + * + * @access public + * @return string + */ + public function execute() + { + // Parse payload + if (empty($this->payload)) { + $this->payload = file_get_contents('php://input'); + } + + if (is_string($this->payload)) { + $this->payload = json_decode($this->payload, true); + } + + // Check JSON format + if (! is_array($this->payload)) { + + return $this->getResponse(array( + 'error' => array( + 'code' => -32700, + 'message' => 'Parse error' + )), + array('id' => null) + ); + } + + // Handle batch request + if (array_keys($this->payload) === range(0, count($this->payload) - 1)) { + + $responses = array(); + + foreach ($this->payload as $payload) { + + if (! is_array($payload)) { + + $responses[] = $this->getResponse(array( + 'error' => array( + 'code' => -32600, + 'message' => 'Invalid Request' + )), + array('id' => null) + ); + } + else { + + $server = new Server($payload); + $response = $server->execute(); + + if ($response) $responses[] = $response; + } + } + + return empty($responses) ? '' : '['.implode(',', $responses).']'; + } + + // Check JSON-RPC format + if (! isset($this->payload['jsonrpc']) || + ! isset($this->payload['method']) || + ! is_string($this->payload['method']) || + $this->payload['jsonrpc'] !== '2.0' || + (isset($this->payload['params']) && ! is_array($this->payload['params']))) { + + return $this->getResponse(array( + 'error' => array( + 'code' => -32600, + 'message' => 'Invalid Request' + )), + array('id' => null) + ); + } + + // Procedure not found + if (! isset(self::$procedures[$this->payload['method']])) { + + return $this->getResponse(array( + 'error' => array( + 'code' => -32601, + 'message' => 'Method not found' + )), + $this->payload + ); + } + + $callback = self::$procedures[$this->payload['method']]; + $params = array(); + + $reflection = new ReflectionFunction($callback); + + if (isset($this->payload['params'])) { + + $parameters = $reflection->getParameters(); + + if (! $this->mapParameters($this->payload['params'], $parameters, $params)) { + + return $this->getResponse(array( + 'error' => array( + 'code' => -32602, + 'message' => 'Invalid params' + )), + $this->payload + ); + } + } + + $result = $reflection->invokeArgs($params); + + return $this->getResponse(array('result' => $result), $this->payload); + } +} |