PHP实现OPC UA客户端:开源库推荐与代码示例

江岸
江岸
发布时间:2026-09-22| 5人浏览过
原创

PHP现在可以直接作为OPC UA客户端连接PLC、SCADA、传感器或其他OPC UA服务器,不一定要先经过Node.js、Python或Java中转。对于新的PHP项目,可以优先考虑纯PHP的 php-opcua/opcua-client,通过Composer安装后直接使用 opc.tcp:// 连接服务器,完成节点浏览、数据读取、写入、订阅和安全认证。对于对底层协议栈成熟度要求更高的项目,也可以让PHP通过独立服务调用open62541。

下面从库的选择开始,实际完成一次“连接服务器 → 找到节点 → 读取数据 → 写入数据 → 订阅变化 → 配置安全连接”的完整流程。

PHP连接OPC UA到底需要哪些东西

如果以前接触过Modbus,很容易把OPC UA理解成“连接IP和端口,然后读一个地址”。实际上OPC UA多了一层完整的信息模型。

PHP应用连接的不是一个简单寄存器,而是OPC UA服务器的地址空间。服务器内部可能存在设备、对象、变量、方法等不同节点,每个节点通过NodeId标识。例如:

ns=2;s=Temperature
ns=2;s=PLC/Speed
ns=3;i=1001
i=2259

其中 ns 表示命名空间索引,s 表示字符串型标识符,i 表示数字型标识符。

一次典型读取过程可以理解为:PHP先通过TCP连接OPC UA Endpoint,建立SecureChannel和Session,然后向服务器发送Read请求,请求指定NodeId当前的Value。

PHP实现OPC UA客户端:开源库推荐与代码示例
PHP访问OPC UA时,业务代码不需要自己处理二进制协议,客户端库负责SecureChannel、Session和服务请求。

PHP有哪些OPC UA开源库可以选择

PHP的OPC UA生态不像C、C++、.NET和Java那样庞大,但已经出现可以直接处理OPC UA Binary协议的纯PHP实现。选择时不要只看“能否连接”,还要看安全策略、证书、订阅、断线恢复以及目标PHP版本。

方案 主要特点 适合场景 需要注意
php-opcua/opcua-client 纯PHP,直接支持opc.tcp,包含Browse、Read、Write、订阅、历史读取和安全连接 希望PHP直接连接PLC或OPC UA服务器 要求PHP 8.2+,生产部署前应针对实际设备验证兼容性
techdock/opcua 另一套现代PHP OPC UA客户端实现 PHP 8.4+项目,希望评估不同纯PHP实现 项目仍处于较早版本阶段,应充分测试
open62541 C语言开源OPC UA协议栈,可实现客户端和服务器 工业网关、边缘服务、长期运行的独立通信进程 不是PHP库,通常需要通过独立服务、FFI或其他接口与PHP集成

php-opcua/opcua-client 当前项目文档说明其使用PHP 8.2+,运行时核心依赖为OpenSSL扩展,并直接实现OPC UA Binary协议、会话、安全通道、读写、浏览和订阅等功能。:contentReference[oaicite:0]{index=0}

techdock/opcua 在Packagist中要求PHP 8.4及Sockets扩展,也提供Binary Protocol、安全通道、Session、Browse、Read/Write和订阅等能力,可作为另一种PHP原生方案评估。:contentReference[oaicite:1]{index=1}

open62541则是C语言实现的开源OPC UA协议栈,采用MPL 2.0许可证,官方文档提供客户端和服务器能力。它并不能直接替代Composer里的PHP包,但如果系统需要长期保持大量设备连接,可以把open62541部署为独立采集服务,再把整理后的数据交给PHP业务系统。:contentReference[oaicite:2]{index=2}

如果目标只是“让PHP读取PLC里的几个变量”,直接采用纯PHP客户端通常最简单。如果系统要连接几十或几百台设备,并持续运行订阅、断线重连和数据采集,更合理的架构往往是让常驻Worker或专门的OPC UA采集服务管理连接,而不是让每次HTTP请求重新连接PLC。

安装php-opcua/opcua-client

下面以 php-opcua/opcua-client 为主进行演示。假设项目目录为:

/var/www/opcua-demo

进入项目目录,通过Composer安装:

cd /var/www/opcua-demo
composer require php-opcua/opcua-client

