Events
Overview
The OPC UA client dispatches 47 PSR-14 events covering every stage of the client lifecycle: connections, reads, writes, browses, subscriptions, alarms, cache operations, retries, security, and type discovery. In Laravel, these events flow through Laravel's event system automatically — just register listeners.
How It Works
The OpcuaServiceProvider resolves Psr\EventDispatcher\EventDispatcherInterface from the Laravel container and passes it to OpcuaManager. Every client created through the manager dispatches events through this dispatcher. No extra configuration needed.
OPC UA Client ──dispatch──▸ PSR-14 EventDispatcher ──▸ Laravel Event System ──▸ Your Listeners
Listening to Events
Using Event::listen
// AppServiceProvider or any service provider boot() method
use Illuminate\Support\Facades\Event;
use PhpOpcua\Client\Event\ClientConnected;
use PhpOpcua\Client\Event\NodeValueRead;
use PhpOpcua\Client\Event\DataChangeReceived;
Event::listen(ClientConnected::class, function (ClientConnected $e) {
logger()->info("Connected to {$e->endpointUrl}");
});
Event::listen(NodeValueRead::class, function (NodeValueRead $e) {
logger()->debug("Read {$e->nodeId}: {$e->dataValue->getValue()}");
});
Event::listen(DataChangeReceived::class, function (DataChangeReceived $e) {
DB::table('sensor_readings')->insert([
'value' => $e->dataValue->getValue(),
'client_handle' => $e->clientHandle,
'timestamp' => now(),
]);
});
Using Listener Classes
// app/Listeners/LogOpcUaConnection.php
namespace App\Listeners;
use PhpOpcua\Client\Event\ClientConnected;
class LogOpcUaConnection
{
public function handle(ClientConnected $event): void
{
logger()->info("OPC UA connected to {$event->endpointUrl}");
}
}
// AppServiceProvider::boot()
Event::listen(ClientConnected::class, LogOpcUaConnection::class);
Using Queued Listeners
For heavy processing (database writes, HTTP calls, notifications), use queued listeners so the OPC UA client doesn't block:
namespace App\Listeners;
use Illuminate\Contracts\Queue\ShouldQueue;
use PhpOpcua\Client\Event\DataChangeReceived;
class StoreDataChange implements ShouldQueue
{
public $queue = 'opcua';
public function handle(DataChangeReceived $event): void
{
DB::table('sensor_readings')->insert([
'value' => $event->dataValue->getValue(),
'client_handle' => $event->clientHandle,
'subscription_id' => $event->subscriptionId,
]);
}
}
Event Reference
Connection Lifecycle
| Event |
Properties |
When |
ClientConnecting |
client, endpointUrl |
Before TCP connection |
ClientConnected |
client, endpointUrl |
After successful connection |
ConnectionFailed |
client, endpointUrl, exception |
Connection attempt failed |
ClientReconnecting |
client |
Before auto-retry reconnection |
ClientDisconnecting |
client |
Before graceful disconnect |
ClientDisconnected |
client |
After disconnect |
Session
| Event |
Properties |
When |
SessionCreated |
client, sessionId, authenticationToken |
OPC UA session created |
SessionActivated |
client, sessionId |
Session activated with credentials |
SessionClosed |
client, sessionId |
Session closed |
Secure Channel
| Event |
Properties |
When |
SecureChannelOpened |
client, securityPolicy, securityMode |
Secure channel established |
SecureChannelClosed |
client |
Secure channel closed |
Read / Write
| Event |
Properties |
When |
NodeValueRead |
client, nodeId, attributeId, dataValue |
After reading a node value |
NodeValueWritten |
client, nodeId, value, type, statusCode |
After successful write |
NodeValueWriteFailed |
client, nodeId, value, type, statusCode |
Write returned bad status |
WriteTypeDetecting |
client, nodeId |
Before auto-detecting write type |
WriteTypeDetected |
client, nodeId, builtinType |
Write type resolved |
Browse
| Event |
Properties |
When |
NodeBrowsed |
client, nodeId, direction, resultCount |
After browsing a node |
Subscriptions
| Event |
Properties |
When |
SubscriptionCreated |
client, subscriptionId, revisedPublishingInterval, revisedLifetimeCount, revisedMaxKeepAliveCount |
Subscription created |
SubscriptionDeleted |
client, subscriptionId |
Subscription deleted |
SubscriptionTransferred |
client, subscriptionId |
Subscription transferred |
SubscriptionKeepAlive |
client, subscriptionId, sequenceNumber |
Keep-alive received (no data) |
MonitoredItemCreated |
client, subscriptionId, clientHandle, monitoredItemId |
Monitored item created |
MonitoredItemModified |
client, subscriptionId, monitoredItemId |
Monitored item modified |
MonitoredItemDeleted |
client, subscriptionId, monitoredItemId |
Monitored item deleted |
TriggeringConfigured |
client, subscriptionId, triggeringItemId |
Triggering links configured |
DataChangeReceived |
client, subscriptionId, sequenceNumber, clientHandle, dataValue |
Data change notification |
EventNotificationReceived |
client, subscriptionId, sequenceNumber, clientHandle, eventFields |
Event notification received |
PublishResponseReceived |
client, subscriptionId, sequenceNumber |
Raw publish response |
Alarms
| Event |
Properties |
When |
AlarmActivated |
client, subscriptionId, clientHandle, sourceName?, severity?, message? |
Alarm entered active state |
AlarmDeactivated |
client, subscriptionId, clientHandle, sourceName?, severity?, message? |
Alarm returned to normal |
AlarmAcknowledged |
client, subscriptionId, clientHandle, sourceName?, message? |
Alarm acknowledged |
AlarmConfirmed |
client, subscriptionId, clientHandle, sourceName?, message? |
Alarm confirmed |
AlarmShelved |
client, subscriptionId, clientHandle, sourceName?, message? |
Alarm shelved |
AlarmSeverityChanged |
client, subscriptionId, clientHandle, sourceName?, severity?, previousSeverity? |
Alarm severity changed |
AlarmEventReceived |
client, subscriptionId, clientHandle, eventFields |
Generic alarm event |
LimitAlarmExceeded |
client, subscriptionId, clientHandle, sourceName?, severity?, message? |
Limit alarm threshold exceeded |
OffNormalAlarmTriggered |
client, subscriptionId, clientHandle, sourceName?, severity?, message? |
Off-normal condition detected |
Cache
| Event |
Properties |
When |
CacheHit |
client, key |
Value found in cache |
CacheMiss |
client, key |
Value not in cache, fetching from server |
Retry
| Event |
Properties |
When |
RetryAttempt |
client, attempt, maxRetries, exception |
Before retry attempt |
RetryExhausted |
client, attempt, maxRetries, exception |
All retries failed |
Certificate Trust
| Event |
Properties |
When |
ServerCertificateTrusted |
client, fingerprint |
Server cert found in trust store |
ServerCertificateAutoAccepted |
client, fingerprint |
Server cert auto-accepted and saved |
ServerCertificateRejected |
client, fingerprint, reason |
Server cert rejected |
ServerCertificateManuallyTrusted |
client, fingerprint |
Cert manually added to trust store |
ServerCertificateRemoved |
client, fingerprint |
Cert removed from trust store |
Type Discovery
| Event |
Properties |
When |
DataTypesDiscovered |
client, count |
After discoverDataTypes() completes |
Practical Examples
Log all connection events
use PhpOpcua\Client\Event\ClientConnected;
use PhpOpcua\Client\Event\ClientDisconnected;
use PhpOpcua\Client\Event\ConnectionFailed;
Event::listen(ClientConnected::class, function ($e) {
logger()->info("OPC UA connected: {$e->endpointUrl}");
});
Event::listen(ClientDisconnected::class, function ($e) {
logger()->info('OPC UA disconnected');
});
Event::listen(ConnectionFailed::class, function ($e) {
logger()->error("OPC UA connection failed: {$e->endpointUrl}", [
'error' => $e->exception->getMessage(),
]);
});
use PhpOpcua\Client\Event\CacheHit;
use PhpOpcua\Client\Event\CacheMiss;
$hits = 0;
$misses = 0;
Event::listen(CacheHit::class, function () use (&$hits) { $hits++; });
Event::listen(CacheMiss::class, function () use (&$misses) { $misses++; });
Alert on alarms via notification
use PhpOpcua\Client\Event\AlarmActivated;
use App\Notifications\AlarmTriggered;
Event::listen(AlarmActivated::class, function (AlarmActivated $e) {
$operators = User::role('operator')->get();
Notification::send($operators, new AlarmTriggered(
source: $e->sourceName,
severity: $e->severity,
message: $e->message,
));
});
Track write failures
use PhpOpcua\Client\Event\NodeValueWriteFailed;
Event::listen(NodeValueWriteFailed::class, function (NodeValueWriteFailed $e) {
logger()->warning("Write failed on {$e->nodeId}", [
'value' => $e->value,
'status' => sprintf('0x%08X', $e->statusCode),
]);
});
Retry observability
use PhpOpcua\Client\Event\RetryAttempt;
use PhpOpcua\Client\Event\RetryExhausted;
Event::listen(RetryAttempt::class, function (RetryAttempt $e) {
logger()->warning("Retry {$e->attempt}/{$e->maxRetries}: {$e->exception->getMessage()}");
});
Event::listen(RetryExhausted::class, function (RetryExhausted $e) {
logger()->error("All {$e->maxRetries} retries exhausted", [
'error' => $e->exception->getMessage(),
]);
});
Broadcast data changes to the frontend
use PhpOpcua\Client\Event\DataChangeReceived;
use Illuminate\Support\Facades\Broadcast;
Event::listen(DataChangeReceived::class, function (DataChangeReceived $e) {
broadcast(new \App\Events\SensorUpdated(
clientHandle: $e->clientHandle,
value: $e->dataValue->getValue(),
));
});
Disabling Events
To disable event dispatching for a specific client, pass null as the event dispatcher in the connection config:
$client = Opcua::connectTo('opc.tcp://...', [
'event_dispatcher' => null,
]);
Or use the NullEventDispatcher:
use PhpOpcua\Client\Event\NullEventDispatcher;
$client = Opcua::connect();
$client->setEventDispatcher(new NullEventDispatcher());