在区块链应用开发中,PHP 作为一种广泛使用的服务器端编程语言,常需要与以太坊网络交互,例如查询账户余额、发送交易、调用智能合约等,本文将详细介绍如何通过 PHP 执行以太坊命令,涵盖环境准备、核心实现方式、代码示例及最佳实践,帮助开发者高效集成以太坊功能到 PHP 项目中。

环境准备:搭建 PHP 与以太坊的交互基础

在开始编写 PHP 代码之前,需确保以下环境已配置完成:

PHP 环境

确保 PHP 版本 ≥ 7.4(推荐 8.0 ),部分扩展对高版本 PHP 支持更好,可通过命令 php -v 检查当前版本。

必要的 PHP 扩展

  • curl:用于发送 HTTP 请求(与以太坊节点交互的基础)。
    安装方式(Linux):sudo apt-get install php-curl(Ubuntu/Debian)或 sudo yum install php-curl(CentOS)。
    Windows 用户可在 php.ini 中取消注释 extension=php_curl.dll 并重启 Apache/Nginx。

  • json:PHP 内置扩展,用于解析和生成 JSON 数据(以太坊节点返回结果为 JSON 格式)。

以太坊节点或 RPC 服务

PHP 需要通过 JSON-RPC 协议与以太坊节点通信,可选择以下方式:

  • 本地节点:运行 Geth(以太坊客户端),开启 RPC 服务。
    启动命令示例:geth --http --http.addr "0.0.0.0" --http.port "8545" --http.api "eth,net,web3"
  • 远程节点服务:使用 Infura、Alchemy 等第三方 RPC 服务(无需本地部署,注册后获取 RPC URL)。

PHP 执行以太坊命令的核心实现方式

PHP 本身不直接支持以太坊协议,需通过 HTTP 请求调用以太坊节点的 JSON-RPC 接口,以下是核心实现步骤:

理解以太坊 JSON-RPC 接口

以太坊节点提供标准的 JSON-RPC API,每个接口对应一个特定功能(如 eth_getBalance 查询余额、eth_sendRawTransaction 发送交易),请求格式为:

{
  "jsonrpc": "2.0",
  "method": "方法名",
  "params": ["参数1", "参数2"],
  "id": 1
}

响应格式为:

{
  "jsonrpc": "2.0",
  "result": "返回结果",
  "id": 1
}

使用 PHP cURL 发送 JSON-RPC 请求

通过 PHP 的 curl 扩展可向以太坊节点发送 HTTP 请求,核心步骤包括:

  • 初始化 cURL 会话
  • 设置请求 URL(节点 RPC 地址)
  • 设置请求头(Content-Type: application/json
  • 设置 POST 请求体(JSON 格式的 RPC 请求)
  • 执行请求并解析响应

代码示例:封装以太坊 RPC 客户端

以下是一个简单的 PHP 类封装,用于发送以太坊 JSON-RPC 请求:

<?php
class EthereumRPC {
    private $rpcUrl;
    public function __construct($rpcUrl) {
        $this->rpcUrl = $rpcUrl;
    }
    /**
     * 发送 JSON-RPC 请求
     * @param string $method 方法名(如 eth_getBalance)
     * @param array $params 方法参数
     * @return array 响应结果
     */
    public function request($method, $params = []) {
        $data = json_encode([
            "jsonrpc" => "2.0",
            "method" => $method,
            "params" => $params,
            "id" => uniqid()
        ]);
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $this->rpcUrl);
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
        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 ($error = curl_error($ch)) {
            throw new Exception("cURL 错误: " . $error);
        }
        curl_close($ch);
        $result = json_decode($response, true);
        if (json_last_error() !== JSON_ERROR_NONE) {
            throw new Exception("JSON 解析错误: " . json_last_error_msg());
        }
        if (isset($result['error'])) {
            throw new Exception("以太坊节点错误: " . $result['error']['message']);
        }
        return $result['result'];
    }
}

常用以太坊命令的 PHP 实现

基于上述 EthereumRPC 类,可实现常见的以太坊操作。

查询账户余额

使用 eth_getBalance 方法,参数为地址和区块号(可选,"latest" 表示最新区块)。

$rpcUrl = "http://localhost:8545"; // 或 Infura/Alchemy 的 RPC URL
$eth = new EthereumRPC($rpcUrl);
$address = "0x742d35Cc6634C0532925a3b844Bc454e4438f44e"; // 示例地址
$balance = $eth->request("eth_getBalance", [$address, "latest"]);
// 余额默认为 Wei,转换为 Ether
$balanceInEther = bcdiv($balance, "1000000000000000000", 18);
echo "账户余额: " . $balanceInEther . " ETH\n";

获取最新区块号

使用 eth_blockNumber 方法,无需参数。

$latestBlockNumber = $eth->request("eth_blockNumber");
echo "最新区块号: " . hexdec($latestBlockNumber) . "\n"; // 转换为十进制

发送交易(转账)

发送交易需要构造交易数据并签名,步骤较复杂:

  1. 获取 nonce(账户的交易次数)
  2. 设置 gas 价格(gasPrice)和 gas 限制(gasLimit)
  3. 构造交易数据(接收地址、转账金额等)
  4. 使用私钥对交易数据签名
  5. 发送签名后的交易

以下是简化示例(需安装 web3.php 库处理签名,或使用 phpseclib 等库):

// 假设已安装 web3.php(composer require web3p/web3.php)
use Web3\Contract;
use Web3\Providers\HttpProvider;
use Web3\Web3;
$web3 = new Web3(new HttpProvider($rpcUrl));
$eth = $web3->eth;
$fromAddress = "0xYourAddress";
$privateKey = "0xYourPrivateKey"; // 实际开发中需安全存储,避免硬编码
$toAddress = "0xRecipientAddress";
$amount = "0.01"; // ETH 数量
// 获取 nonce
$eth->getTransactionCount($fromAddress, "latest", function ($err, $nonce) use ($eth, $fromAddress, $privateKey, $toAddress, $amount) {
    if ($err) {
        throw new Exception("获取 nonce 失败: " . $err->getMessage());
    }
    // 构造交易
    $transaction = [
        'from' => $fromAddress,
        'to' => $toAddress,
        'value' => $web3->toWei($amount, 'ether')->toString(),
        'gas' => "21000", // 转账的固定 gas 消耗
        'gasPrice' => "20000000000", // 20 Gwei
        'nonce' => $nonce,
    ];
    // 发送交易(需签名,此处简化为调用 sendRawTransaction,实际需用私钥签名)
    $eth->sendTransaction($transaction, function ($err, $txHash) {
        if ($err) {
            throw new Exception("发送交易失败: " . $err->getMessage());
        }
        echo "交易哈希: " . $txHash . "\n";
    });
});

调用智能合约

使用 eth_call 方法读取智能合约数据(无需交易),或发送交易调用合约方法(需修改状态),需先加载合约 ABI(应用程序二进制接口)。

// 假设有合约 ABI 和地址
$contractAddress = "0xContractAddress";
$abi = '[{"constant":true,"inputs":[],"name":"myFunction","outputs":[{"name":"","type":"uint256"}],"type":"function"}]';
$web3 = new Web3(new HttpProvider($rpcUrl));
$contract = new Contract($web3->provider, $