确认PHP已经启用OpenSSL:

php -m | grep -i openssl

示例输出:

openssl

如果没有输出,需要根据当前PHP安装方式启用OpenSSL扩展。这里不要直接照搬某个Linux发行版的包名,因为PHP可能来自系统软件仓库、Remi、宝塔、Docker或自行编译。

从安装到读取变量的完整操作顺序

实际接入PLC时,推荐不要一开始就写复杂业务逻辑,而是按固定顺序验证。先确认网络和Endpoint,再浏览节点,找到正确NodeId,最后进行Read和Write。

PHP实现OPC UA客户端:开源库推荐与代码示例
推荐的接入顺序:先验证Endpoint和NodeId,再进入业务读写,能减少大量“连接成功但读不到变量”的排查时间。

最简单的PHP OPC UA读取示例

假设OPC UA服务器地址为:

opc.tcp://192.168.1.100:4840

需要读取的变量NodeId为:

ns=2;s=Temperature

创建文件:

/var/www/opcua-demo/read.php

代码如下:

<?php

declare(strict_types=1);

require __DIR__ . '/vendor/autoload.php';

use PhpOpcua\Client\ClientBuilder;

$endpoint = 'opc.tcp://192.168.1.100:4840';
$nodeId = 'ns=2;s=Temperature';

$client = null;

try {
    $client = ClientBuilder::create()
        ->connect($endpoint);

    $dataValue = $client->read($nodeId);

    echo "NodeId: {$nodeId}\n";
    echo "Value: ";
    var_dump($dataValue->getValue());
} catch (Throwable $e) {
    fwrite(STDERR, 'OPC UA读取失败:' . $e->getMessage() . PHP_EOL);
    exit(1);
} finally {
    if ($client !== null) {
        $client->disconnect();
    }
}

在项目目录执行:

php read.php

如果服务器允许匿名连接,并且NodeId确实存在,就可以得到变量当前值。

例如温度变量类型为Double时,示例输出可能类似:

NodeId: ns=2;s=Temperature
Value: float(23.6)

这里的 23.6 只是示例结果,实际值由OPC UA服务器返回。

不知道NodeId时先浏览地址空间

实际接入设备时,最常见的问题往往不是PHP代码,而是不知道设备厂商到底把变量放在哪个NodeId下面。

OPC UA标准地址空间中的Objects目录可以使用 i=85 表示。可以先从这里浏览。

<?php

declare(strict_types=1);

require __DIR__ . '/vendor/autoload.php';

use PhpOpcua\Client\ClientBuilder;

$client = ClientBuilder::create()
    ->connect('opc.tcp://192.168.1.100:4840');

try {
    $references = $client->browse('i=85');

    foreach ($references as $reference) {
        echo $reference->displayName
            . ' => '
            . $reference->nodeId
            . PHP_EOL;
    }
} finally {
    $client->disconnect();
}

示例输出可能类似:

Server => ns=0;i=2253
PLC1 => ns=2;i=1000
ProductionLine => ns=2;s=ProductionLine

设备的实际节点结构完全由服务器决定,因此不要把网上文章里的 ns=2;s=Temperature 直接当成自己的PLC地址。示例NodeId只是说明格式。

NodeId里的ns=2到底是什么意思

这里很容易踩坑。ns=2 不是固定代表“PLC变量”,它只是当前服务器NamespaceArray中的第2号命名空间索引。

例如某台设备可能使用:

ns=2;s=Motor.Speed

换一台服务器,同一个厂商模型也可能因为命名空间加载顺序不同而变成:

ns=3;s=Motor.Speed

所以在正式系统中,应尽量了解设备的Namespace URI与NodeId映射,不要盲目假定Namespace Index永远不变。

PHP写入OPC UA变量

读取正常以后,再测试写操作。假设服务器暴露一个设定值:

ns=2;s=Setpoint

并且该变量的数据类型为Double,可以显式指定类型:

<?php

declare(strict_types=1);

require __DIR__ . '/vendor/autoload.php';

use PhpOpcua\Client\ClientBuilder;
use PhpOpcua\Client\Types\BuiltinType;

$client = ClientBuilder::create()
    ->connect('opc.tcp://192.168.1.100:4840');

