Browsing the Address Space
Getting Started
Introduction ConnectionCore
Browsing Reading-writing Method-call Subscriptions History-readReference
Types Error-handling Security Architecture Extension-object-codecs Testing Events Trust-storeBrowsing the Address Space
Browsing a Node
browse() returns the references (children) of a node:
use PhpOpcua\Client\Types\NodeId;
// Using string format
$references = $client->browse('i=85'); // Objects folder
// Or with a NodeId object
$references = $client->browse(NodeId::numeric(0, 85));
// Events: dispatches NodeBrowsed after each browse() call, CacheHit/CacheMiss when cache is active
foreach ($references as $ref) {
echo sprintf(
"%s (NodeId: ns=%d;i=%s, Class: %s)\n",
$ref->displayName,
$ref->nodeId->namespaceIndex,
$ref->nodeId->identifier,
$ref->nodeClass->name,
);
}Browse Parameters
use PhpOpcua\Client\Types\BrowseDirection;
use PhpOpcua\Client\Types\NodeClass;
$references = $client->browse(
nodeId: NodeId::numeric(0, 85),
direction: BrowseDirection::Forward,
referenceTypeId: NodeId::numeric(0, 33), // HierarchicalReferences
includeSubtypes: true,
nodeClasses: [NodeClass::Object, NodeClass::Variable], // filter by class
);Browse direction:
| Direction | Value | Meaning |
|---|---|---|
Forward |
0 | Children |
Inverse |
1 | Parents |
Both |
2 | Both directions |
Common reference types:
| NodeId | Name |
|---|---|
NodeId::numeric(0, 33) |
HierarchicalReferences |
NodeId::numeric(0, 35) |
Organizes |
NodeId::numeric(0, 47) |
HasComponent |
NodeId::numeric(0, 46) |
HasProperty |
NodeId::numeric(0, 40) |
HasTypeDefinition |
Node class filter:
Pass an array of NodeClass enum values to filter results. Empty array (default) means all classes.
use PhpOpcua\Client\Types\NodeClass;
// Only objects and variables
$refs = $client->browse($nodeId, nodeClasses: [NodeClass::Object, NodeClass::Variable]);
// Only methods
$refs = $client->browse($nodeId, nodeClasses: [NodeClass::Method]);
// All classes (default)
$refs = $client->browse($nodeId);Available NodeClass values: Object, Variable, Method, ObjectType, VariableType, ReferenceType, DataType, View.
ReferenceDescription Properties
Each reference returned by browse() has these properties:
$ref->referenceTypeId; // NodeId
$ref->isForward; // bool
$ref->nodeId; // NodeId
$ref->browseName; // QualifiedName
$ref->displayName; // LocalizedText
$ref->nodeClass; // NodeClass enum
$ref->typeDefinition; // ?NodeIdHandling Continuation
Some servers paginate large result sets. You have two options.
Automatic (recommended)
browseAll() follows all continuation points and returns the complete list:
$refs = $client->browseAll('i=85');Manual
If you need control over pagination:
$result = $client->browseWithContinuation(NodeId::numeric(0, 85));
$allRefs = $result->references;
$continuationPoint = $result->continuationPoint;
while ($continuationPoint !== null) {
$next = $client->browseNext($continuationPoint);
$allRefs = array_merge($allRefs, $next->references);
$continuationPoint = $next->continuationPoint;
}Recursive Browse
browseRecursive() walks the address space from a starting node and builds a tree of BrowseNode objects. Continuation points are handled at each level, and cycle detection prevents infinite loops on circular references.
$tree = $client->browseRecursive('i=85', maxDepth: 2);
foreach ($tree as $node) {
echo $node->displayName . "\n";
foreach ($node->getChildren() as $child) {
echo " " . $child->displayName . "\n";
}
}Parameters
$tree = $client->browseRecursive(
nodeId: NodeId::numeric(0, 85),
direction: BrowseDirection::Forward,
maxDepth: 3,
referenceTypeId: NodeId::numeric(0, 33),
includeSubtypes: true,
nodeClasses: [NodeClass::Object, NodeClass::Variable],
);| Parameter | Default | Description |
|---|---|---|
nodeId |
(required) | Starting node |
direction |
Forward |
Browse direction |
maxDepth |
null (configured default: 10) |
Max recursion depth. -1 for unlimited (capped at 256) |
referenceTypeId |
null |
Filter by reference type |
includeSubtypes |
true |
Include reference subtypes |
nodeClasses |
[] |
Filter by NodeClass enum values. Empty = all classes |
Depth Limits
maxDepth |
Behavior |
|---|---|
null |
Uses configured default (10, or your setDefaultBrowseMaxDepth() value) |
1 |
Direct children only |
-1 |
Unlimited (capped at 256) |
> 256 |
Capped at 256 |
You can change the default globally:
$client = ClientBuilder::create()
->setDefaultBrowseMaxDepth(20)
->connect('opc.tcp://localhost:4840');
$tree = $client->browseRecursive($nodeId); // uses 20
$tree = $client->browseRecursive($nodeId, maxDepth: 3); // override: 3Warning: High depth values can cause problems. Each level sends one browse request per node -- thousands of nodes means thousands of round-trips. Large trees eat memory, and massive browsing can overwhelm resource-constrained PLCs. Start small and increase only when needed.
BrowseNode Properties
Each node in the tree wraps a ReferenceDescription and holds its children:
$node->reference; // ReferenceDescription
$node->nodeId; // NodeId
$node->displayName; // LocalizedText
$node->browseName; // QualifiedName
$node->nodeClass; // NodeClass enum
$node->getChildren(); // BrowseNode[]
$node->hasChildren(); // boolCycle Detection
The method tracks every visited NodeId. If a node appears again, it is included as a leaf with no children, cutting the recursion. This matches the behavior of open62541, node-opcua, and the OPC Foundation .NET SDK.
Printing a Tree
function printTree(array $nodes, int $indent = 0): void
{
foreach ($nodes as $node) {
echo str_repeat(' ', $indent) . $node->displayName . "\n";
printTree($node->getChildren(), $indent + 1);
}
}
$tree = $client->browseRecursive(NodeId::numeric(0, 85), maxDepth: 3);
printTree($tree);Path Resolution
Instead of browsing step by step, resolve a human-readable path to a NodeId in one call:
$nodeId = $client->resolveNodeId('/Objects/Server/ServerStatus/State');
$dataValue = $client->read($nodeId);This calls TranslateBrowsePathsToNodeIds under the hood -- a single round-trip, much faster than manual browsing.
Path Format
- Segments separated by
/ - Leading
/is optional - Default start is Root (
ns=0;i=84) - For non-zero namespaces, use
ns:Nameformat
// Simple path
$nodeId = $client->resolveNodeId('/Objects/Server');
// With namespaced segments
$nodeId = $client->resolveNodeId('/Objects/2:MyPLC/2:Temperature');
// Custom starting node
$nodeId = $client->resolveNodeId('Server', NodeId::numeric(0, 85));Advanced: translateBrowsePaths
For full control over TranslateBrowsePathsToNodeIds, including resolving multiple paths in a single request:
use PhpOpcua\Client\Types\QualifiedName;
use PhpOpcua\Client\Types\StatusCode;
// Fluent builder
$results = $client->translateBrowsePaths()
->from('i=85')->path('Server', 'ServerStatus')
->execute();
if (StatusCode::isGood($results[0]->statusCode)) {
$targetNodeId = $results[0]->targets[0]->targetId;
}
// Or with array (still works)
$results = $client->translateBrowsePaths([
[
'startingNodeId' => NodeId::numeric(0, 85),
'relativePath' => [
['targetName' => new QualifiedName(0, 'Server')],
['targetName' => new QualifiedName(0, 'ServerStatus')],
],
],
]);Tip: The builder's
->from()sets the starting node, and->path()accepts segment names as separate arguments. Call->from()again to add another path in the same request.
Each path element supports:
| Field | Default | Description |
|---|---|---|
targetName |
(required) | QualifiedName of the target |
referenceTypeId |
HierarchicalReferences | Reference type to follow |
isInverse |
false |
Follow inverse references |
includeSubtypes |
true |
Include subtypes |
Caching
Browse results are cached by default using an in-memory PSR-16 cache with a 300-second TTL. This avoids redundant server round-trips when the address space is browsed repeatedly — common in industrial PLC environments where the node tree rarely changes.
Default Behavior
Caching is active out of the box. No setup required:
$refs = $client->browse('i=85'); // hits the server
$refs = $client->browse('i=85'); // served from cacheCache Bypass
Skip the cache for a single call with useCache: false:
$refs = $client->browse('i=85', useCache: false);
$refs = $client->browseAll('i=85', useCache: false);
$nodeId = $client->resolveNodeId('/Objects/Server', useCache: false);Custom Cache Driver
Any PSR-16 CacheInterface implementation works — including Laravel's cache:
use PhpOpcua\Client\ClientBuilder;
use PhpOpcua\Client\Cache\InMemoryCache;
use PhpOpcua\Client\Cache\FileCache;
// In-memory (default)
$client = ClientBuilder::create()
->setCache(new InMemoryCache(ttl: 300))
->connect('opc.tcp://localhost:4840');
// File-based (survives PHP process restart)
$client = ClientBuilder::create()
->setCache(new FileCache('/tmp/opcua-cache', ttl: 600))
->connect('opc.tcp://localhost:4840');
// Laravel
$client = ClientBuilder::create()
->setCache(app('cache')->store('redis'))
->connect('opc.tcp://localhost:4840');
// Disable caching entirely
$client = ClientBuilder::create()
->setCache(null)
->connect('opc.tcp://localhost:4840');Cache Invalidation
// Invalidate results for a specific node
$client->invalidateCache(NodeId::numeric(0, 85));
// Flush all cached results
$client->flushCache();Cached Methods
| Method | Cached |
|---|---|
browse() |
Yes |
browseAll() |
Yes |
resolveNodeId() |
Yes |
getEndpoints() |
Yes |
discoverDataTypes() |
Yes (discovered type definitions are cached and replayed) |
browseWithContinuation() |
No |
browseNext() |
No |
browseRecursive() |
No (but each internal browseAll() call is cached) |
Cache Key Format
Keys include the endpoint URL hash, operation type, NodeId, and browse parameters. Two clients pointing at different servers never collide:
opcua:{endpoint_hash}:browse:{nodeId}:{direction}:{includeSubtypes}:{nodeClassMask}
opcua:{endpoint_hash}:browseAll:{nodeId}:{direction}:{includeSubtypes}:{nodeClassMask}
opcua:{endpoint_hash}:resolve:{startingNodeId}:{path_hash}
opcua:{endpoint_hash}:endpoints:{url_hash}
opcua:{endpoint_hash}:dataTypes:{namespaceIndex|all}Well-Known NodeIds
| Name | NodeId | Description |
|---|---|---|
| Root | NodeId::numeric(0, 84) |
Root of the address space |
| Objects | NodeId::numeric(0, 85) |
Objects folder |
| Types | NodeId::numeric(0, 86) |
Types folder |
| Views | NodeId::numeric(0, 87) |
Views folder |
| Server | NodeId::numeric(0, 2253) |
Server object |
| ServerStatus | NodeId::numeric(0, 2256) |
Server status |
| ServiceLevel | NodeId::numeric(0, 2267) |
Service level |