Windows 通過 WMI (Windows Management Instrumentation) 暴露系統管理數據,PHP 可透過 COM 組件調用 WMI 查詢語言 (WQL) 獲取豐富指標。
<?php
/**
* Windows 系統信息採集器 (基於 WMI)
* 需啟用 php_com_dotnet 擴展,且運行用戶有 WMI 查詢權限
*/
class WindowsSysInfo {
private $wmi;
public function __construct() {
if (!class_exists('COM')) {
throw new Exception('COM 擴展未啟用,請在 php.ini 中開啟 extension=com_dotnet');
}
try {
$locator = new COM('WbemScripting.SWbemLocator');
$this->wmi = $locator->ConnectServer('.', 'root\\cimv2');
} catch (com_exception $e) {
throw new Exception('WMI 連接失敗:' . $e->getMessage());
}
}
public function get(): array {
return [
'cpu' => $this->getCpuInfo(),
'memory' => $this->getMemoryInfo(),
'swap' => $this->getSwapInfo(),
'disk' => $this->getDiskInfo(),
'os' => $this->getOsInfo(),
'uptime' => $this->getUptime(),
];
}
/** 通用 WQL 查詢封裝 */
private function query(string $wql, array $properties = []): array {
$results = [];
try {
$objSet = $this->wmi->ExecQuery($wql);
foreach ($objSet as $obj) {
$item = [];
if (empty($properties)) {
// 獲取所有屬性
foreach ($obj->Properties_ as $prop) {
$item[$prop->Name] = $prop->Value;
}
} else {
foreach ($properties as $prop) {
$item[$prop] = $obj->$prop ?? null;
}
}
$results[] = $item;
}
} catch (com_exception $e) {
// 忽略單個查詢失敗
}
return $results;
}
private function getCpuInfo(): array {
$data = $this->query(
'SELECT Name, NumberOfCores, NumberOfLogicalProcessors, L2CacheSize, MaxClockSpeed FROM Win32_Processor',
['Name', 'NumberOfCores', 'NumberOfLogicalProcessors', 'L2CacheSize', 'MaxClockSpeed']
);
$cpu = ['cores' => 0, 'logical' => 0, 'model' => '', 'cache_mb' => 0, 'max_mhz' => 0];
foreach ($data as $proc) {
$cpu['cores'] += $proc['NumberOfCores'] ?? 0;
$cpu['logical'] += $proc['NumberOfLogicalProcessors'] ?? 0;
$cpu['model'] = $proc['Name'] ?? 'Unknown';
$cpu['cache_mb'] = round(($proc['L2CacheSize'] ?? 0) / 1024, 2); // KB -> MB
$cpu['max_mhz'] = $proc['MaxClockSpeed'] ?? 0;
}
return $cpu;
}
private function getMemoryInfo(): array {
$data = $this->query(
'SELECT TotalVisibleMemorySize, FreePhysicalMemory FROM Win32_OperatingSystem',
['TotalVisibleMemorySize', 'FreePhysicalMemory']
);
$mem = ['total_mb' => 0, 'free_mb' => 0, 'used_mb' => 0, 'percent' => 0];
if ($data) {
$totalKb = $data[0]['TotalVisibleMemorySize'] ?? 0;
$freeKb = $data[0]['FreePhysicalMemory'] ?? 0;
$mem['total_mb'] = round($totalKb / 1024, 2);
$mem['free_mb'] = round($freeKb / 1024, 2);
$mem['used_mb'] = $mem['total_mb'] - $mem['free_mb'];
$mem['percent'] = $mem['total_mb'] > 0 ? round($mem['used_mb'] / $mem['total_mb'] * 100, 2) : 0;
}
return $mem;
}
private function getSwapInfo(): array {
$data = $this->query(
'SELECT AllocatedBaseSize, CurrentUsage FROM Win32_PageFileUsage',
['AllocatedBaseSize', 'CurrentUsage']
);
$swap = ['total_mb' => 0, 'used_mb' => 0, 'free_mb' => 0, 'percent' => 0];
foreach ($data as $pf) {
$swap['total_mb'] += round(($pf['AllocatedBaseSize'] ?? 0), 2); // 已是 MB
$swap['used_mb'] += round(($pf['CurrentUsage'] ?? 0), 2);
}
$swap['free_mb'] = $swap['total_mb'] - $swap['used_mb'];
$swap['percent'] = $swap['total_mb'] > 0 ? round($swap['used_mb'] / $swap['total_mb'] * 100, 2) : 0;
return $swap;
}
private function getDiskInfo(): array {
$data = $this->query(
'SELECT DeviceID, Size, FreeSpace, VolumeName FROM Win32_LogicalDisk WHERE DriveType = 3',
['DeviceID', 'Size', 'FreeSpace', 'VolumeName']
);
$disks = [];
foreach ($data as $disk) {
$size = $disk['Size'] ?? 0;
$free = $disk['FreeSpace'] ?? 0;
$disks[] = [
'drive' => $disk['DeviceID'],
'label' => $disk['VolumeName'] ?? '',
'total_gb' => round($size / 1024 / 1024 / 1024, 2),
'free_gb' => round($free / 1024 / 1024 / 1024, 2),
'used_gb' => round(($size - $free) / 1024 / 1024 / 1024, 2),
'percent' => $size > 0 ? round(($size - $free) / $size * 100, 2) : 0,
];
}
return $disks;
}
private function getOsInfo(): array {
$data = $this->query(
'SELECT Caption, Version, BuildNumber, CSDVersion, InstallDate, SerialNumber FROM Win32_OperatingSystem',
['Caption', 'Version', 'BuildNumber', 'CSDVersion', 'InstallDate', 'SerialNumber']
);
if (!$data) return ['error' => '無法獲取系統信息'];
$os = $data[0];
// InstallDate 格式:20240115083000.000000+480
$installTime = 0;
if (!empty($os['InstallDate'])) {
$dt = $os['InstallDate'];
$installTime = strtotime(substr($dt, 0, 14));
}
return [
'name' => $os['Caption'] ?? 'Windows',
'version' => $os['Version'] ?? '',
'build' => $os['BuildNumber'] ?? '',
'sp' => $os['CSDVersion'] ?? '',
'install_date' => $installTime ? date('Y-m-d H:i:s', $installTime) : 'Unknown',
'serial' => $os['SerialNumber'] ?? '',
];
}
private function getUptime(): array {
$data = $this->query(
'SELECT LastBootUpTime FROM Win32_OperatingSystem',
['LastBootUpTime']
);
$uptime = ['seconds' => 0, 'human' => 'Unknown', 'boot_time' => 0];
if ($data && !empty($data[0]['LastBootUpTime'])) {
// 格式:20240115083000.000000+480
$dt = $data[0]['LastBootUpTime'];
$bootTime = strtotime(substr($dt, 0, 14));
$now = time();
$seconds = $now - $bootTime;
$days = floor($seconds / 86400);
$hours = floor(($seconds % 86400) / 3600);
$minutes = floor(($seconds % 3600) / 60);
$uptime = [
'seconds' => $seconds,
'human' => sprintf('%d天 %d小時 %d分鐘', $days, $hours, $minutes),
'boot_time' => $bootTime,
];
}
return $uptime;
}
}
// 使用示例
try {
$sys = new WindowsSysInfo();
$info = $sys->get();
echo '<pre>';
print_r($info);
echo '</pre>';
} catch (Exception $e) {
echo '錯誤:' . $e->getMessage();
}
?>
Array
(
[cpu] => Array
(
[cores] => 4
[logical] => 8
[model] => Intel(R) Core(TM) i7-8565U CPU @ 1.80GHz
[cache_mb] => 8.00
[max_mhz] => 1992
)
[memory] => Array
(
[total_mb] => 16284.00
[free_mb] => 4521.00
[used_mb] => 11763.00
[percent] => 72.24
)
[swap] => Array
(
[total_mb] => 4096.00
[used_mb] => 1024.00
[free_mb] => 3072.00
[percent] => 25.00
)
[disk] => Array
(
[0] => Array
(
[drive] => C:
[label] => Windows
[total_gb] => 476.94
[free_gb] => 123.45
[used_gb] => 353.49
[percent] => 74.12
)
)
[os] => Array
(
[name] => Microsoft Windows 10 Pro
[version] => 10.0.19045
[build] => 19045
[sp] =>
[install_date] => 2023-05-12 14:30:00
[serial] => 00330-XXXXX-XXXXX-XXXXX
)
[uptime] => Array
(
[seconds] => 123456
[human] => 1天 10小時 17分鐘
[boot_time] => 1705000000
)
)
php.ini;extension=com_dotnet,去掉分號wmimgmt.msc) 授予 root\cimv2 命名空間的「啟用帳號」和「遠程啟用」權限。interface SysInfoProvider {
public function get(): array;
}
class SysInfoFactory {
public static function create(): ?SysInfoProvider {
if (PHP_OS_FAMILY === 'Linux') return new LinuxSysInfo();
if (stripos(PHP_OS, 'FreeBSD') !== false) return new FreeBsdSysInfo();
if (PHP_OS_FAMILY === 'Windows') return new WindowsSysInfo();
return null;
}
}
// 使用
$provider = SysInfoFactory::create();
if ($provider) {
$info = $provider->get();
// 統一處理顯示邏輯
}