try {
    $client->write(
        'ns=2;s=Setpoint',
        42.5,
        BuiltinType::Double
    );

    $result = $client->read('ns=2;s=Setpoint');

    echo '当前设定值:';
    var_dump($result->getValue());
} finally {
    $client->disconnect();
}

写入之后再读一次,是开发阶段比较直接的验证方法。但要注意,“读回42.5”并不一定意味着实际设备已经完成动作。某些PLC变量只是命令输入,真正执行结果还需要读取对应状态节点确认。

不要在不了解设备变量含义的情况下对真实PLC执行Write。与普通数据库字段不同,OPC UA节点可能直接关联电机速度、阀门开度、生产线启停或其他物理设备。

为什么有时能连接,却写入失败

Read成功而Write失败并不奇怪,常见原因包括:

  • 节点本身只读;
  • 匿名用户只有读取权限;
  • 用户账号没有写入权限;
  • 写入的数据类型与服务器变量类型不一致;
  • PLC当前工作状态禁止修改参数;
  • 服务器要求使用加密连接后才开放写操作。

因此不要看到“连接正常”就判断OPC UA配置完全正确。连接、Session认证、节点权限和变量数据类型属于不同层次的问题。

生产环境应配置安全连接

前面的代码直接调用 connect(),更适合受控测试网络。正式环境如果服务器支持安全Endpoint,应根据设备配置使用安全策略和消息安全模式。

php-opcua/opcua-client 文档提供了 Basic256Sha256 与 SignAndEncrypt 的连接方式,也支持客户端证书以及用户名密码认证。:contentReference[oaicite:3]{index=3}

<?php

declare(strict_types=1);

require __DIR__ . '/vendor/autoload.php';

use PhpOpcua\Client\ClientBuilder;
use PhpOpcua\Client\Security\SecurityMode;
use PhpOpcua\Client\Security\SecurityPolicy;

$client = ClientBuilder::create()
    ->setSecurityPolicy(SecurityPolicy::Basic256Sha256)
    ->setSecurityMode(SecurityMode::SignAndEncrypt)
    ->setClientCertificate(
        '/etc/opcua/client.pem',
        '/etc/opcua/client.key',
        '/etc/opcua/ca.pem'
    )
    ->setUserCredentials(
        'operator',
        getenv('OPCUA_PASSWORD')
    )
    ->connect('opc.tcp://192.168.1.100:4840');

try {
    $value = $client->read('ns=2;s=Temperature');

    var_dump($value->getValue());
} finally {
    $client->disconnect();
}

密码不要直接写进PHP源码。可以从环境变量、Secret管理系统或服务器安全配置中读取。

PHP实现OPC UA客户端:开源库推荐与代码示例
OPC UA安全并不只是用户名密码:安全通道、证书信任和Session用户权限分别解决不同问题。

证书为什么经常导致第一次连接失败

工业OPC UA服务器通常维护自己的信任列表。当PHP客户端第一次拿着新的客户端证书连接时,服务器可能把它放入Rejected或Untrusted目录,需要管理员手动将证书加入Trusted列表。

另一方面,PHP客户端也需要决定是否信任服务器证书。这意味着证书认证是双向信任问题,而不仅仅是在PHP目录中放两个PEM文件。

调试时可以先确认:

  1. 当前Endpoint支持哪种SecurityPolicy;
  2. 服务器是否信任PHP客户端证书;
  3. 客户端是否信任服务器证书;
  4. 证书时间是否有效;
  5. 用户名所对应的UserTokenPolicy是否允许当前安全方式。

使用订阅代替不停轮询

如果只需要每30秒读取一次温度,定时执行 read() 没问题。但如果需要实时监控设备状态,不建议写成无限循环每100毫秒执行一次Read。

OPC UA本身提供Subscription和MonitoredItem。客户端建立订阅后,由服务器按照采样和发布机制整理变化数据,客户端通过Publish请求持续获取通知。该库文档也提供了 createSubscription()、createMonitoredItems() 和 publish()。:contentReference[oaicite:4]{index=4}

<?php

declare(strict_types=1);

require __DIR__ . '/vendor/autoload.php';

use PhpOpcua\Client\ClientBuilder;

$client = ClientBuilder::create()
    ->connect('opc.tcp://192.168.1.100:4840');

