query($query);
if (!$result) {
return 0;
}
$row = mysqli_fetch_assoc($result);
return (int)($row[$field] ?? 0);
}
function fetchInfraRows($conn, $query) {
$rows = [];
if ($conn instanceof PDO) {
$stmt = $conn->query($query);
return $stmt ? $stmt->fetchAll(PDO::FETCH_ASSOC) : [];
}
if (function_exists('sqlsrv_query')) {
$stmt = @sqlsrv_query($conn, $query);
if ($stmt !== false) {
while ($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
$rows[] = $row;
}
return $rows;
}
}
if (function_exists('odbc_exec')) {
$stmt = @odbc_exec($conn, $query);
if ($stmt !== false) {
while ($row = odbc_fetch_array($stmt)) {
$rows[] = $row;
}
return $rows;
}
}
return $rows;
}
function normalizeDateValue($value) {
if ($value instanceof DateTimeInterface) {
return $value->format('Y-m-d');
}
if ($value === null || trim((string)$value) === '') {
return null;
}
return substr((string)$value, 0, 10);
}
function normalizeOsKey($os) {
$os = trim((string)$os);
if ($os === '') {
return 'Unknown';
}
// Windows Server / Client
if (preg_match('/Windows/i', $os)) {
// Server versions
if (preg_match('/2003/', $os)) return 'Windows 2003';
if (preg_match('/2008/', $os)) return 'Windows 2008';
if (preg_match('/2012/', $os)) return 'Windows 2012';
if (preg_match('/2016/', $os)) return 'Windows 2016';
if (preg_match('/2019/', $os)) return 'Windows 2019';
if (preg_match('/2022/', $os)) return 'Windows 2022';
if (preg_match('/2025/', $os)) return 'Windows 2025';
// Clients
if (preg_match('/Windows\s*10/i', $os)) return 'Windows 10';
if (preg_match('/Windows\s*11/i', $os)) return 'Windows 11';
return 'Windows';
}
if (preg_match('/Windows\s+(10|11)/i', $os, $match)) {
return 'Windows ' . $match[1];
}
// RedHat / RHEL
// Examples handled:
// Linux / RedHat 8.8-0.8 -> RedHat 8.8
// Linux / RedHat 8-8.0-8 -> RedHat 8.8
// Red Hat Enterprise Linux 8.10 -> RedHat 8.10
if (preg_match('/Red\s*Hat|RedHat|RHEL/i', $os)) {
if (preg_match('/Red\s*Hat\s+(\d+)[\.\-](\d+)/i', $os, $match)) {
return 'RedHat ' . $match[1] . '.' . $match[2];
}
if (preg_match('/RedHat\s+(\d+)[\.\-](\d+)/i', $os, $match)) {
return 'RedHat ' . $match[1] . '.' . $match[2];
}
if (preg_match('/RHEL\s+(\d+)[\.\-](\d+)/i', $os, $match)) {
return 'RedHat ' . $match[1] . '.' . $match[2];
}
if (preg_match('/release\s+(\d+)[\.\-](\d+)/i', $os, $match)) {
return 'RedHat ' . $match[1] . '.' . $match[2];
}
if (preg_match('/(\d+)[\.\-](\d+)/', $os, $match)) {
return 'RedHat ' . $match[1] . '.' . $match[2];
}
if (preg_match('/(\d+)/', $os, $match)) {
return 'RedHat ' . $match[1];
}
return 'RedHat';
}
// AlmaLinux
if (preg_match('/AlmaLinux/i', $os)) {
if (preg_match('/AlmaLinux\s+(\d+)[\.\-](\d+)/i', $os, $match)) {
return 'AlmaLinux ' . $match[1] . '.' . $match[2];
}
if (preg_match('/release\s+(\d+)[\.\-](\d+)/i', $os, $match)) {
return 'AlmaLinux ' . $match[1] . '.' . $match[2];
}
if (preg_match('/(\d+)[\.\-](\d+)/', $os, $match)) {
return 'AlmaLinux ' . $match[1] . '.' . $match[2];
}
if (preg_match('/(\d+)/', $os, $match)) {
return 'AlmaLinux ' . $match[1];
}
return 'AlmaLinux';
}
// Rocky Linux
if (preg_match('/Rocky/i', $os)) {
if (preg_match('/(\d+)[\.\-](\d+)/', $os, $match)) {
return 'Rocky Linux ' . $match[1] . '.' . $match[2];
}
if (preg_match('/(\d+)/', $os, $match)) {
return 'Rocky Linux ' . $match[1];
}
return 'Rocky Linux';
}
// CentOS
if (preg_match('/CentOS/i', $os)) {
if (preg_match('/(\d+)[\.\-](\d+)/', $os, $match)) {
return 'CentOS ' . $match[1] . '.' . $match[2];
}
if (preg_match('/(\d+)/', $os, $match)) {
return 'CentOS ' . $match[1];
}
return 'CentOS';
}
// Ubuntu
if (preg_match('/Ubuntu/i', $os)) {
if (preg_match('/(\d+)\.(\d+)/', $os, $match)) {
return 'Ubuntu ' . $match[1] . '.' . $match[2];
}
if (preg_match('/(\d+)/', $os, $match)) {
return 'Ubuntu ' . $match[1];
}
return 'Ubuntu';
}
// Debian
if (preg_match('/Debian/i', $os)) {
if (preg_match('/(\d+)\.(\d+)/', $os, $match)) {
return 'Debian ' . $match[1] . '.' . $match[2];
}
if (preg_match('/(\d+)/', $os, $match)) {
return 'Debian ' . $match[1];
}
return 'Debian';
}
// SUSE / SLES
if (preg_match('/SUSE|SLES/i', $os)) {
if (preg_match('/(\d+).*?(?:SP\s*|\.)(\d+)/i', $os, $match)) {
return 'SLES ' . $match[1] . '.' . $match[2];
}
if (preg_match('/(\d+)/', $os, $match)) {
return 'SLES ' . $match[1];
}
return 'SLES';
}
// Oracle Linux
if (preg_match('/Oracle Linux/i', $os)) {
if (preg_match('/(\d+)\.(\d+)/', $os, $match)) {
return 'Oracle Linux ' . $match[1] . '.' . $match[2];
}
if (preg_match('/(\d+)/', $os, $match)) {
return 'Oracle Linux ' . $match[1];
}
return 'Oracle Linux';
}
// AIX
if (preg_match('/AIX\s+6100/i', $os)) {
return 'AIX <7.2';
}
if (preg_match('/AIX\s+7100/i', $os)) {
return 'AIX <7.2';
}
if (preg_match('/AIX\s+7200/i', $os)) {
return 'AIX 7.2';
}
if (preg_match('/AIX\s+7300/i', $os)) {
return 'AIX 7.3';
}
if (preg_match('/AIX/i', $os)) {
return 'AIX <7.2';
}
if (preg_match('/Unix/i', $os)) {
return 'Unix';
}
return $os;
}
function isWindowsOs($os) {
return preg_match('/Windows/i', (string)$os) === 1;
}
function isAixOs($os) {
return preg_match('/AIX/i', (string)$os) === 1;
}
function isLinuxOs($os) {
$os = trim((string)$os);
if ($os === '') {
return false;
}
if (isWindowsOs($os) || isAixOs($os)) {
return false;
}
if (preg_match('/Unix/i', $os)) {
return false;
}
return preg_match('/Linux|Red Hat|RedHat|RHEL|AlmaLinux|Rocky|CentOS|Ubuntu|Debian|SUSE|SLES|Oracle|Data Domain OS|/i', $os) === 1;
}
function hasS1Agent($value) {
$value = trim((string)$value);
if ($value === '') {
return false;
}
if (in_array($value, ['N', 'Non supported OS'], true)) {
return false;
}
return true;
}
function incrementCounter(&$array, $key) {
if (!isset($array[$key])) {
$array[$key] = 0;
}
$array[$key]++;
}
function getLifecycleStatus($osKey, $lifecycleMap) {
$match = null;
$bestLength = 0;
if (isset($lifecycleMap[$osKey])) {
$match = $lifecycleMap[$osKey];
} else {
foreach ($lifecycleMap as $key => $data) {
if (stripos($osKey, $key) === 0 && strlen($key) > $bestLength) {
$match = $data;
$bestLength = strlen($key);
}
}
}
if (!$match || empty($match['SupportEndDate'])) {
return [
'label' => 'Unknown',
'color' => '#6c757d',
'end' => null
];
}
$today = new DateTimeImmutable('today');
$warningLimit = $today->modify('+1 year');
$supportEndDate = new DateTimeImmutable($match['SupportEndDate']);
if ($supportEndDate < $today) {
return [
'label' => 'Expired',
'color' => '#dc3545',
'end' => $supportEndDate->format('Y-m-d')
];
}
if ($supportEndDate < $warningLimit) {
return [
'label' => 'Warning',
'color' => '#fd7e14',
'end' => $supportEndDate->format('Y-m-d')
];
}
return [
'label' => 'Supported',
'color' => '#198754',
'end' => $supportEndDate->format('Y-m-d')
];
}
function sortOsCounter(&$counter) {
uksort($counter, function($a, $b) {
return strnatcasecmp($a, $b);
});
}
function chartFromCounter($counter, $lifecycleMap = null, $limit = null, $groupOthers = false) {
uksort($counter, function($a, $b) use ($lifecycleMap) {
$statusA = $lifecycleMap ? getLifecycleStatus($a, $lifecycleMap) : null;
$statusB = $lifecycleMap ? getLifecycleStatus($b, $lifecycleMap) : null;
$dateA = ($statusA && !empty($statusA['end'])) ? $statusA['end'] : '0000-00-00';
$dateB = ($statusB && !empty($statusB['end'])) ? $statusB['end'] : '0000-00-00';
return strcmp($dateB, $dateA); // décroissant
});
if ($limit !== null && count($counter) > $limit) {
$limited = array_slice($counter, 0, $limit, true);
$remaining = array_slice($counter, $limit, null, true);
if ($groupOthers && count($remaining) > 0) {
$limited['Others'] = array_sum($remaining);
}
$counter = $limited;
}
$labels = [];
$data = [];
$colors = [];
$palette = [
'#198754',
'#fd7e14',
'#0d6efd',
'#6f42c1',
'#20c997',
'#dc3545',
'#6c757d',
'#0dcaf0',
'#6610f2',
'#d63384',
'#2f855a',
'#495057'
];
$i = 0;
foreach ($counter as $key => $value) {
$labels[] = $key;
$data[] = $value;
if ($lifecycleMap !== null && $key !== 'Others') {
$colors[] = getLifecycleStatus($key, $lifecycleMap)['color'];
} else {
$colors[] = $palette[$i % count($palette)];
}
$i++;
}
return [
'labels' => $labels,
'data' => $data,
'colors' => $colors
];
}
/* ============================================================
Data loading
============================================================ */
$connEntry = DB_ENTRY02();
$globalRows = [];
$result = $connEntry->query("
SELECT
Server,
AD,
GLPI,
SCCM,
EPO,
NBU,
OS,
crit,
dpt,
virtual,
S1
FROM GlobalCrossover
WHERE OS IS NOT NULL
AND LTRIM(RTRIM(OS)) <> ''
");
if ($result) {
while ($row = mysqli_fetch_assoc($result)) {
$globalRows[] = $row;
}
}
$connInfra = DB_INFRA();
$lifecycleMap = [];
$lifecycleRows = fetchInfraRows($connInfra, "
SELECT
OSKey,
OSFamily,
SupportEndDate,
Comment
FROM dbo.OSLifeCycle
");
foreach ($lifecycleRows as $row) {
$key = trim((string)($row['OSKey'] ?? ''));
if ($key === '') {
continue;
}
$lifecycleMap[$key] = [
'OSFamily' => $row['OSFamily'] ?? '',
'SupportEndDate' => normalizeDateValue($row['SupportEndDate'] ?? null),
'Comment' => $row['Comment'] ?? ''
];
}
/* ============================================================
KPI + chart data
============================================================ */
$totalWindows = 0;
$sccmOk = 0;
$s1Eligible = 0;
$s1Ok = 0;
$windowsAll = [];
$linuxAix = [];
$typeCounter = [
'Physical' => 0,
'Virtual' => 0
];
$statusCounter = [
'Supported' => 0,
'Warning' => 0,
'Expired' => 0,
'Unknown' => 0
];
$osRowsForTable = [];
foreach ($globalRows as $row) {
$os = $row['OS'] ?? '';
$osKey = normalizeOsKey($os);
$department = strtoupper(trim((string)($row['dpt'] ?? '')));
if (isWindowsOs($os)) {
$totalWindows++;
if (trim((string)($row['SCCM'] ?? '')) === 'Y') {
$sccmOk++;
}
incrementCounter($windowsAll, $osKey);
}
// SentinelOne KPI = Windows + Linux only. AIX excluded.
if (isWindowsOs($os) || isLinuxOs($os)) {
$s1Eligible++;
if (hasS1Agent($row['S1'] ?? '')) {
$s1Ok++;
}
}
if (isLinuxOs($os) || isAixOs($os)) {
incrementCounter($linuxAix, $osKey);
}
if (($row['virtual'] ?? '') === 'Y') {
incrementCounter($typeCounter, 'Virtual');
}
if (($row['virtual'] ?? '') === 'N') {
incrementCounter($typeCounter, 'Physical');
}
$status = getLifecycleStatus($osKey, $lifecycleMap);
incrementCounter($statusCounter, $status['label']);
if (!isset($osRowsForTable[$osKey])) {
$osRowsForTable[$osKey] = [
'OSKey' => $osKey,
'Count' => 0,
'Status' => $status['label'],
'EndDate' => $status['end'] ?? '-'
];
}
$osRowsForTable[$osKey]['Count']++;
}
$sccmPercent = $totalWindows > 0 ? round($sccmOk * 100 / $totalWindows, 2) : 0;
$s1Percent = $s1Eligible > 0 ? round($s1Ok * 100 / $s1Eligible, 2) : 0;
$totalType = array_sum($typeCounter);
$virtualPercent = $totalType > 0 ? round($typeCounter['Virtual'] * 100 / $totalType, 1) : 0;
$physicalPercent = $totalType > 0 ? round($typeCounter['Physical'] * 100 / $totalType, 1) : 0;
$chartWindowsAll = chartFromCounter($windowsAll, $lifecycleMap);
$chartLinuxAix = chartFromCounter($linuxAix, $lifecycleMap, 14, true);
$chartSupport = [
'labels' => array_keys($statusCounter),
'data' => array_values($statusCounter),
'colors' => ['#198754', '#fd7e14', '#dc3545', '#6c757d']
];
/* ============================================================
Existing cards data
============================================================ */
$nbADactive = mysqlScalar($connEntry, "SELECT COUNT(*) AS total FROM adcomputers", "total");
$nbADinactive = mysqlScalar($connEntry, "SELECT COUNT(*) AS total FROM adcomputers WHERE enabled = 'False'", "total");
$nbnessus = mysqlScalar($connEntry, "
SELECT COUNT(*) AS total
FROM GlobalCrossover
WHERE EPO IS NOT NULL
AND LTRIM(RTRIM(EPO)) <> ''
AND EPO NOT IN ('Non supported OS', 'N')
", "total");
$connGlpi = DB_GLPI();
$nbglpi = mysqlScalar($connGlpi, "
SELECT COUNT(name) AS total
FROM glpi_computers
WHERE entities_id = 6
AND is_deleted = 0
AND states_ID = 2
AND computertypes_id IN (7,19)
AND name <> ''
", "total");
mysqli_close($connGlpi);
$connNbu = DB_ENTRY01();
$nbnbu = mysqlScalar($connNbu, "SELECT COUNT(DISTINCT server) AS total FROM nb_jobs_full", "total");
mysqli_close($connNbu);
uasort($osRowsForTable, function($a, $b) {
$dateA = ($a['EndDate'] === '-' || empty($a['EndDate'])) ? '9999-12-31' : $a['EndDate'];
$dateB = ($b['EndDate'] === '-' || empty($b['EndDate'])) ? '9999-12-31' : $b['EndDate'];
$dateCompare = strcmp($dateA, $dateB);
if ($dateCompare !== 0) {
return $dateCompare;
}
return $b['Count'] <=> $a['Count'];
});
?>
Web Infra Reports IT
Active Directory
· inactive(s)
SCCM (Windows)
%
/ Windows
SentinelOne
%
/ Windows + Linux
Environment
Virtual
%
Physical
%
OS Support Status
$count): ?>
0 ? round($count * 100 / count($globalRows), 1) : 0;
?>