관리-도구
편집 파일: Client.php
<?php namespace JsonRPC; use RuntimeException; use BadFunctionCallException; use InvalidArgumentException; const TRANSPORT_TCP = 0; const TRANSPORT_HTTP = 1; class Client { /** * RPC transport type * * @access private * @var integer */ private $transport; /** * Path to the unix socket of the server * * @access private * @var string */ private $unix_socket_path; /** * HTTP client timeout * * @access private * @var integer */ private $timeout; /** * Enable debug output to the php error log * * @access public * @var boolean */ public $debug = false; /** * User that runs the script * @var string * @access private */ private $user; /** * Default HTTP headers to send to the server * * @access private * @var array */ private $headers = "POST / HTTP/1.1\r\nHost: localhost\r\nContent-Type: application/json\r\nAccept: application/json\r\n"; /** * Stream handle * * @access private */ private $ch; /** * Constructor * * @access public * @param string $unix_socket_path Path to the unix socket * @param integer $timeout Timeout */ public function __construct($unix_socket_path, $timeout = 5, $transport = TRANSPORT_TCP) { $this->unix_socket_path = 'unix://'.$unix_socket_path; $this->timeout = $timeout; $this->transport = $transport; $this->ch = stream_socket_client($this->unix_socket_path, $errno, $errstr, $this->timeout); if (!$this->ch) { throw new RuntimeException("$errstr ($errno)"); } } /** * Destructor * * @access public */ public function __destruct() { fclose($this->ch); } /** * Automatic mapping of methods * * @access public * @param string $method Method name * @param array $params Method arguments * @return array */ public function __call($method, array $params) { return $this->execute($method, $params); } /** * Execute a method * * @access public * @param string $method Method name * @param array $params Method arguments * @return array */ public function execute($method, array $params = array()) { return $this->parseResponse( $this->doRequest($this->prepareRequest($method, $params)) ); } /** * Prepare the payload * * @access public * @param string $method Method name * @param array $params Method arguments * @return array */ public function prepareRequest($method, array $params = array()) { $payload = array( 'jsonrpc' => '2.0', 'method' => $method, 'id' => mt_rand() ); if (! empty($params)) { $payload['params'] = $params; } return $payload; } /** * Parse the response and return the method result * * @access public * @param array $payload * @return mixed */ public function parseResponse(array $payload) { return $this->getResult($payload); } /** * Get a RPC call result * * @access public * @param array $payload * @return mixed */ public function getResult(array $payload) { if (isset($payload['error']['code'])) { $this->handleRpcErrors($payload['error']); } return isset($payload['result']) ? $payload['result'] : null; } /** * Throw an exception according the RPC error * * @access public * @param integer $code */ public function handleRpcErrors($error) { switch ($error['code']) { case -32601: throw new BadFunctionCallException('Method not found: '. $error['message']); case -32602: throw new InvalidArgumentException('Invalid arguments: '. $error['message']); default: throw new RuntimeException('Invalid request/response: '. $error['message'], $error['code']); } } /** * Prepare http message * * @access private * @param string $headers * @param string $body * @return string */ private function prepareHttpMessage($headers, $body) { return $headers . "Content-Length: ".strlen($body)."\r\n\r\n".$body; } /** * Parse http message * * @access private * @param string $text * @return array */ private function parseHttpMessage($text) { $data = array(); $response = explode("\r\n", $text); $header = preg_split("/\s+/", $response[0]); $data["code"] = $header[1]; $data["body"] = ""; $is_body = false; foreach (array_slice($response, 1) as $str) { if ($is_body) { $data["body"] .= $str; continue; } $header = preg_split("/:\s+/", $str); if ($header[0]) { $data[$header[0]] = $header[1]; continue; } $is_body = true; } return $data; } /** * Do the HTTP request * * @access public * @param array $payload Data to send * @return array */ public function doRequest($payload) { $json_payload = json_encode($payload); $body = $this->transport == TRANSPORT_TCP ? $json_payload : $this->prepareHttpMessage($this->headers, $json_payload); if (!fwrite($this->ch, $body)) { throw new RuntimeException('Socket write error'); } $response = ''; while (($buffer = fgets($this->ch)) !== false) { $response .= $buffer; if ($this->transport == TRANSPORT_TCP) { break; } } if (!$response) { throw new RuntimeException('Socket read error'); } $data = $this->transport == TRANSPORT_TCP ? $response : $this->parseHttpMessage($response)["body"]; if ($this->debug) { error_log('==> Request: '.PHP_EOL.json_encode($payload, JSON_PRETTY_PRINT)); error_log('==> Response: '.PHP_EOL.$response); } $result = json_decode($data, true); return is_array($result) ? $result : array(); } } ?>