try {
    $subscription = $client->createSubscription(
        publishingInterval: 500.0
    );

    $client->createMonitoredItems(
        $subscription->subscriptionId,
        [
            [
                'nodeId' => 'ns=2;s=Temperature'
            ]
        ]
    );

    while (true) {
        $response = $client->publish();

        foreach ($response->notifications as $notification) {
            $value = $notification['dataValue']->getValue();

            echo date('Y-m-d H:i:s')
                . ' Temperature='
                . $value
                . PHP_EOL;
        }
    }
} finally {
    $client->disconnect();
}

这个程序属于长期运行进程,因此更适合这样执行:

php monitor.php

然后通过systemd、Supervisor、Docker或其他进程管理器保持运行。

不建议把这样的无限循环直接放到普通Nginx + PHP-FPM接口中,因为HTTP请求有超时、Worker占用以及进程生命周期等问题。

Web项目里应该每次请求都连接PLC吗

少量后台操作可以这样做:

浏览器请求
    ↓
PHP
    ↓
连接 OPC UA
    ↓
读取一次
    ↓
断开连接
    ↓
返回结果

但实时监控系统更适合:

PLC / OPC UA Server
        ↓
长期运行 OPC UA Worker
        ↓
Redis / MySQL / 时序数据库
        ↓
PHP Web API
        ↓
浏览器

这样Web请求不需要直接等待PLC建立Session,设备临时离线也不会把网站请求全部拖住。

php-opcua 项目还提供单独的Session Manager,用于在PHP请求之外保持OPC UA Session,这本身也说明持续连接与传统短生命周期Web请求属于不同类型的问题。:contentReference[oaicite:5]{index=5}

一次读取多个节点,不要逐个建立连接

如果页面需要同时显示温度、压力、转速等多个数据,最差的写法是每个变量重新建立一次OPC UA连接。

应该至少复用同一个Session,并使用批量读取功能。该客户端提供 readMulti():

<?php

declare(strict_types=1);

require __DIR__ . '/vendor/autoload.php';

use PhpOpcua\Client\ClientBuilder;

$client = ClientBuilder::create()
    ->connect('opc.tcp://192.168.1.100:4840');

try {
    $results = $client->readMulti()
        ->node('ns=2;s=Temperature')->value()
        ->node('ns=2;s=Pressure')->value()
        ->node('ns=2;s=PLC/Speed')->value()
        ->execute();

    foreach ($results as $result) {
        var_dump($result->getValue());
    }
} finally {
    $client->disconnect();
}

批量读取的重点不是少写几行PHP,而是减少大量独立请求带来的协议开销。具体一次请求允许读取多少节点,还受到目标OPC UA服务器限制。

连接失败时按什么顺序排查

OPC UA接入出现问题时,不建议一开始就怀疑PHP库。可以按照网络层到应用层逐级排查。

1. 先确认IP和端口

例如服务器Endpoint是:

opc.tcp://192.168.1.100:4840

先确认PHP所在服务器能够访问目标端口:

nc -vz 192.168.1.100 4840

如果TCP端口都不可达,就没有必要继续检查NodeId。

2. 确认Endpoint是否正确

有的OPC UA服务器不只是:

opc.tcp://192.168.1.100:4840

而可能带有服务器路径,例如:

opc.tcp://192.168.1.100:4840/UA/MyServer

应该以实际设备或OPC UA服务器公布的Endpoint为准。

3. 再检查SecurityPolicy

如果服务器关闭None模式,只允许加密Endpoint,那么匿名、无加密的示例自然无法连接。此时应该查询服务器允许的Endpoint和安全策略,而不是反复修改NodeId。

4. 连接成功后再检查NodeId

如果Session已经建立,但Read返回NodeId不存在之类的问题,才应该把注意力转向Namespace和NodeId。

5. 最后检查权限和数据类型

能够Read但不能Write,一般更接近权限、AccessLevel或数据类型问题。

什么时候应该选择open62541,而不是纯PHP

纯PHP客户端最大的优势是部署简单,Composer安装后就可以直接融入Laravel、Symfony或普通PHP项目。不过它并不是所有工业系统的唯一选择。

如果项目具有下面这些特征,可以考虑把OPC UA通信独立出来:

  • 需要同时维护大量长期OPC UA连接;
  • 通信进程需要全年持续运行;
  • 对设备兼容验证和底层协议控制要求很高;
  • PHP主要负责管理后台、API和业务展示,而不是实时采集;
  • 计划在工业网关或嵌入式Linux设备上部署采集程序。

