| eyebrow | Docs · Getting started | |||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| lede | Three layers of a Laravel-OPCUA application: the facade you call, the manager Laravel binds, and the optional daemon that holds OPC UA sessions across requests. Internalise this once. | |||||||||||||||||
| see_also |
|
|||||||||||||||||
| prev |
|
|||||||||||||||||
| next |
|
Three concepts you need clear in your head before going deeper. This page is the orientation map.
┌─────────────────────────────────────────────────────────────────────┐
│ Your Laravel application │
│ - HTTP controllers │
│ - Console commands │
│ - Queue workers │
│ - Livewire components │
│ - Listeners │
│ │
│ Opcua::read(...) ←── facade │
│ OpcuaManager::read(...) ←── injected manager │
└─────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ OpcuaManager (container singleton) │
│ - Resolves configuration │
│ - Picks direct vs managed mode │
│ - Wires logger / cache / event dispatcher from the container │
│ - Caches connection instances per name │
└─────────────────────────────────────────────────────────────────────┘
│ │
▼ (direct mode) ▼ (managed mode)
┌─────────────────────────────┐ ┌──────────────────────────────────┐
│ opcua-client │ │ ManagedClient ──IPC──→ daemon │
│ ClientBuilder→Client │ │ (opcua-session-manager) │
│ Opens a session per use │ │ Persistent sessions │
└─────────────────────────────┘ └──────────────────────────────────┘
│ │
└──────────────┬───────────────┘
▼
┌─────────────────────┐
│ OPC UA server │
│ (PLC, SCADA, …) │
└─────────────────────┘
Three things to internalise:
- Application code is always the same shape. Whether the
underlying mode is direct or managed, the facade / manager
API is identical. You write
Opcua::read('ns=2;s=...')either way. OpcuaManageris the integration layer. It owns configuration parsing, mode selection, lazy connection creation. Everything Laravel-specific lives in or around it.- The daemon is optional. Without it, every operation opens a fresh OPC UA session. With it, sessions live across HTTP requests — the whole point of using Laravel rather than a CLI tool.
When the daemon is not running (or
OPCUA_SESSION_MANAGER_ENABLED=false), OpcuaManager creates a
fresh Client per connection name on first use.
HTTP request 1
Opcua::read('ns=2;s=...')
→ OpcuaManager::connection('default') (cached in Laravel container singleton)
→ If no Client cached: ClientBuilder::create()->setX()->...->connect()
→ Client::read(...)
→ Response
(end of request — singleton state survives for next request only if process is long-lived)
HTTP request 2 (in PHP-FPM — fresh process)
Same flow — Client was destroyed when the request worker recycled
PHP-FPM workers recycle the singleton between requests. The "caching in container" doesn't help across requests; it only helps within a single request that issues multiple OPC UA calls.
Cost: ~1 second OPC UA handshake per request worker. If your controller does several OPC UA operations in one request, they share the same session — no per-call handshake.
When the daemon is running, OpcuaManager creates a
ManagedClient that speaks IPC with the daemon. The daemon
holds the OPC UA session.
opcua:session daemon runs in the background, holding 0..N OPC UA sessions
HTTP request 1
Opcua::read('ns=2;s=...')
→ OpcuaManager::connection('default')
→ ManagedClient::connect('opc.tcp://...')
→ IPC frame: {"command":"open","endpointUrl":"opc.tcp://...","config":{...},"authToken":"..."}
→ Daemon checks the session-reuse cache (key derived from
endpoint + identity-related config keys) → returns sessionId
→ ManagedClient::read(...)
→ IPC frame: {"command":"query","sessionId":"...","method":"read","params":[...],"authToken":"..."}
→ Daemon reads from its existing OPC UA session
→ Response: {"success":true,"data":<DataValue payload>}
(end of request — daemon-side OPC UA session lives on, unless the
request explicitly called Opcua::disconnect(), which also sends
{"command":"close"} and releases the session)
HTTP request 2
Same flow — the daemon's session is reused; sub-millisecond connect
Cost: ~1ms IPC round-trip per call. The OPC UA handshake happens once on the daemon side, then never again until the daemon restarts.
For request-driven workloads, this is the difference between an unusable 1-second OPC UA latency per request and a snappy ~3-5ms total.
OpcuaManager::isSessionManagerRunning() does a
file_exists($socketPath) check for Unix-domain endpoints; for
TCP endpoints it always returns true. Mode selection happens on
each call to connection() — the manager picks managed mode
whenever the socket file exists (or, on TCP, whenever
session_manager.enabled is true).
Forcing direct mode in production (e.g. for a CLI command that doesn't need the daemon):
OPCUA_SESSION_MANAGER_ENABLED=false php artisan plc:somethingSee Session manager · Monitoring the daemon.
OpcuaManager is bound as a singleton:
$this->app->singleton(OpcuaManager::class, fn($app) => new OpcuaManager(...));
$this->app->alias(OpcuaManager::class, 'opcua');Both app(OpcuaManager::class) and app('opcua') return the
same instance.
PhpOpcua\LaravelOpcua\Facades\Opcua resolves the singleton and
proxies method calls through it. Every method on the facade is
documented in the DocBlock — your IDE picks them all up.
config/opcua.php — a single config file. The package merges
its built-in defaults so you can override only what you need.
OPC UA log lines route through config/logging.php channels
specified by log_channel in config/opcua.php. Use any Laravel
log channel (single, stack, syslog, papertrail, slack, …).
OPC UA browse / resolve / endpoint caching routes through
config/cache.php stores specified by cache_store. Any Laravel
cache backend works (file, redis, memcached, database, …).
The 47-class PSR-14 event catalogue from opcua-client (see the
event reference)
flows through Laravel's event dispatcher — Illuminate\Events\Dispatcher
implements the PSR-14 EventDispatcherInterface, so the events
arrive at Event::listen(...) listeners natively. Queue listeners
with ShouldQueue, broadcast with ShouldBroadcast. See
Events · Overview.
php artisan opcua:session starts the daemon. Configurable via
flags and config. See Reference · Artisan
commands.
- Does not embed the OPC UA stack. It depends on
opcua-client, which is the actual implementation. - Does not depend on the session manager at runtime. The daemon dependency is bundled, but the package works without the daemon — just without persistent sessions.
- Does not bind every OPC UA value to Eloquent. Persisting reads is your application's concern; see Recipes · Persistent tag history for the canonical pattern.
- Does not provide a UI. Build your own (Blade, Livewire, Inertia, Filament); see Integrations and Recipes.
This package wraps opcua-client and opcua-session-manager —
the OPC UA-level documentation lives in those libraries:
This documentation focuses on the Laravel-specific surface: how to use it from a controller, a job, a Livewire component, a Pest test. For OPC UA primitives (what's a NodeId? what's a security policy?), the upstream docs are the source of truth.
- Facade vs injection — the first design choice in any non-trivial application.
- The config file — every
knob in
config/opcua.php. - Session manager · Overview — when and why to run the daemon.