Upgrading to v4.5
Bump to v4.5.0. Two inherited changes need attention: subscription notifications are typed objects now, and server certificates are bound to the endpoint ApplicationUri by default. The IPC envelope is unchanged.
v4.5 is a lock-step release with php-opcua/opcua-client v4.5.0 — the core's security-hardening release. The session manager itself gained no new commands and no new IPC surface; what it gained is the plumbing for two core changes that cross the daemon boundary.
Two things need your attention:
- Subscription notifications are typed objects. Application code that reads
$publishResult->notificationswith array offsets must move to properties. - The server certificate is now bound to the endpoint's
ApplicationUri, and the check is on by default. Servers with a misconfigured SAN extension will start failing.
Everything else — the IPC envelope, the CLI, the cache codec, the ManagedClient API — is unchanged.
Step 1 — Update Composer
composer require php-opcua/opcua-session-manager:^4.5
This pulls v4.5.0 and the matching opcua-client ^4.5 as a transitive dependency. Pin tighter (~4.5.0) if you want to vet patches before they roll out.
Step 2 — Migrate notification handling
The core's PublishResult::$notifications used to hold associative arrays with a 'type' discriminator. It now holds DataChangeNotification and EventNotification objects. ManagedClient::publish() returns them decoded — TypeSerializer rebuilds the typed instance, with a real DataValue / Variant inside, on the client side of the socket.
foreach ($client->publish()->notifications as $n) {
if ($n['type'] === 'DataChange') {
echo $n['clientHandle'], ' → ', var_export($n['dataValue'], true), PHP_EOL;
}
}
use PhpOpcua\Client\Module\Subscription\DataChangeNotification;
use PhpOpcua\Client\Module\Subscription\EventNotification;
foreach ($client->publish()->notifications as $n) {
if ($n instanceof DataChangeNotification) {
echo $n->clientHandle, ' → ', var_export($n->dataValue->getValue(), true), PHP_EOL;
}
if ($n instanceof EventNotification) {
echo $n->clientHandle, ' → ', count($n->eventFields), ' fields', PHP_EOL;
}
}
Note that $n->dataValue is a real DataValue — in v4.4 the value that came back over IPC was still the serialized array, so this is a fix as much as a migration.
If you use auto-publish, there is nothing to migrate. The PSR-14 events (DataChangeReceived, EventNotificationReceived, …) carry the same payload they always did.
Step 3 — Decide on ApplicationUri verification
The core now requires the server certificate's SAN ApplicationUri to match the ApplicationUri the endpoint declares in its ApplicationDescription during discovery. On mismatch it throws UntrustedCertificateException, so a certificate trusted for server A is no longer accepted from server B.
The check is on by default. If one of your servers has a misconfigured SAN extension, opt out for that session only:
$client = (new ManagedClient())
->setSecurityPolicy(SecurityPolicy::Basic256Sha256)
->setSecurityMode(SecurityMode::SignAndEncrypt)
->verifyApplicationUri(false);
$client->open('opc.tcp://legacy-plc.example:4840');
The flag rides in the open command's config object as verifyApplicationUri, lands in SessionConfig, and is applied to that session's ClientBuilder. Leave it unset to keep the core default (verification on).
Treat the opt-out as a workaround for a broken server, not a default — it disables the check that stops one server from presenting another's trusted certificate.
Step 4 — Verify the daemon version
vendor/bin/opcua-session-manager --version
# → opcua-session-manager 4.5.0
What did not change
- IPC envelope shape. Still the flat
{command, sessionId?, method?, params?, args?, authToken?}request and{success, data | error}response. No new commands. - Notification payload on the wire. Notifications keep the
{type, clientHandle, dataValue|eventFields}envelope the daemon has emitted since v4.0 — only the PHP type on either end changed. Version skew therefore works in both directions (see below). ManagedClientpublic API. One method added (verifyApplicationUri()); nothing removed, renamed, or re-signed.- CLI flag names and defaults. Same set as v4.4.
- Cache codec. Still
Cache\WireCacheCodec. No reseed required.
What did change
Inherited from opcua-client v4.5.0
CreateSessionResponse.serverSignatureis verified (Part 4 §5.6.2 proof of possession) — previously read and discarded.- The ECDH ephemeral key signature is verified on ECC profiles before the key is accepted for nonce derivation.
- Server certificate ↔ endpoint
ApplicationUribinding, with theverifyApplicationUri(bool)opt-out (Step 3). - Secure channel headers are validated —
channelId/tokenIdmust match the negotiated values and sequence numbers must increase strictly (anti-replay, Part 6 §6.7.2.4). - Trust store decisions no longer rely on SHA-1 alone —
FileTrustStorecompares stored DER via SHA-256, keeping SHA-1 only for file naming. PublishResult::$notificationsholds typed objects (Step 2), and the wire DTOs (NodeId,DataValue,Variant,EndpointDescription, the module result DTOs, …) are nowfinal.EndpointDescriptiongained a nullableapplicationUri, round-tripped across IPC byTypeSerializer.- PHPStan level 9 on the core's
src/, no baseline.
Added in opcua-session-manager v4.5.0
TypeSerializerhandles the typed notifications in both directions. Without this a Publish response carrying any notification aborted the daemon's reply withSerializationException.ManagedClient::verifyApplicationUri()plusSessionConfig::$verifyApplicationUriand the matchingClientBuilderwiring inCommandHandler::buildClientFromConfig().EndpointDescription::$applicationUriround-trips across IPC, sogetEndpoints()results carry it.SessionManagerDaemon::VERSIONbumped to'4.5.0'.
See the full CHANGELOG for the line-by-line list.
Compatibility note — client / daemon version skew
Because the notification payload on the wire is unchanged, skew works in both directions:
- A v4.5
ManagedClientagainst a v4.4 daemon decodes the daemon's Publish reply into typed objects — the daemon emits the same{type, …}envelope either way. - A v4.4
ManagedClientagainst a v4.5 daemon keeps receiving arrays, exactly as before.
The one asymmetry is verifyApplicationUri: a pre-v4.5 daemon ignores the config key (SessionConfig::fromArray() has always dropped unknown keys), so the opt-out silently does nothing there. That is safe — a v4.4 daemon does not perform the check in the first place.
Upgrade order: daemon first, then ManagedClient instances, so the security checks are active before application code starts relying on the typed notifications.
Rollback
Rolling back to v4.4 is safe at the protocol level. At the application level, revert the Step 2 migration first — a v4.4 ManagedClient hands back arrays, so instanceof checks silently stop matching rather than failing loudly.
composer require php-opcua/opcua-session-manager:^4.4