Writing values
Write one tag or many. The library detects the right OPC UA type for you by default; pass an explicit BuiltinType when you need to override that decision.
write() updates a node's Value attribute. Attribute-level writes
(e.g. changing a DisplayName) are theoretically possible via the OPC
UA Write service but rare in practice and not exposed on this library's
typed API — for those, drop down to a custom module.
Single value
$nodeId
The target node.
$value
The PHP value to write. Accepts bool, int, float, string,
DateTimeImmutable, NodeId, Variant, ExtensionObject, and arrays
of any of the above.
$type
Override the OPC UA type used on the wire. When null, the library
auto-detects the type — see "Type detection" below.
use PhpOpcua\Client\Types\StatusCode;
$status = $client->write('ns=2;s=Devices/PLC/Setpoint', 42.5);
if (! StatusCode::isGood($status)) {
throw new RuntimeException(
'Write rejected: ' . StatusCode::getName($status)
);
}
The return value is the per-item StatusCode (an int). A Good
status (0) means the server accepted the write. Common bad statuses:
| StatusCode | Meaning |
|---|---|
BadNodeIdUnknown |
The node does not exist on this server |
BadUserAccessDenied |
Session is not authorised to write |
BadTypeMismatch |
The value does not match the node's DataType |
BadOutOfRange |
Value is outside the declared engineering range |
BadWriteNotSupported |
The attribute is read-only on this server |
Type detection
By default, the builder enables write-type auto-detection
(setAutoDetectWriteType(true)). When $type is omitted, the client:
-
01
Checks the metadata cache.
If the node's
DataTypeis already cached, use it directly — no server round-trip. -
02
Otherwise, reads the node's `DataType` attribute.
Dispatches
WriteTypeDetecting, issues a Read, and caches the result. The detection adds one round-trip on the first write to each node. -
03
Maps the OPC UA `DataType` NodeId to a `BuiltinType`.
Standard built-ins (
Boolean,Int32,Double,String, …) map directly. Custom DataTypes that resolve to a built-in via their supertype chain also map. -
04
Encodes the PHP value as that built-in.
Dispatches
WriteTypeDetectedwith the chosen type and afromCacheflag, then sends the WriteRequest.
Disable detection when you want absolute control:
$client = ClientBuilder::create()
->setAutoDetectWriteType(false)
->connect('opc.tcp://plc.local:4840');
// Now $type is mandatory.
$client->write('ns=2;s=Tag', 42, BuiltinType::Int32);
Detection failures
WriteTypeDetectionException is raised when:
- The
DataTyperead returns a bad status. - The DataType cannot be mapped to a
BuiltinType(a structure without a registered codec, an unknown abstract type).
WriteTypeMismatchException is reserved for cases where an explicit
$type passed by the caller would clash with a known DataType. The
library does not currently raise it during normal write paths — the
class exists for custom validation code and future static-analysis
hooks.
Multiple writes — writeMulti
use PhpOpcua\Client\Types\BuiltinType;
$statuses = $client->writeMulti([
['nodeId' => 'ns=2;s=Setpoint', 'value' => 42.5],
['nodeId' => 'ns=2;s=Mode', 'value' => 1, 'type' => BuiltinType::Int32],
['nodeId' => 'ns=2;s=Label', 'value' => 'auto'],
]);
foreach ($statuses as $i => $status) {
if (! StatusCode::isGood($status)) {
// Inspect the i-th entry
}
}
The return is an int[] parallel to the request array.
Fluent builder
$statuses = $client->writeMulti()
->node('ns=2;s=Setpoint')->value(42.5)
->node('ns=2;s=Mode')->int32(1)
->node('ns=2;s=Label')->string('auto')
->execute();
The builder exposes one method per BuiltinType (boolean, byte,
int16, uint16, int32, uint32, int64, uint64, float,
double, string, …). Each fixes the wire-type for that entry and
skips auto-detection.
| Builder method | OPC UA type |
|---|---|
value(mixed) |
auto-detected |
typed(mixed, BuiltinType) |
explicit |
boolean(bool) |
Boolean |
sbyte(int) / byte(int) |
SByte / Byte |
int16(int) / uint16(int) |
Int16 / UInt16 |
int32(int) / uint32(int) |
Int32 / UInt32 |
int64(int) / uint64(int) |
Int64 / UInt64 |
float(float) |
Float |
double(float) |
Double |
string(string) |
String |
For any built-in type without a dedicated method (ByteString,
DateTime, LocalizedText, NodeId, Variant, …) reach for
typed($value, BuiltinType::ByteString) (or the matching BuiltinType
case) instead.
Writing arrays
Arrays use the corresponding Variant array form. The library detects
"array of T" automatically when auto-detect is on; with explicit types,
the same BuiltinType applies to every element.
$client->write('ns=2;s=Setpoints', [10.0, 20.0, 30.0]);
// Auto-detected as Variant<Double[]> if the node's DataType is Double.
$client->write('ns=2;s=Setpoints', [10.0, 20.0, 30.0], BuiltinType::Double);
// Explicit — works even when auto-detect is off.
For mixed-type arrays, use Variant directly or write multiple
individual values. See Recipes · Writing typed
arrays.
Write events
The write path dispatches three PSR-14 events:
WriteTypeDetecting— before detection starts (carries the node)WriteTypeDetected— after detection (carriesdetectedTypeandfromCache)NodeValueWritten— on success (carries the value and the status code)NodeValueWriteFailed— on a bad status code (carries the failure details)
Wire a dispatcher to instrument write traffic — see Observability · Events.