在当今区块链技术飞速发展的时代,以太坊作为全球领先的智能合约平台,吸引了无数开发者的目光,对于许多基于 Web 技术栈的开发者而言,使用 PHP 与以太坊进行交互是一个常见的需求,而实现这种交互的核心方式之一,就是通过以太坊的 JSON-RPC (JSON-RPC) 接口,本文将详细介绍如何使用 PHP 对接以太坊 RPC,实现从环境搭建到具体功能调用的一系列操作。
什么是以太坊 RPC?

以太坊 JSON-RPC 是一个标准的通信协议,它允许客户端(如我们的 PHP 应用程序)与以太坊节点(如 Geth, Parity 或 Infura 这样的远程节点)进行通信,通过发送 JSON 格式的请求,客户端可以调用节点提供的各种方法,例如查询账户余额、发送交易、部署智能合约、读取智能合约状态等,你可以把 RPC 看作是 PHP 应用与以太坊区块链之间的“翻译官”和“桥梁”。
准备工作
在开始编码之前,我们需要准备以下几样东西:
cURL 扩展(PHP 默认自带)或者一些轻量级的 HTTP 客户端库,如 Guzzle,本文主要使用 cURL 进行演示,因为它更通用,无需额外安装依赖。PHP 对接以太坊 RPC 核心步骤
对接以太坊 RPC 的核心步骤无非就是:构造 JSON-RPC 请求 -> 通过 HTTP 发送请求 -> 接收并解析 JSON 响应。

构造 JSON-RPC 请求
一个标准的 JSON-RPC 请求包含以下字段:
jsonrpc: 版本,通常为 "2.0"。method: 要调用的以太坊 RPC 方法名,如 eth_blockNumber, eth_getBalance。params: 方法调用所需的参数数组,顺序和类型需与方法定义一致,如果没有参数则为空数组。id: 请求 ID,用于标识请求,响应中会包含相同的 ID。获取最新区块号的请求 JSON 如下:
{
"jsonrpc": "2.0",
"method": "eth_blockNumber",
"params": [],
"id": 1
} 发送 HTTP 请求并接收响应
我们可以使用 PHP 的 cURL 扩展来发送 POST 请求到以太坊节点的 RPC URL。

PHP 代码示例
下面我们通过几个常用的场景来展示具体的 PHP 代码实现。
连接以太坊节点并获取最新区块号
<?php
/**
* 以太坊 RPC 客户端类示例
*/
class EthereumRpcClient
{
private $rpcUrl;
private $id = 0;
public function __construct($rpcUrl)
{
$this->rpcUrl = $rpcUrl;
}
/**
* 发送 JSON-RPC 请求
* @param string $method 方法名
* @param array $params 参数数组
* @return array 响应结果
* @throws Exception
*/
public function request($method, $params = [])
{
$this->id ;
$payload = json_encode([
'jsonrpc' => '2.0',
'method' => $method,
'params' => $params,
'id' => $this->id
]);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $this->rpcUrl);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 10); // 设置超时时间
$response = curl_exec($ch);
if (curl_errno($ch)) {
throw new Exception('cURL Error: ' . curl_error($ch));
}
curl_close($ch);
$result = json_decode($response, true);
if (json_last_error() !== JSON_ERROR_NONE) {
throw new Exception('JSON Decode Error: ' . json_last_error_msg());
}
// 检查 RPC 响应中的 error 字段
if (isset($result['error']) && $result['error'] !== null) {
throw new Exception('RPC Error: ' . $result['error']['message'] . ' (Code: ' . $result['error']['code'] . ')');
}
return $result['result'] ?? null;
}
}
// 使用示例
try {
// 替换成你的 Infura 或其他节点服务的 RPC URL
// 测试网示例 (Goerli)
$rpcUrl = 'https://goerli.infura.io/v3/YOUR_INFURA_PROJECT_ID';
// 主网示例 (需要替换为你的实际 RPC URL)
// $rpcUrl = 'https://mainnet.infura.io/v3/YOUR_INFURA_PROJECT_ID';
$client = new EthereumRpcClient($rpcUrl);
// 1. 获取最新区块号
$latestBlockNumber = $client->request('eth_blockNumber');
echo "最新区块号 (Hex): " . $latestBlockNumber . "\n";
// 将十六进制区块号转为十进制
$latestBlockNumberDec = hexdec($latestBlockNumber);
echo "最新区块号 (Dec): " . $latestBlockNumberDec . "\n";
} catch (Exception $e) {
echo "Error: " . $e->getMessage() . "\n";
}
?>
查询指定地址的 ETH 余额
// 接续上面的 EthereumRpcClient 类
// 使用示例
try {
$client = new EthereumRpcClient($rpcUrl); // 确保 $rpcUrl 已定义
// 要查询的地址
$address = '0x742d35Cc6634C0532925a3b844Bc454e4438f44e'; // 示例地址,可以替换成你想查询的地址
// eth_getBalance 的第二个参数是区块号,可以是 'latest', 'pending', 'earliest' 或十六进制区块号
$balance = $client->request('eth_getBalance', [$address, 'latest']);
echo "地址 {$address} 的余额 (Hex Wei): " . $balance . "\n";
// 将 Wei 转为 ETH (1 ETH = 10^18 Wei)
$balanceEth = bcdiv(hexdec($balance), 1000000000000000000, 18);
echo "地址 {$address} 的余额 (ETH): " . $balanceEth . "\n";
} catch (Exception $e) {
echo "Error: " . $e->getMessage() . "\n";
}
发送交易(转账 ETH)
发送交易比查询操作要复杂,因为它需要私钥签名。注意:在实际应用中,切勿在代码中硬编码私钥! 应该使用硬件钱包、环境变量或专门的密钥管理服务。
这里我们为了演示,会使用一个私钥,但请务必理解其风险。
// 接续上面的 EthereumRpcClient 类
// 辅助函数:将私钥转换为地址(简化版,实际应使用更安全的库如 web3.php)
function privateKeyToAddress($privateKey) {
// 这里仅作概念演示,实际需要椭圆曲线运算等复杂逻辑
// 通常我们会使用如 `web3.php` 这样的库来处理
// 假设这个函数能返回地址
// 注意:这是一个简化的示意,真实实现非常复杂且不安全直接写
return '0x' . substr(keccak256(hex2bin(str_pad($privateKey, 64, '0', STR_PAD_LEFT))), -40);
}
// 辅助函数:签名交易