架構設計
- 傳輸協議:HTTP POST
- 消息格式:JSON-RPC 2.0 規範(
{jsonrpc:"2.0", method, params, id})
- 組件:
jsonRPCServer(服務端分發)、jsonRPCClient(客戶端代理)
服務端核心:jsonRPCServer
class jsonRPCServer {
public static function handle($object) {
// 1. 校驗請求
if ($_SERVER['REQUEST_METHOD'] !== 'POST' ||
empty($_SERVER['CONTENT_TYPE']) ||
$_SERVER['CONTENT_TYPE'] !== 'application/json') {
return false;
}
// 2. 解析輸入
$request = json_decode(file_get_contents('php://input'), true);
if (!$request || !isset($request['method'], $request['params'], $request['id'])) {
return false;
}
// 3. 執行方法
try {
if ($result = @call_user_func_array([$object, $request['method']], $request['params'])) {
$response = ['id'=>$request['id'], 'result'=>$result, 'error'=>null];
} else {
$response = ['id'=>$request['id'], 'result'=>null,
'error'=>'unknown method or incorrect parameters'];
}
} catch (Exception $e) {
$response = ['id'=>$request['id'], 'result'=>null, 'error'=>$e->getMessage()];
}
// 4. 輸出響應
if (!empty($request['id'])) { // 通知類無需響應
header('Content-Type: application/json');
echo json_encode($response);
}
return true;
}
}
客戶端核心:jsonRPCClient
class jsonRPCClient {
private $url, $id = 1, $debug = false, $notification = false;
public function __construct($url, $debug = false) {
$this->url = $url;
$this->debug = $debug;
}
public function __call($method, $params) {
if (!is_scalar($method)) throw new Exception('Method name must be scalar');
$params = array_values((array)$params);
$request = ['method'=>$method, 'params'=>$params, 'id'=>$this->notification ? null : $this->id++];
$requestJson = json_encode($request);
$opts = ['http'=>['method'=>'POST', 'header'=>'Content-Type: application/json', 'content'=>$requestJson]];
$context = stream_context_create($opts);
$result = file_get_contents($this->url, false, $context);
if ($result === false) throw new Exception('Unable to connect to '.$this->url);
$response = json_decode($result, true);
if (!$this->notification) {
if ($response['id'] !== $request['id']) throw new Exception('Response ID mismatch');
if ($response['error'] !== null) throw new Exception('RPC Error: '.$response['error']);
return $response['result'];
}
return true;
}
}
完整調用示例
1. 服務端入口 server.php
require 'jsonRPCServer.php';
require 'MemberService.php'; // 業務類
$service = new MemberService();
jsonRPCServer::handle($service) or print 'no request';
2. 業務類 MemberService.php
class MemberService {
public function getName() { return 'Hello JSON-RPC'; }
public function getUser($id) { return ['id'=>$id, 'name'=>'User'.$id]; }
}
3. 客戶端調用 client.php
require 'jsonRPCClient.php';
$client = new jsonRPCClient('https://localhost/rpc/server.php', true); // 開啟調試
try {
echo $client->getName()."\n"; // Hello JSON-RPC
print_r($client->getUser(123)); // Array([id]=>123 [name]=>User123)
} catch (Exception $e) {
echo 'Error: '.$e->getMessage();
}
進階擴展建議
- 批量請求:支援
[{id:1,method...}, {id:2,method...}] 數組請求,減少 HTTP 開銷。
- 異步/通知:
$client->setRPCNotification(true) 發送無 ID 請求,不等待響應。
- 認證授權:在 HTTP Header 或 params 中傳遞 Token,Server 端驗證。
- Swoole/RoadRunner:替換阻塞式
file_get_contents 為異步 HTTP 客戶端,支援高併發。