From 921b759d55d9da3c6014ce460b15905debfce1b8 Mon Sep 17 00:00:00 2001 From: Eser DENIZ Date: Mon, 18 May 2026 22:12:36 +0200 Subject: [PATCH 1/2] fix(windows): replace removed wmic with PowerShell Get-CimInstance wmic was removed in Windows 11 24H2 / Server 2025 (the image GitHub's windows-latest runner now uses), so the constructor crashed with "Undefined array key 'Caption'" before any test could run. Swap to Get-CimInstance Win32_OperatingSystem (same data, supported successor) and emit the same Key=Value lines the existing parser expects. Also null-coalesce the Caption lookup so a missing key degrades to "not a Server" instead of throwing. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/OperatingSystems/Windows.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/OperatingSystems/Windows.php b/src/OperatingSystems/Windows.php index d80da70..98b40ee 100644 --- a/src/OperatingSystems/Windows.php +++ b/src/OperatingSystems/Windows.php @@ -25,10 +25,11 @@ public function __construct() private function retrieveFromSwVers(): void { - $result = Shell::exec('wmic os get Caption, Version, BuildNumber /format:list'); - // BuildNumber=22631 - // Caption=Microsoft Windows 11 Famille + // wmic was removed in Windows 11 24H2 / Server 2025; use CIM via PowerShell instead. + $result = Shell::exec('powershell -NoProfile -Command "Get-CimInstance Win32_OperatingSystem | ForEach-Object { \'Caption=\'+$_.Caption; \'Version=\'+$_.Version; \'BuildNumber=\'+$_.BuildNumber }"'); + // Caption=Microsoft Windows 11 Pro // Version=10.0.22631 + // BuildNumber=22631 $lines = explode(PHP_EOL, $result); @@ -41,7 +42,7 @@ private function retrieveFromSwVers(): void $this->cached_name = 'Windows'; - if (str_contains($infos['Caption'], 'Server')) { + if (str_contains($infos['Caption'] ?? '', 'Server')) { // Server 2022 $this->cached_edition = implode(' ', array_slice(explode(' ', $infos['Caption']), 2)); $this->cached_version = $this->getWindowsVersion($infos['Version'] ?? ''); From a42befce71aff6121065e1d9ca06ba3e1ae66627 Mon Sep 17 00:00:00 2001 From: Eser DENIZ Date: Mon, 18 May 2026 22:18:25 +0200 Subject: [PATCH 2/2] fix(windows): use EncodedCommand and stop crashing on empty Version MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two issues survived the first wmic→PowerShell swap on windows-latest: 1. Win32_OperatingSystem.Version came through empty on the new Server image, so getWindowsVersion('') threw InvalidArgumentException from the constructor. Read the version from [Environment]::OSVersion instead, which is always populated. 2. -Command with single quotes inside a cmd.exe double-quoted argument is fragile. Switch to -EncodedCommand (UTF-16LE base64) so the shell never has to interpret the script, which removes a whole class of future quoting regressions. Also soften getWindowsVersion to return null on malformed input — the constructor calls it unconditionally, so a throw there propagates as a hard crash in any consumer that just wants to print system info. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/OperatingSystems/Windows.php | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/OperatingSystems/Windows.php b/src/OperatingSystems/Windows.php index 98b40ee..b1ff4bf 100644 --- a/src/OperatingSystems/Windows.php +++ b/src/OperatingSystems/Windows.php @@ -3,7 +3,6 @@ namespace KnotsPHP\System\OperatingSystems; use KnotsPHP\System\Contracts\OperatingSystemContract; -use KnotsPHP\System\Exceptions\InvalidArgumentException; use KnotsPHP\System\Helpers\Shell; final class Windows implements OperatingSystemContract @@ -25,8 +24,14 @@ public function __construct() private function retrieveFromSwVers(): void { - // wmic was removed in Windows 11 24H2 / Server 2025; use CIM via PowerShell instead. - $result = Shell::exec('powershell -NoProfile -Command "Get-CimInstance Win32_OperatingSystem | ForEach-Object { \'Caption=\'+$_.Caption; \'Version=\'+$_.Version; \'BuildNumber=\'+$_.BuildNumber }"'); + // wmic was removed in Windows 11 24H2 / Server 2025; use PowerShell instead. + // -EncodedCommand sidesteps cmd.exe nested-quote issues. Decoded script: + // $o=Get-CimInstance Win32_OperatingSystem; + // $v=[Environment]::OSVersion.Version; + // 'Caption='+$o.Caption; + // 'Version='+$v.Major+'.'+$v.Minor+'.'+$v.Build; + // 'BuildNumber='+$v.Build + $result = Shell::exec('powershell -NoProfile -EncodedCommand JABvAD0ARwBlAHQALQBDAGkAbQBJAG4AcwB0AGEAbgBjAGUAIABXAGkAbgAzADIAXwBPAHAAZQByAGEAdABpAG4AZwBTAHkAcwB0AGUAbQA7ACQAdgA9AFsARQBuAHYAaQByAG8AbgBtAGUAbgB0AF0AOgA6AE8AUwBWAGUAcgBzAGkAbwBuAC4AVgBlAHIAcwBpAG8AbgA7ACcAQwBhAHAAdABpAG8AbgA9ACcAKwAkAG8ALgBDAGEAcAB0AGkAbwBuADsAJwBWAGUAcgBzAGkAbwBuAD0AJwArACQAdgAuAE0AYQBqAG8AcgArACcALgAnACsAJAB2AC4ATQBpAG4AbwByACsAJwAuACcAKwAkAHYALgBCAHUAaQBsAGQAOwAnAEIAdQBpAGwAZABOAHUAbQBiAGUAcgA9ACcAKwAkAHYALgBCAHUAaQBsAGQA'); // Caption=Microsoft Windows 11 Pro // Version=10.0.22631 // BuildNumber=22631 @@ -94,9 +99,9 @@ public function getWindowsVersion(string $versionString): ?string // 10.0.22631 / 10.0.20348 $versionParts = explode('.', $versionString); - // Check if the version string is valid + // Bail out gracefully on malformed input rather than crashing the constructor. if (count($versionParts) < 2) { - throw new InvalidArgumentException('Invalid Windows version string: '.$versionString); + return null; } // Extract major and minor version numbers