Changelog
[4.5.0] - 2026-08-11
Lock-step release with php-opcua/opcua-client v4.5.0, its security-hardening release. One CLI command needed migrating — watch, for the typed subscription notifications. Separately, this release fixes three defects in generate:nodeset's output that were found while verifying the bump on Linux.
Changed
- Bumped
php-opcua/opcua-clientfrom^4.4.0to^4.5.0. - Bumped
Application::VERSIONto4.5.0.opcua-cli --versionnow reports4.5.0. watchmigrated to the typed notifications. The core'sPublishResult::$notificationsnow holdsDataChangeNotification/EventNotificationobjects instead of['type' => 'DataChange', …]arrays, soWatchCommandmatches withinstanceof DataChangeNotificationand reads$notif->dataValueinstead of array offsets. Without the change the command silently printed nothing: the array offset on an object never matched. Output format is unchanged.
Fixed — generate:nodeset output
All three are pre-existing, and all three produced code that fails only on a case-sensitive filesystem or at the point where PHP resolves a relative class name. They are the source of the corresponding fixes in php-opcua/opcua-client-nodeset v4.5.0.
-
DTOs with an enum field were unconstructible, and their codecs could not decode. Codecs are generated into
<Namespace>\Codecsand DTOs into<Namespace>\Types, while both address enums relatively asEnums\Foo— which PHP resolves against the current namespace, i.e.<Namespace>\Codecs\Enumsand<Namespace>\Types\Enums. Neither exists. In a codec that raisedError: Class not foundon decode; in a DTO the declared property type resolved to a non-existent class, so no value could satisfy it and construction always threwTypeError.CodeGenerator::generateDtoClass()andgenerateCodecClass()now emituse <Namespace>\Enums;whenever a generated enum is referenced, and omit it otherwise. -
Dependency registrar names were guessed from the model URI.
GenerateNodesetCommandderived the dependency class from the directory name (http://opcfoundation.org/UA/DI/→DI→DIRegistrar) while the registrar is actually named after the NodeSet2 file (Opc.Ua.Di.NodeSet2.xml→DiRegistrar). Any spec whose file casing differs from its URI got an unloadable reference. The newresolveDependencyRegistrar()takes the name from the registrar file that exists next to the output directory: exact match first, then case-insensitive, then — for a spec split across several nodesets, such as AML →AMLBaseTypes+AMLLibraries— the single registrar whose name extends the referenced one. When the dependency has not been generated yet it keeps the directory-derived guess, leaving it to the consumer's cleanup pass.Resolution reads the names
glob()reports and compares them case-sensitively, rather than probingis_file()with the guessed name. On a case-insensitive filesystem (Windows, default macOS) the probe confirmsDIRegistrar.phpwhile the file — and therefore the class — isDiRegistrar, so generating on Windows would emit a reference that fails to autoload on Linux. The generated output is now identical on every platform.
Tests
- New
tests/Unit/GeneratedEnumResolutionTest.php(5 tests): theEnumsimport is emitted for DTOs and codecs that need it and omitted for those that don't, plus an end-to-end case that generates an enum, a DTO and a codec, loads them, and round-trips a value throughBinaryEncoder/BinaryDecoder. That last test reproduces the exactTypeErrorwhen the import is removed. - New
tests/Unit/DependencyRegistrarResolutionTest.php(5 tests): exact match, exact-over-case-insensitive precedence, split-spec resolution, the not-yet-generated fallback, and the ambiguous-candidates fallback. The exact-match test also asserts that the emitted class name matches a file on disk byte-for-byte, which is what fails when resolution goes throughis_file()on a case-insensitive filesystem. The precedence test needs two names differing only in case, so it skips where the filesystem cannot hold both. tests/Integration/CliTest.phpmigrated to the typed notifications alongsideWatchCommand.
Compatibility
- No CLI interface change. Same commands, same flags, same output formats.
- Applications embedding
CodeGeneratordirectly will see the extrauseline in generated DTOs and codecs that reference enums. Previously generated code keeps working; regenerating is what picks up the fix.
[4.4.0] - 2026-05-28
Lock-step release with php-opcua/opcua-client v4.4.0. The CLI consumes the core's OpcUaClientInterface plus ClientBuilder / Types\* surface, all of which is additive in v4.4 — every command keeps working as-is. New core capabilities (AggregateModule, HistoryUpdate, FileTransferModule, the pluggable ClientTransportInterface) are reachable via the underlying client; surfacing them as dedicated CLI sub-commands is roadmap (see ROADMAP.md).
Changed
- Bumped
php-opcua/opcua-clientfrom^4.3.0to^4.4.0. - Bumped
Application::VERSIONto4.4.0.opcua-cli --versionnow reports4.4.0. - Bumped CI test-server suite from
uanetstandard-test-suite@v1.2.0to@v1.5.0(adds the HTTPS Binary server on:4852, the Security Key Service on:4851, ECC NIST / Brainpool servers on:4848/:4849, and the open62541-backedhistorizingserver on:24842that the new HistoryUpdate integration tests target). composer.jsonsupport.docsnow points at the canonical docs site (https://www.php-opcua.com/documentation/opcua-cli) instead of the GitHubtree/master/docURL.
Compatibility
- No CLI source change was required: every command consumes
ClientBuilder,OpcUaClientInterface, and the sharedTypes\*DTOs, none of which had breaking changes in v4.4. tests/Integration/benefits transparently from the bumped test-suite (all 12 servers available; existing integration tests still run against the sameopcua-no-securitybaseline server on:4840).
[4.3.0] - 2026-04-24
Security
- Fixed PHP code injection in
generate:nodesetvia unescapedNodeId/encodingId/RequiredModel.ModelUri(src/CodeGenerator.php,src/Commands/GenerateNodesetCommand.php). - Fixed path traversal in
generate:nodesetvia unsanitized enumNameattribute. - Hardened XML parsing with
LIBXML_NONETinNodeSetParser. - 10 regression tests added in
tests/Unit/SecurityTest.phpwith 5 malicious fixture XMLs.
Changed
- Bumped
php-opcua/opcua-clientfrom^4.2.0to^4.3.0. - Bumped
Application::VERSIONto4.3.0. - Bumped CI test-server suite from
uanetstandard-test-suite@v1.1.0to@v1.2.0. - Clearer error message for failed
--debug-fileopen and malformed NodeSet XML, via a new\RuntimeExceptionhandler inApplication::run().
Added
- Integration-test readiness probe (
tests/Integration/Helpers/Readiness.php) to fix flaky first-test-after-container-boot on PHP 8.3 / 8.5 runners. - Standalone binary releases for
linux-x86_64,linux-aarch64,macos-arm64, andwindows-x86_64(experimental), produced on tag push by.github/workflows/release-binaries.ymlviastatic-php-cli+ Box. See README anddoc/04-build-from-source.md. - New
explorecommand — interactive TUI browser of the server address space (tree + details + log panes), built onphp-tui/php-tui. Linux/macOS only; Windows prints a clear "not yet supported" error (upstreamphp-tuidoes not yet support Windows).
[4.2.0] - 2026-04-17
Changed
- Bumped
php-opcua/opcua-clientdependency from^4.1to^4.2.0. The CLI is aligned with theopcua-clientv4.2.0 release which introduces the Kernel + ServiceModule architecture (internal refactor; public API unchanged),ClientBuilder::addModule()/replaceModule(), and the new server BuildInfo convenience methods onOpcUaClientInterface. No CLI source change was required: all commands consumeClientBuilder,OpcUaClientInterface, and theTypes\DTOs, none of which had breaking changes. - Fixed
Application::VERSION— was frozen at1.0.0since the v4.0.0 extraction fromopcua-client.opcua-cli --versionnow reports the actual package version (4.2.0) and will stay aligned with theopcua-clientrelease it bundles, per the versioning note at the top ofROADMAP.md. - CI workflow aligned with
opcua-client..github/workflows/tests.ymlnow splitsunitandintegrationjobs: unit tests run cross-OS onubuntu-latest,macos-latest, andwindows-latest× PHP 8.2–8.5 (12 combinations, 258 tests each), integration tests run Ubuntu-only againstphp-opcua/uanetstandard-test-suite@v1.1.0withneeds: unitgating × PHP 8.2–8.5 (4 combinations).[DOC]-prefixed commits skip CI on both jobs. Code-style check (composer format:check) runs once on Ubuntu/PHP 8.5 instead of every matrix slot. Triggers expanded from[master]to[main, master].codecov/codecov-actionbumped fromv5tov6to silence Node.js 20 deprecation warnings on GitHub Actions runners.
Fixed
watch(polling and subscription) and any read/write against NodeIds whose string identifier contains/. The previousopcua-clientv4.2.0 shipped with an overly permissive heuristic inClient::resolveNodeId()that routed every/-bearing string throughTranslateBrowsePathModule, so real NodeIds such asns=1;s=TestServer/Dynamic/Counter(routinely exposed by UA-.NETStandard-based servers) failed withServiceException: 0x806F0000 (BadNotFound). Fixed upstream inopcua-clientv4.2.0; the CLI picks up the fix via the^4.2.0constraint. Two integration tests intests/Integration/CliTest.php(watches Counter node with polling modeandwrites a value and watch CLI detects it via polling) regained green status with no code change on the CLI side.- Windows compatibility for the output classes.
ConsoleOutput::writeln()/error(), everyJsonOutputwriter, andStreamLogger::log()now emit a literal"\n"line separator instead ofPHP_EOL. On WindowsPHP_EOLexpands to"\r\n", which broke every byte-exact assertion on CLI output ("Hello\n"vs"Hello\r\n"— "Strings contain different line endings") and produced\r\n-terminated lines in piped/redirected output that downstream tools (jq,grep, JSON NDJSON parsers, shell redirection into files) would not handle cleanly. Converging on\nalso matches the convention of every other mainstream CLI (git,node,python, Unix coreutils) on Windows, where the Console subsystem renders\ncorrectly without needing CRLF at the source.tests/Unit/OutputTest.phpalso opens scratch streams in binary mode ('w+b') so that Windows text-modefopen()does not silently re-introduce the\n→\r\ntranslation on the round-trip through the temp file. Only the dedicated non-memory fallback test (it falls back to TERM env when posix_isatty not available on non-memory stream) still uses default text mode because it never reads back its contents.
[4.1.0] - 2026-04-13
Added
- ECC security policy support. All 10 CLI commands now work transparently with the 4 new Elliptic Curve Cryptography policies introduced in
opcua-clientv4.1.0:--security-policy=ECC_nistP256(NIST P-256, AES-128-CBC, SHA-256)--security-policy=ECC_nistP384(NIST P-384, AES-256-CBC, SHA-384)--security-policy=ECC_brainpoolP256r1(Brainpool P-256, AES-128-CBC, SHA-256)--security-policy=ECC_brainpoolP384r1(Brainpool P-384, AES-256-CBC, SHA-384)- No
--cert/--keyrequired — ECC certificates are auto-generated when omitted. - Username/password authentication uses the
EccEncryptedSecretprotocol automatically. - ECC disclaimer: No commercial OPC UA vendor supports ECC endpoints yet. This implementation is tested exclusively against the OPC Foundation's UA-.NETStandard reference stack.
- 12 new ECC integration tests against the
uanetstandard-test-suiteECC servers:- 6 NIST ECC tests (port 4848): browse and read with P-256 Sign, P-256 SignAndEncrypt (anonymous + admin), P-384 SignAndEncrypt (anonymous + admin), P-384 Sign.
- 6 Brainpool ECC tests (port 4849): browse and read with brainpoolP256r1 Sign, brainpoolP256r1 SignAndEncrypt (anonymous + admin), brainpoolP384r1 SignAndEncrypt (anonymous + admin), brainpoolP384r1 Sign.
- 4 new unit tests for ECC security policy resolution in
CommandRunner(short names and full URIs for all 4 ECC policies).
Changed
- Bumped minimum
php-opcua/opcua-clientdependency from^4.0to^4.1. - Security support expanded from 6 to 10 policies (6 RSA + 4 ECC).
- Updated documentation (README, doc/, llms.txt, llms-full.txt, llms-skills.md) to reflect ECC support, add ECC examples, and include the ECC disclaimer.
- Updated CI test server suite from
php-opcua/uanetstandard-test-suite@v1.0.0to@v1.1.0.
[4.0.2] - 2026-04-07
Added
- AI-Ready documentation. Added
llms-skills.mdwith 11 task-oriented recipes for AI coding assistants (browse, read, write, watch, security, trust management, code generation, address space export, JSON scripting, endpoint discovery, global options). Designed to be fed to Claude, Cursor, Copilot, ChatGPT, and other AI tools so they can generate correct CLI commands from a user's intent. - Added AI-Ready section to README with instructions for integrating with Claude Code, Cursor, GitHub Copilot, and other AI tools.
[4.0.1] - 2026-04-02
Changed
- Migrated test infrastructure from
opcua-test-suitetouanetstandard-test-suite. Integration tests now run against the OPC Foundation's UA-.NETStandard reference implementation instead of node-opcua. - Updated GitHub Actions workflow to use
php-opcua/uanetstandard-test-suite@v1.0.0.
Fixed
- Fixed
trustCLI integration test — the no-security server (.NET) correctly does not expose a certificate withSecurityPolicy=None. Test now uses the all-security server (port 4843).
[4.0.0] - 2026-03-29
Added
- Extracted CLI tool from php-opcua/opcua-client into a standalone package.
- 10 commands:
browse,read,write,endpoints,watch,generate:nodeset,dump:nodeset,trust,trust:list,trust:remove. - Full security support (6 RSA policies, 3 auth modes), JSON output, debug logging.
- NodeSet2.xml code generator: typed DTOs, PHP enums, binary codecs, registrar with dependency resolution.
- Server address space dump to NodeSet2.xml.
- Server certificate trust management from the terminal.
- 272 tests (253 unit + 19 integration), 592 assertions, 99.9% code coverage.