以下是 Hyperf 对接 PLC 的完整方案按协议分类 --- 协议选择 PLC 通信协议主要有三种选哪个取决于你的 PLC 品牌 ┌────────────┬─────────────────────────────────┬─────────────────────────┐ │ 协议 │ 适用 PLC │ 推荐库 │ ├────────────┼─────────────────────────────────┼─────────────────────────┤ │ Modbus TCP │ 通用三菱、台达、施耐德等 │ aldas/modbus-tcp-client │ ├────────────┼─────────────────────────────────┼─────────────────────────┤ │ S7 协议 │ 西门子 S7-200/300/400/1200/1500 │ snap7(PHP 扩展)│ ├────────────┼─────────────────────────────────┼─────────────────────────┤ │ OPC UA │ 现代 PLC 通用标准 │ 中间件方案 │ └────────────┴─────────────────────────────────┴─────────────────────────┘ --- 推荐方案aldas/modbus-tcp-client 这是 PHP 生态里最活跃、维护最好的 Modbus 库199 stars持续更新且底层用原生 socket可以配合 Swoole 协程 hook 实现非阻塞。 安装composerrequire aldas/modbus-tcp-client Hyperf 集成示例 在 Hyperf 中Swoole 的 HOOK_ALL 会自动 hook socket所以直接用即可?php namespace App\Service;use ModbusTcpClient\Network\BinaryStreamConnection;use ModbusTcpClient\Packet\ModbusFunction\ReadHoldingRegistersRequest;use ModbusTcpClient\Packet\ModbusFunction\ReadHoldingRegistersResponse;use ModbusTcpClient\Packet\ResponseFactory;use ModbusTcpClient\Utils\Types;class PlcService{private string$host;private int$port;publicfunction__construct(string$host192.168.1.100, int$port502){$this-host$host;$this-port$port;}// 读取保持寄存器Holding Registers功能码 0x03 publicfunctionreadHoldingRegisters(int$startAddress, int$quantity): array{$connectionBinaryStreamConnection::getBuilder()-setPort($this-port)-setHost($this-host)-setConnectTimeoutSec(1.5)-setReadTimeoutSec(1.5)-build();$requestnew ReadHoldingRegistersRequest($startAddress,$quantity, unitId:1);try{$connection-connect();$binaryData$connection-sendAndReceive($request);/** var ReadHoldingRegistersResponse$response*/$responseResponseFactory::parseResponseOrThrow($binaryData);$result[];foreach($responseas$address$word){$result[$address]$word-getInt16();}return$result;}finally{$connection-close();}}// 写单个寄存器功能码 0x06 publicfunctionwriteSingleRegister(int$address, int$value): void{$connectionBinaryStreamConnection::getBuilder()-setPort($this-port)-setHost($this-host)-build();$requestnew\ModbusTcpClient\Packet\ModbusFunction\WriteSingleRegisterRequest($address,$value, unitId:1);try{$connection-connect();$connection-sendAndReceive($request);}finally{$connection-close();}}}在 Controller 中使用?php namespace App\Controller;use App\Service\PlcService;use Hyperf\Di\Annotation\Inject;class PlcController extends AbstractController{#[Inject]private PlcService$plcService;publicfunctionreadData(): array{// 读取地址40001开始的10个寄存器return$this-plcService-readHoldingRegisters(0,10);}}高频轮询定时任务?php namespace App\Crontab;use App\Service\PlcService;use Hyperf\Crontab\Annotation\Crontab;#[Crontab(rule: * * * * *, name: PlcPoll, memo: PLC数据采集)]class PlcPollCrontab{publicfunctionexecute(): void{$servicemake(PlcService::class);$data$service-readHoldingRegisters(0,20);// 存入 Redis 或数据库}}--- 西门子 S7 方案 如果是西门子 PLC用 snap7 C 库 PHP FFI# 安装 snap7apt-getinstalllibsnap7-dev# 或者用 Docker 镜像直接带 snap7也可以走 S7 → OPC UA → PHP 的中间件架构用 Node.js/Python 做 OPC UA 桥接Hyperf 通过 HTTP/Redis 消费数据这样更稳定。 --- 关键注意事项1. 连接池 — PLC 通常只支持少量并发连接1-8个务必用连接池控制并发2. 超时设置 — 网络抖动时要有重试机制避免协程泄漏3. Unit ID — 不同 PLC 的 Unit ID 不同西门子通常是255其他默认14. 字节序 — 不同 PLC 大小端不同aldas 库提供了 getInt16()/ getUInt32()等方法处理