opcua-client · master
Docs · Operations

Resolving paths

OPC UA browse paths are not URLs — they are sequences of BrowseName references. translateBrowsePaths() turns "/Objects/Server/ServerStatus" into a NodeId without you knowing the namespace indices.

The OPC UA TranslateBrowsePathsToNodeIds service takes a starting node and a list of RelativePath segments — each segment a QualifiedName — and returns the NodeId reached by following those references. It is the right tool when:

  • You know the human-readable path of a node but not its NodeId.
  • You want code that survives a server reorganising its namespace indices.

This library wraps it in two shapes: a thin path resolver (resolveNodeId()) and a fluent multi-path builder (translateBrowsePaths()).

resolveNodeId — single path

php examples/resolve.php
$serverStatus = $client->resolveNodeId('/Objects/Server/ServerStatus');
// → NodeId(ns=0;i=2256) on every standard OPC UA server
Parameters
$path
string required

Slash-separated browse path. Segments are interpreted as namespace-0 QualifiedNames by default; to specify a non-zero namespace, prefix a segment with <ns>: (e.g. /2:Devices/2:PLC/2:Speed).

$startingNodeId
NodeId|string|null optional default null

The node the path is relative to. null (default) starts at the Root folder (ns=0;i=84). Pass a NodeId or string to start elsewhere — useful when you have an object reference and want to resolve a method or a property under it.

$useCache
bool optional default true

Cache the resolution result. Same PSR-16 store as browse().

The slash-vs-NodeId dispatch

resolveNodeId() is called internally by every API that accepts NodeId|string. The dispatcher looks at the first character of the string:

  • Matches /^(ns=\d+;)?[isgb]=/ → parse as a NodeId (i=, s=, g=, b=)
  • Contains / and does not match the NodeId grammar → resolve as a browse path
  • Anything else → raise InvalidNodeIdException

This is why 'ns=2;s=Devices/PLC/Speed' is read as a string NodeId (slash inside an s= identifier is part of the identifier) while '/Objects/Server' is read as a browse path.

Warning

String NodeIds whose identifier starts with / are ambiguous and not supported by the dispatch heuristic. The combination is rare in practice; if you encounter one, build the NodeId with NodeId::string() and pass the object instead of a string.

translateBrowsePaths — multiple paths

For batched resolution, translateBrowsePaths() returns an array of BrowsePathResult items, one per request:

php batched path translation
$results = $client->translateBrowsePaths([
    [
        'startingNodeId' => 'i=85',                      // Objects
        'path'           => '/Server/ServerStatus',
    ],
    [
        'startingNodeId' => 'i=85',
        'path'           => '/2:Devices/2:PLC/2:Speed',
    ],
]);

foreach ($results as $i => $r) {
    if ($r->statusCode === 0) {
        foreach ($r->targets as $t) {
            echo "[$i] " . $t->targetId . "\n";
        }
    }
}

Each BrowsePathResult carries a statusCode and a targets array. A single path can yield multiple targets when the path traverses references that fork — typically zero or one in practice.

Fluent builder

translateBrowsePaths() returns a BrowsePathsBuilder when called without arguments:

php fluent path builder
$results = $client->translateBrowsePaths()
    ->from('i=85')
        ->path('Server', 'ServerStatus')
    ->from('i=85')
        ->path('Devices', 'PLC', 'Speed')
    ->execute();
Method Effect
from(NodeId|string) Start a new request anchored at that node
path(string ...$segments) Append QualifiedName segments (<ns>:Name syntax accepted)
segment(QualifiedName $qname) Append a single pre-built segment
execute(): BrowsePathResult[] Issue the call, return the results

Common usage

Path resolution shines in two patterns:

1. Namespace-stable lookups at startup.

php discover IDs at startup
$plcNs  = 2;   // resolved from the namespace table at connect time

$ids = [
    'speed'  => $client->resolveNodeId("/{$plcNs}:Devices/{$plcNs}:PLC/{$plcNs}:Speed"),
    'mode'   => $client->resolveNodeId("/{$plcNs}:Devices/{$plcNs}:PLC/{$plcNs}:Mode"),
    'health' => $client->resolveNodeId("/{$plcNs}:Devices/{$plcNs}:PLC/{$plcNs}:Health"),
];

// Use $ids['speed'] etc. for the rest of the session — fast, cached.

2. Methods on dynamically-discovered objects.

When you browse a folder and find an Object node, its callable methods are not visible from the reference alone — you resolve them by their BrowseName:

php resolve a method under an object
$method = $client->resolveNodeId('/Reset', startingNodeId: $deviceObject);

$client->call($deviceObject, $method, inputArguments: []);