这时可以采用类似结构:

PLC
 ↓
open62541采集服务
 ↓
MQTT / HTTP / Redis / 数据库
 ↓
PHP业务系统

open62541官方文档明确提供OPC UA客户端实现,项目使用C语言并具有跨平台设计。:contentReference[oaicite:6]{index=6}

这样做的代价是系统多了一层服务,部署和维护复杂度会增加,所以连接数量很少的普通PHP项目没有必要为了“架构更专业”强行增加中间层。

PHP OPC UA项目的推荐结构

如果要把本文示例整理成实际项目,可以把OPC UA连接代码集中封装,而不是在Controller中到处直接调用 ClientBuilder。

opcua-demo/
├── composer.json
├── vendor/
├── src/
│   └── OpcUa/
│       └── OpcUaService.php
├── bin/
│   └── monitor.php
└── public/
    └── index.php

OpcUaService.php 负责连接、读取和写入;monitor.php 负责长期订阅;Web Controller或API只调用Service或者读取缓存数据。

这样后面更换PLC、修改证书或者增加重连机制时,不需要把整个PHP项目里的业务代码全部修改一遍。

实际项目中还要特别注意什么

不要把设备密码提交到Git

OPC UA账号、私钥以及证书私钥都不应该直接出现在仓库中。至少使用环境变量,例如:

OPCUA_ENDPOINT=opc.tcp://192.168.1.100:4840
OPCUA_USER=operator
OPCUA_PASSWORD=your-secret-password

不要默认所有设备都支持同样的安全策略

客户端支持某种SecurityPolicy,不代表PLC服务器也支持。最终应该选择客户端和服务器共同支持的安全Endpoint。

不要把None模式直接当成生产默认值

开发网络为了快速验证,可以先使用无加密连接。如果OPC UA流量经过普通办公网络、跨主机网络或其他不完全可信的链路,应优先配置签名和加密。

给通信程序设置超时、重连和日志

PLC重启、交换机切换、网络闪断都是实际运行中会发生的情况。一次能连上并不意味着程序已经适合长期运行。正式采集程序至少应该记录:

  • 连接建立和断开时间;
  • Endpoint;
  • 重连次数;
  • NodeId读取错误;
  • 服务器返回的状态码;
  • 证书或认证错误。

该怎么选择最终实现方案

PHP实现OPC UA客户端:开源库推荐与代码示例
方案选择的关键不是“哪一个库绝对最好”,而是PHP是否需要直接长期维护工业设备连接。

如果需求是Laravel、Symfony或普通PHP项目直接读取几台PLC的数据,可以从 php-opcua/opcua-client 开始。它不要求额外启动C/C++网关,开发和部署路径最短。

如果当前项目已经统一使用PHP 8.4,也可以把 techdock/opcua 加入兼容性测试,比较两套PHP实现对目标PLC、证书和安全策略的支持情况。

如果系统的核心任务变成“长期连接大量工业设备并持续采集”,则可以把OPC UA通信从PHP Web层拆出来,使用专门Worker,或者使用open62541等协议栈建立独立采集服务,让PHP继续负责API、后台管理和业务逻辑。

总结

PHP实现OPC UA客户端现在并不需要自己处理TCP数据包和OPC UA二进制编码。使用 php-opcua/opcua-client,基本流程可以归纳为:Composer安装客户端库,连接 opc.tcp:// Endpoint,通过Browse找到真实NodeId,再使用Read和Write操作变量;需要实时监控时使用Subscription,需要正式部署时增加证书、安全策略和用户认证。

真正容易出问题的地方通常不是几行PHP语法,而是Endpoint、安全策略、证书信任、Namespace、NodeId、数据类型和节点访问权限。按这些层次逐步验证,比看到报错后反复更换PHP代码更有效。

对于少量设备,纯PHP方案结构简单;对于长期运行、大规模采集,则应该把OPC UA连接管理从普通HTTP请求中分离出去。确定这一点之后,再选择具体开源库,整个系统的结构会清晰很多。

本文涉及的库能力和API已参考 php-opcua/opcua-client 项目与文档、Packagist中的 techdock/opcua 信息以及 open62541 官方资料核对。:contentReference[oaicite:7]{index=7}

相关标签:

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。