diff --git a/CHANGELOG.md b/CHANGELOG.md index 1873347..356714c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Release Notes +v3.2.3 (2026-10-12) +------------------- + +User-Agent identifies the PHP agent (v3.2.3) +-------------------------------------------- + +### Changed + +- When `guardCoreVersion` is not set, the agent fills it from Composer's runtime API if `rennf93/guard-core-php` is installed (leading `v` stripped); otherwise it stays null. A value set by the caller wins. +- The ingestion `User-Agent` header is now `guard-agent-php/` (was `guard-agent/`, identical to the Python agent), so the Guard SaaS can tell the PHP agent apart from the Python one. Lockstep release with the agent family at 3.2.3. + +___ + v3.2.2 (2026-10-09) ------------------- diff --git a/bin/test_agent.php b/bin/test_agent.php index 3c437c2..bd704de 100644 --- a/bin/test_agent.php +++ b/bin/test_agent.php @@ -1010,6 +1010,14 @@ function assertStaticOnlyClass(T $t, string $class): void // ===================================================================== // 2. Wire models // ===================================================================== +$t->section('guard core version'); +$t->same('4.3.3', AgentConfigResolver::bareVersion('v4.3.3'), 'guard core version: leading v stripped'); +$rootPackage = \Composer\InstalledVersions::getRootPackage()['name']; +$t->same(ltrim(\Composer\InstalledVersions::getPrettyVersion($rootPackage) ?? '', 'vV'), AgentConfigResolver::installedGuardCoreVersion($rootPackage), 'guard core version: an installed package resolves from Composer'); +$t->same(null, AgentConfigResolver::bareVersion(null), 'guard core version: null stays null'); +$t->same(null, AgentConfigResolver::resolve(['apiKey' => 'test-api-key-123'])->guardCoreVersion, 'guard core version: null when guard-core-php is not installed'); +$t->same('9.9.9', AgentConfigResolver::resolve(['apiKey' => 'test-api-key-123', 'guardCoreVersion' => '9.9.9'])->guardCoreVersion, 'guard core version: a user-set value wins'); + $t->section('models'); $event = makeEvent([ @@ -1698,6 +1706,7 @@ function assertStaticOnlyClass(T $t, string $class): void $t->same('proj-smoke', $record['x_project_id'] ?? null, 'smoke: X-Project-Id header sent'); $t->same('install-smoke-1', $record['x_agent_install_id'] ?? null, 'smoke: X-Agent-Install-Id header sent'); $t->same(Version::USER_AGENT, $record['user_agent'] ?? null, 'smoke: User-Agent header sent'); +$t->same('guard-agent-php/' . Version::VERSION, $record['user_agent'] ?? null, 'smoke: User-Agent names the PHP port'); // The mock runs with requireSigned=true, so a signature computed over the gzip // bytes would have been rejected with 401; 200 proves the HMAC covers the diff --git a/src/Config/AgentConfigResolver.php b/src/Config/AgentConfigResolver.php index bbbb110..99fe79b 100644 --- a/src/Config/AgentConfigResolver.php +++ b/src/Config/AgentConfigResolver.php @@ -23,6 +23,25 @@ private function __construct() { } + public static function installedGuardCoreVersion(string $package = 'rennf93/guard-core-php'): ?string + { + if (!class_exists(\Composer\InstalledVersions::class) + || !\Composer\InstalledVersions::isInstalled($package)) { + return null; + } + + return self::bareVersion(\Composer\InstalledVersions::getPrettyVersion($package)); + } + + public static function bareVersion(?string $prettyVersion): ?string + { + if ($prettyVersion === null || $prettyVersion === '') { + return null; + } + + return ltrim($prettyVersion, 'vV'); + } + /** * @param array $input * @@ -83,7 +102,8 @@ public static function resolve(array $input): AgentConfig ?? AgentConfig::DEFAULT_SENSITIVE_HEADERS, maxPayloadSize: self::intInput($input, 'maxPayloadSize', 'max_payload_size', 1024), guardVersion: self::optionalStringInput($input, 'guardVersion', 'guard_version'), - guardCoreVersion: self::optionalStringInput($input, 'guardCoreVersion', 'guard_core_version'), + guardCoreVersion: self::optionalStringInput($input, 'guardCoreVersion', 'guard_core_version') + ?? self::installedGuardCoreVersion(), compressionEnabled: self::boolInput($input, 'compressionEnabled', 'compression_enabled', true), compressionThreshold: self::intInput($input, 'compressionThreshold', 'compression_threshold', 1024), installId: self::optionalStringInput($input, 'installId', 'install_id'), diff --git a/src/Version.php b/src/Version.php index 5a3d7cb..b028d2f 100644 --- a/src/Version.php +++ b/src/Version.php @@ -11,8 +11,8 @@ */ final class Version { - public const VERSION = '3.2.2'; + public const VERSION = '3.2.3'; - /** User-Agent value sent with every request (mirrors guard-agent). */ - public const USER_AGENT = 'guard-agent/' . self::VERSION; + /** User-Agent value sent with every request (identifies the PHP port). */ + public const USER_AGENT = 'guard-agent-php/' . self::VERSION; }