Direct interaction
SocketConnection is the helper you reach for when ManagedClient is too high-level — debugging, scripting, integrating from non-PHP languages. It speaks the IPC envelope and nothing else.
PhpOpcua\SessionManager\Client\SocketConnection is a tiny static
helper that sends one IPC request and returns the decoded response.
It bypasses every typed surface of ManagedClient — no session
state, no wasSessionReused(), no method dispatch — and is the
right tool for:
- Healthchecks that don't need a full client
- One-shot IPC scripts (introspection, debugging, smoke tests)
- Bridges from non-PHP callers when you want to write the protocol by hand
The API
Two static methods:
SocketConnection::send(string \$endpoint, array \$payload, float \$timeout = 30.0): array
SocketConnection::sendVia(TransportInterface \$transport, array \$payload): array
send() takes care of building the transport from a URI; sendVia()
accepts a pre-built transport for hot paths where you keep one
around.
use PhpOpcua\SessionManager\Client\SocketConnection;
$response = SocketConnection::send(
endpoint: '/tmp/opcua-session-manager.sock', // unix path, tcp://, or unix:// URI
payload: ['command' => 'ping'],
timeout: 5.0,
);
assert($response['success'] === true);
echo "{$response['data']['sessions']} sessions on the daemon\n";
The return is the decoded response — exactly the envelope the
daemon wrote on the wire. The success / failure discrimination is
on $response['success']; failures expose
$response['error']['type'] and $response['error']['message'].
Endpoint resolution
The $endpoint argument is parsed by TransportFactory::create():
| Form | Transport selected |
|---|---|
unix:///path.sock |
UnixSocketTransport |
tcp://127.0.0.1:9990 |
TcpLoopbackTransport |
tcp://[::1]:9990 |
TcpLoopbackTransport (IPv6) |
/path.sock (scheme-less) |
UnixSocketTransport (backwards-compat) |
Non-loopback TCP endpoints throw at transport construction — the guard the daemon enforces at startup also runs on the client side.
If the endpoint is a Unix socket and the file does not exist, the helper raises:
DaemonException: Socket not found: /tmp/opcua-session-manager.sock. Is the daemon running?
A friendly version of "connection refused" for the common case.
Pre-built transport (sendVia)
When you make many calls in the same script, build the transport
once and pass it to sendVia():
use PhpOpcua\SessionManager\Client\SocketConnection;
use PhpOpcua\SessionManager\Ipc\TransportFactory;
$transport = TransportFactory::create('/tmp/opcua-session-manager.sock', timeout: 5.0);
// One open/close per call inside sendVia — the daemon expects request/response per connection.
for ($i = 0; $i < 100; $i++) {
$response = SocketConnection::sendVia($transport, ['command' => 'ping']);
}
Each sendVia() opens and closes the transport (the daemon
expects request/response per connection, not a persistent
multiplex). The benefit over send() is the cached endpoint
resolution and TransportFactory instantiation.
Healthcheck shape
The canonical healthcheck:
use PhpOpcua\SessionManager\Client\SocketConnection;
use PhpOpcua\SessionManager\Exception\DaemonException;
$endpoint = getenv('OPCUA_SOCKET_PATH') ?: '/tmp/opcua-session-manager.sock';
try {
$response = SocketConnection::send($endpoint, [
'command' => 'ping',
], timeout: 2.0);
if (! $response['success']) {
exit(1);
}
echo "ok sessions={$response['data']['sessions']}\n";
exit(0);
} catch (DaemonException $e) {
fwrite(STDERR, "fail {$e->getMessage()}\n");
exit(1);
}
Wire it as a periodic check from your monitoring agent. See Recipes · Healthcheck and monitoring.
Debugging with netcat
The simplest IPC tool of all is nc(1). For Unix sockets:
echo '{"command":"ping"}' \
| nc -U /tmp/opcua-session-manager.sock
# → {"success":true,"data":{"status":"ok",...}}
For TCP loopback:
echo '{"command":"ping"}' \
| nc 127.0.0.1 9990
The trailing newline matters — NDJSON framing relies on \n to
delimit. echo adds it for free; printf does not unless you
include it explicitly.
For deeper netcat patterns (list sessions, run commands by hand, verify the auth path), see Recipes · Debugging with netcat.
Cross-language bridges
The IPC protocol is JSON over a local socket — every language has a client for that. To call the daemon from Go, Python, Node.js, or anything else:
-
01
Open a Unix or TCP loopback socket
to the daemon's endpoint.
-
02
Write a request frame
— one JSON object, terminated by
\n, with the fields documented in Envelope and framing. -
03
Read the response frame
— one JSON object up to the first
\n. Parse it. -
04
Close the socket.
The daemon expects one round-trip per connection.
The type serialisation rules in
Type serialization apply — your
cross-language client needs to encode NodeId, Variant, etc.
matching the PHP shapes.
Where not to use it
- Anywhere
ManagedClientalready covers the surface. The typed surface — error handling, session state, fluent configuration — is what you give up. - For high-frequency calls.
ManagedClientreuses a single transport object across many calls without the per-call open / close overheadSocketConnectionincurs. - In application code.
SocketConnectionis plumbing. Treat it likepg_connect()— useful for tooling, hide behind a proper abstraction in production.