
简介这是一份基于web3.php库操作以太坊私链的PHP开发资源包适合有PHP基础、希望接入区块链的开发者。资源围绕私链交互场景覆盖连接RPC节点、账户私钥管理、发送交易、调用智能合约及监听链上事件等核心功能。压缩包共1935个文件约2.29MB以1332个PHP源码文件为主体另含200个phpt测试用例、88个XML配置、73个Markdown文档及JSON、LICENSE、YML等辅助文件完整呈现标准PHP项目的目录结构与工程化配置。包内附有示例代码、Composer依赖清单、PHPUnit测试配置以及持续集成脚本便于开发者直接参考或二次开发。已有4156人学习这份资源适合需要快速理解web3.php用法并在私链环境中开展以太坊应用开发的技术人员。1. 项目概述为什么用PHP操作以太坊做DApp后端的时候团队技术栈是纯PHP但以太坊生态的工具链几乎被Node.js和Python垄断。当时翻遍GitHub能直接用的PHP方案也就两类一类是自己拿Guzzle去怼JSON-RPC接口另一类就是用现成的web3.php库。我选择了后者。web3.php操作以太坊的核心思路说白了就是把以太坊节点的JSON-RPC接口封装成PHP方法让你在业务代码里像调用本地方法一样去查余额、发交易、读合约。这个库能解决什么问题简单说如果你的PHP项目需要接入以太坊主网、测试网或者自己搭的私有链web3.php可以帮你完成三类最核心的操作查询链上数据余额、区块、交易记录、发送交易ETH转账、合约调用、监听链上事件。适合谁参考后端PHP开发者、需要给现有PHP系统增加区块链能力的团队以及像我一样被Node.js生态劝退但想搞以太坊应用的人。接下来我把实际部署和踩坑过程完整写出来都是可以直接抄作业的级别。2. 环境准备依赖、安装与节点连接2.1 PHP运行环境与扩展要求先说环境底线。web3.php当前版本要求PHP 7.1以上我建议直接用PHP 8.0或8.1跑起来更省心。需要确保PHP安装了几个基础扩展curlHTTP请求、openssl签名和加密相关、mbstring字符串处理以及json扩展PHP内置一般都有。如果你想在本地跑测试网合约交互还需要gmp扩展它在处理大整数计算时比PHP自带整数类型靠谱得多。还有个容易踩坑的点PHP 8.0开始移除了很多老函数比如each()、create_function()而web3.php的老版本在PHP 8下会报错。我一开始用composer装的是最新版问题不大但如果你的项目里锁定了旧版本依赖升级PHP 8之后极大概率会崩。解决办法很简单强制更新web3.php到2.x版本或者干脆先用PHP 7.4跑通流程再说。2.2 用Composer安装web3.php安装过程不需要手动下载源码直接在你项目根目录执行composer require web3p/web3.php如果你需要额外的交易签名工具再装一个composer require web3p/ethereum-tx装完之后vendor/web3p目录下就有两个核心包了。web3.php是主库ethereum-tx专门负责构建和签名原始交易后面发交易那步你会用到它。安装过程中如果提示ext-gmp missing说明你的PHP环境没装gmp扩展CentOS上执行yum install php-gmpUbuntu上执行apt-get install php8.1-gmp版本号按你的PHP实际版本改装完重启PHP-FPM就行。2.3 连接节点Infura还是本地节点web3.php本身不维护区块链数据它只是个客户端必须连接一个以太坊节点。常用的有两种方式远程节点服务比如Infura注册后拿到一个HTTPS的RPC地址形如https://mainnet.infura.io/v3/你的项目ID。优点是省事不用同步数据适合快速开发和中小流量项目。本地节点用geth或erigon跑一个全节点或轻节点RPC地址一般是http://127.0.0.1:8545。优点是没有第三方依赖、数据自主可控但首次同步要下载大量区块数据硬盘和带宽损耗不小。连接代码非常短use Web3\Web3; use Web3\Providers\HttpProvider; $web3 new Web3(new HttpProvider(https://mainnet.infura.io/v3/YOUR_PROJECT_ID));如果是本地geth直接传字符串也行$web3 new Web3(http://127.0.0.1:8545);本地geth启动时有个常见的坑老版本用--rpc参数新版本改成了--http而且默认不开放personal接口转账操作会受限。我通常这么启动geth --http --http.addr 0.0.0.0 --http.port 8545 --http.api eth,web3,net --http.corsdomain *--http.corsdomain *是为了允许跨域请求如果你前端页面也在调这个节点这行必须加。生产环境建议把*换成具体域名。3. 核心实操从余额查询到合约交互3.1 查询账户余额与单位换算连接成功后最简单也最高频的操作就是查余额。web3.php的eth-getBalance方法需要传两个参数地址和回调函数。回调函数接收两个参数第一个是错误对象第二个是余额结果。$web3-eth-getBalance(0x你的以太坊地址, function ($err, $balance) { if ($err ! null) { echo Error: . $err-getMessage(); return; } echo $balance-toString(); });这里有一个非常关键的细节$balance不是PHP原生的整数或字符串而是一个BigNumber对象。以太坊链上金额最小单位是wei1 ETH等于10的18次方 weiPHP的整数类型根本存不下这么大的数字所以web3.php返回的是大数对象。你需要调用-toString()拿到原始wei数值再自己换算use Web3\Utils; $ethAmount Utils::fromWei($balance-toString(), ether); echo $ethAmount;Utils::fromWei是web3.php自带的单位转换方法支持wei、gwei、ether等常见单位强烈建议统一用它别自己手写除以10的18次方踩过高精度丢失的坑的人都懂。3.2 构建ETH转账交易并发送转账是另一个高频操作但它比查询复杂得多因为需要私钥签名。web3.php本身不管私钥签名你需要配合ethereum-tx包来完成。第一步获取当前账户的nonce交易序号。每个账户的nonce从0开始每发一笔交易加1而且严格递增。如果nonce重复或跳号交易会被节点拒绝。$web3-eth-getTransactionCount(0x发款方地址, function ($err, $nonce) { // $nonce 也是 BigNumber 对象 });第二步组装交易数据并签名use Web3p\EthereumTx\Transaction; $transaction new Transaction([ nonce 0x . $nonce-toHex(), to 0x收款方地址, value 0x . Utils::toWei(0.01, ether)-toHex(), gas 0x5208, // 21000普通转账固定值 gasPrice 0x . $gasPrice-toHex(), chainId 1 // 主网填1测试网填不同的值 ]); $signedTransaction 0x . $transaction-sign(你的私钥);第三步把签名后的原始交易广播到网络$web3-eth-sendRawTransaction($signedTransaction, function ($err, $txHash) { if ($err ! null) { echo Error: . $err-getMessage(); return; } echo Transaction hash: . $txHash; });这里有几个实际经验分享。gasPrice不要写死建议通过$web3-eth-gasPrice动态获取否则网络拥堵时交易会卡很久。gas值在普通转账里固定21000但如果to地址是合约地址或者你要带data数据gas就必须重新估算最稳妥的方式是用$web3-eth-estimateGas跑一遍。签名用的私钥是64位十六进制字符串前面带不带0x都行但一定不能暴露到前端。3.3 调用智能合约方法调用合约分两种情况读操作call不消耗gas写操作send消耗gas。web3.php专门提供了Contract类简化过程。先准备合约的ABI通常从合约编译结果里拿到的JSON数组然后实例化use Web3\Contract; $abi json_decode(file_get_contents(abi.json), true); $contract new Contract($web3-provider, $abi); $contractAddress 0x合约地址;读操作比如查ERC20代币余额$contract-at($contractAddress)-call(balanceOf, 0x查询地址, function ($err, $result) { if ($err ! null) { echo Error: . $err-getMessage(); return; } // $result 是一个数组按合约方法的返回值顺序排列 echo $result[0]-toString(); });写操作比如调用transfer转代币$contract-at($contractAddress)-send( transfer, [0x收款地址, Utils::toWei(100, ether)-toString()], [from 0x发款地址], function ($err, $result) { if ($err ! null) { echo Error: . $err-getMessage(); return; } echo Tx hash: . $result; } );注意send方法需要在from参数里指定发款账户而且这个账户必须在节点里处于解锁状态。用Infura这种远程节点时节点不托管你的账户所以send方式会失败。解决办法有两个要么用本地geth并手动personal_unlockAccount解锁要么老老实实用ethereum-tx做了签名再sendRawTransaction。我个人强烈推荐后者既能用远程节点又不用把私钥交给第三方节点。4. 常见问题与排查技巧4.1 连接超时与网络层异常用远程节点时最常遇到的就是连接超时或HTTP 429限流。Infura免费版有请求频率限制并发稍微高一点就开始报错。我排查这类问题的第一步是确认节点联通性curl -X POST https://mainnet.infura.io/v3/YOUR_PROJECT_ID \ -H Content-Type: application/json \ -d {jsonrpc:2.0,method:eth_blockNumber,params:[],id:1}如果能正常返回区块高度说明网络没毛病问题大概率出在应用层。这时检查web3.php的HttpProvider有没有设置超时时间。默认超时对某些慢接口不够用尤其区块高、节点负载大的时候。建议在初始化时指定一个合理的超时上限同时做好重试机制。我自己会在Provider外面包一层带重试的封装遇到超时最多重试3次每次间隔递增亲测能挡掉不少偶发问题。如果是本地节点连不上先看geth日志最常见的坑是RPC端口没监听或CORS配置不对。执行netstat -tlnp | grep 8545看端口是否在监听然后确认启动参数里--http.api有没有包含eth和web3方法组。4.2 交易失败排查Gas不足与Nonce冲突交易广播出去不代表一定成功。很多刚上手的人拿到txHash就觉得完事了结果第二天发现交易其实reverted了。排查交易是否成功最简单的方式是等几个区块后用eth_getTransactionReceipt查回执$web3-eth-getTransactionReceipt($txHash, function ($err, $receipt) { if ($err ! null) { echo Error: . $err-getMessage(); return; } if ($receipt null) { echo 交易还在pending或者不存在; return; } echo status: . $receipt-status; });回执里status字段为0x1表示成功0x0表示失败。失败原因最常见的两个一是gas给少了二是合约本身拒绝了调用。合约拒绝的情况节点不会直接告诉你原因你得把合约方法模拟执行一下或者看revert的reason。gas不足好办用estimateGas动态估算后加上20%缓冲再提交。Nonce冲突是另一个隐蔽的坑。当你同一账户连续快速发多笔交易时如果第一笔还没打包你就发了第二笔并复用了nonce第二笔会直接覆盖或者被拒。正确做法是维护一个本地nonce计数器每次发完交易加1不要每次都去节点重新查询因为pending状态的交易不计入getTransactionCount的默认返回结果你会拿到一个偏小的nonce导致重复。4.3 数据类型与编码的坑web3.php里最让新手抓狂的就是各种返回值类型。接口返回的数量余额、nonce、gasPrice都是BigNumber对象不是字符串也不是int。你如果直接把$balance拿去JSON编码大概率会得到一个对象而不是数字前端对接时怎么都对不上。统一用-toString()转成字符串最稳。还有十六进制编码问题。很多接口参数要求传0x开头的十六进制字符串数值转十六进制时不能直接dechex因为dechex处理不了大整数。web3.php提供Utils::toHex()方法传入BigNumber对象或十进制字符串都能正确转十六进制。我自己就因为在gasPrice上偷懒直接dechex吃过亏大数转出来精度全丢了交易在节点那边直接报invalid argument。5. 实操心得与避坑建议5.1 生产环境的私钥管理与安全之前开发测试阶段我把私钥直接写在PHP文件里的一个常量里方便是方便但绝对不能用在生产环境。任何拿到源码的人都能直接转走你的资产。我后来改成了环境变量加密钥管理服务的方式私钥通过环境变量注入PHP代码里用getenv()读取服务器层面限制文件权限和访问范围。如果公司有Vault或者KMS优先用这些服务管理私钥再进一步可以对私钥做加密存储运行时解密放入内存用完及时释放。另外警告一句任何情况下都不要把私钥传到前端也不要打日志。PHP的错误日志一旦把签名后的交易或私钥打出来基本等于资产送人。我在日志模块里统一做了脱敏处理关键字匹配到private key、sign、rawTx这些字段就直接截断。5.2 并发、队列与节点监控以太坊这种异步系统后端最忌讳的就是同步死等。我的做法是把交易发送和交易确认拆成两个环节接口收到请求后先按nonce顺序发送交易把txHash写入队列然后由后台消费者定时去查回执确认成功后更新业务状态。这样做的好处是用户不用一直等区块打包接口响应快失败重试也方便。队列这块我用的PHP原生的Redis队列消费脚本用CLI跑配合supervisor守护。查询回执的频率控制在一个区块时间左右主网约12秒。还要给每个交易加一个最大确认等待时间超过比如2分钟还在pending就告警可能是gasPrice给低了或者网络拥堵需要重新加速。节点侧的监控也很重要。我会定期执行eth_syncing检查节点是否同步完成如果节点还在同步状态查询接口会返回不完整的数据容易误导业务逻辑。另外记录每次RPC调用的耗时发现某个方法耗时暴涨就要排查节点负载和网络链路了。最后再分享一个小技巧如果项目只是查链上数据不做交易可以给web3.php加一层简单的缓存按区块高度做失效判断把高频查询的请求结果缓存几十秒能显著降低节点压力和RPC费用。我在实际项目中就是把eth_blockNumber作为缓存key的前缀区块更新了才刷新数据运行了半年没出过问题效果很明显。本文还有配套的精品资源点击获取