|
1 | 1 | # Changelog |
2 | 2 |
|
| 3 | +## [3.0.0] - 2026-03-22 |
| 4 | + |
| 5 | +### Changed |
| 6 | + |
| 7 | +- **Breaking**: Updated dependency `gianfriaur/opcua-php-client` from `^2.0` to `^3.0`. |
| 8 | +- **Breaking**: `nodeClassMask` parameter replaced with `nodeClasses` array. Browse methods (`browse()`, `browseWithContinuation()`, `browseAll()`, `browseRecursive()`) now accept `NodeClass[] $nodeClasses = []` instead of `int $nodeClassMask = 0`. Pass an array of `NodeClass` enum values (e.g. `[NodeClass::Object, NodeClass::Variable]`) instead of a raw bitmask integer. Empty array means all classes (same as the old `0`). |
| 9 | +- **Breaking**: Strict return types for all service responses. The following methods now return typed DTOs instead of associative arrays: |
| 10 | + - `createSubscription()` → `SubscriptionResult` (`->subscriptionId`, `->revisedPublishingInterval`, `->revisedLifetimeCount`, `->revisedMaxKeepAliveCount`) |
| 11 | + - `createMonitoredItems()` → `MonitoredItemResult[]` (`->statusCode`, `->monitoredItemId`, `->revisedSamplingInterval`, `->revisedQueueSize`) |
| 12 | + - `createEventMonitoredItem()` → `MonitoredItemResult` |
| 13 | + - `call()` → `CallResult` (`->statusCode`, `->inputArgumentResults`, `->outputArguments`) |
| 14 | + - `browseWithContinuation()` / `browseNext()` → `BrowseResultSet` (`->references`, `->continuationPoint`) |
| 15 | + - `publish()` → `PublishResult` (`->subscriptionId`, `->sequenceNumber`, `->moreNotifications`, `->notifications`, `->availableSequenceNumbers`) |
| 16 | + - `translateBrowsePaths()` → `BrowsePathResult[]` (`->statusCode`, `->targets`) with `BrowsePathTarget` (`->targetId`, `->remainingPathIndex`) |
| 17 | +- **Breaking**: Ambiguous `$items` parameters renamed for named parameter clarity: `readMulti($readItems)`, `writeMulti($writeItems)`, `createMonitoredItems($subscriptionId, $monitoredItems)`. Only affects code using named parameters. |
| 18 | +- All `TypeSerializer` getters updated to use `public readonly` properties from `opcua-php-client` v3.0.0 (`$ref->nodeId` instead of `$ref->getNodeId()`, etc.). |
| 19 | +- `TypeSerializer` now preserves `Variant` multi-dimensional array dimensions through serialization/deserialization roundtrips. |
| 20 | +- Method whitelist expanded from 32 to 37 methods to support all new v3.0.0 operations. |
| 21 | + |
| 22 | +### Added |
| 23 | + |
| 24 | +- **All methods accepting `NodeId` now also accept `string`.** Pass OPC UA string format directly (e.g. `'i=2259'`, `'ns=2;s=MyNode'`). Applies to: `read`, `write`, `browse`, `browseAll`, `browseWithContinuation`, `browseRecursive`, `call` (both params), `historyReadRaw`, `historyReadProcessed`, `historyReadAtTime`, `createEventMonitoredItem`, `resolveNodeId` (`$startingNodeId`), `invalidateCache`. Also works inside arrays for `readMulti`, `writeMulti`, `createMonitoredItems`, `translateBrowsePaths`. |
| 25 | +- **Fluent/Builder API** for multi-node operations. `readMulti()`, `writeMulti()`, `createMonitoredItems()`, and `translateBrowsePaths()` now return a fluent builder when called without arguments: `$client->readMulti()->node('i=2259')->value()->node('i=1001')->displayName()->execute()`. The array-based API still works when passing arguments directly. |
| 26 | +- **PSR-3 Logging.** `setLogger(LoggerInterface)` / `getLogger()` on `ManagedClient`. Uses `NullLogger` by default. |
| 27 | +- **PSR-16 Cache management.** `setCache(?CacheInterface)` / `getCache()` on `ManagedClient`. `invalidateCache(NodeId|string)` and `flushCache()` are forwarded to the daemon's underlying `Client` via IPC. |
| 28 | +- **`useCache` parameter** added to `browse()`, `browseAll()`, `getEndpoints()`, and `resolveNodeId()`. Forwarded to the daemon to control cache behaviour per-call. |
| 29 | +- **`getExtensionObjectRepository()`** returns a local `ExtensionObjectRepository` instance on `ManagedClient`. |
| 30 | +- **`discoverDataTypes(?int $namespaceIndex, bool $useCache)`** — forwarded to the daemon's `Client` to discover and register dynamic codecs for server-defined structured types. |
| 31 | +- **`transferSubscriptions(int[] $subscriptionIds, bool $sendInitialValues)`** — transfer existing subscriptions to a new session after reconnection without data loss. Returns `TransferResult[]`. |
| 32 | +- **`republish(int $subscriptionId, int $retransmitSequenceNumber)`** — re-request notifications that were sent but not yet acknowledged. |
| 33 | +- `psr/log` ^3.0 and `psr/simple-cache` ^3.0 added as dependencies. |
| 34 | +- `TypeSerializer` now serializes/deserializes all new v3.0.0 DTO types: `SubscriptionResult`, `MonitoredItemResult`, `CallResult`, `BrowseResultSet`, `PublishResult`, `BrowsePathResult`, `BrowsePathTarget`, `TransferResult`. |
| 35 | +- Unit tests for all new DTO serialization roundtrips (SubscriptionResult, MonitoredItemResult, CallResult, BrowseResultSet, PublishResult, BrowsePathResult, TransferResult). |
| 36 | +- Unit tests for Variant multi-dimensional dimensions preservation. |
| 37 | +- Unit tests for `ManagedClient` Logger, Cache, and ExtensionObjectRepository configuration. |
| 38 | +- Unit tests for new setter rejection (`setLogger`, `setCache` blocked via method whitelist). |
| 39 | +- **Daemon-side PSR-3 logging.** The daemon now uses a `StreamLogger` (PSR-3 compliant) instead of `echo`. Configure via `--log-file` (default: stderr) and `--log-level` (default: info). The same logger is injected into each OPC UA `Client` created by the daemon, so client-level events (connections, retries, errors) are captured in the daemon's log output. |
| 40 | +- **Daemon-side cache configuration.** The daemon's `Client` instances now accept a configurable PSR-16 cache driver. Configure via `--cache-driver` (`memory`, `file`, `none`), `--cache-path` (required for file driver), and `--cache-ttl` (default: 300s). Browse, resolve, endpoint discovery, and type discovery results are cached inside the daemon. |
| 41 | +- `StreamLogger` class (`src/Logging/StreamLogger.php`) — minimal PSR-3 logger that writes to a file or stream with configurable minimum log level and `{placeholder}` interpolation. |
| 42 | +- **Automatic session recovery with subscription transfer.** When a `query` operation fails with `ConnectionException`, the daemon automatically attempts to reconnect, transfer active subscriptions to the new session via `transferSubscriptions()`, and republish unacknowledged notifications. If recovery succeeds, the original operation is retried transparently. Subscriptions that fail to transfer are removed from tracking. All recovery events are logged. |
| 43 | +- `Session` now tracks active subscription IDs. `addSubscription()`, `removeSubscription()`, `getSubscriptionIds()`, `hasSubscriptions()` methods added. Subscriptions are automatically tracked on `createSubscription` and untracked on successful `deleteSubscription`. |
| 44 | +- Fixed `getEndpoints()` returning raw arrays instead of `EndpointDescription[]` objects. |
| 45 | +- Fixed `republish()` returning `publishTime` as ISO 8601 string instead of `?DateTimeImmutable`. |
| 46 | +- Fixed `serializeEndpointDescription()` not including the `serverCertificate` field. |
| 47 | + |
| 48 | +### Breaking Changes |
| 49 | + |
| 50 | +- All service response methods listed above now return typed objects instead of arrays. Code using `$result['key']` must change to `$result->key`. |
| 51 | +- Browse methods no longer accept `int $nodeClassMask`. Use `array $nodeClasses` with `NodeClass` enum values instead. Replace `nodeClassMask: 3` with `nodeClasses: [NodeClass::Object, NodeClass::Variable]`. |
| 52 | +- `readMulti($items)` renamed to `readMulti($readItems)`, `writeMulti($items)` to `writeMulti($writeItems)`, `createMonitoredItems(..., $items)` to `createMonitoredItems(..., $monitoredItems)`. Only affects code using named parameters. |
| 53 | + |
3 | 54 | ## [2.0.0] - 2026-03-20 |
4 | 55 |
|
5 | 56 | ### Changed |
|
0 commit comments