Le Gestionnaire des tâches ne garde rien en mémoire, ne t’alerte jamais et ne te dit pas si ton pare-feu est coupé. Dans ce tutoriel, tu construis System Dashboard, un moniteur système Windows complet en PHP 8, MySQL et JavaScript natif : temps réel, historique de 30 jours, alertes automatiques et audit de sécurité.

🐘 PHP 8.3 sans framework🐬 MySQL / MariaDB⚡ JavaScript natif (ES modules)🪟 WMI + COM🏛️ Clean Architecture🆓 Code open source

Ton PC rame, et tu ne sais pas pourquoi ? Ton disque C: est plein à 93 % sans que personne ne t’ait prévenu ? Un moniteur système Windows répond à ces questions en permanence : il mesure le processeur, la mémoire, les disques, le réseau et la batterie, enregistre tout dans une base de données et lève une alerte dès qu’un seuil est franchi.

Plutôt que d’installer un énième logiciel, on va le coder nous-mêmes. Résultat : une application web moderne, qui tourne en local sur WampServer, sans aucune dépendance externe (ni Composer, ni npm, ni build). Toutes les valeurs affichées sont réelles : elles viennent directement de Windows via WMI, et aucune donnée ne quitte ta machine.

Si tu as aimé notre application de diagnostic PC avec Flask et Python, ce moniteur système Windows va beaucoup plus loin : supervision continue, historique, alertes et audit de sécurité. Et si tu cherches simplement à surveiller un PC Windows en PHP, c’est exactement le bon point de départ.

Moniteur système Windows System Dashboard : tableau de bord avec score de santé, CPU, mémoire, stockage et réseau
Le tableau de bord de System Dashboard : score de santé, jauges CPU / mémoire / stockage et débit réseau en direct.
💡
Pourquoi c’est un super projet pour progresser en PHP

Tu touches à tout ce qu’on attend d’un développeur back-end : Clean Architecture, injection de dépendances maison, API REST sécurisée (sessions, CSRF, Argon2id), base de données indexée, interface temps réel en JavaScript natif… et un sujet rarement abordé : dialoguer avec Windows depuis PHP. Débutant, tu repars avec des bases solides ; confirmé, tu trouveras des choix d’architecture et d’optimisation à discuter.

Ce que fait ton moniteur système Windows

System Dashboard n’est pas une simple page de démo : c’est un vrai outil de supervision, organisé en 13 modules accessibles depuis le menu latéral. Voici ce que tu obtiens à la fin du projet.

📊 Tableau de bordScore de santé sur 100, jauges CPU / RAM / disque, débit réseau et courbes en direct.
🧠 Processeur & mémoireCharge globale et par cœur, fréquence réelle, mémoire engagée, barrettes installées.
💾 StockageVolumes, disques physiques (SSD/HDD, état de santé), lectures/écritures en temps réel.
🌐 RéseauInterfaces, débits, connexions TCP/UDP géolocalisées par pays.
🔋 BatterieNiveau, usure par rapport à la capacité d’origine, tension, autonomie.
⚙️ Processus & servicesListe triable rafraîchie toutes les 3 s, arrêt sécurisé, inventaire des services.
🛡️ Audit de sécuritéPare-feu, Defender, UAC, BitLocker, Secure Boot, ports sensibles exposés.
📈 HistoriqueDe 1 heure à 30 jours, statistiques, export CSV et journal d’événements.
🔔 Alertes & règles« Si le CPU dépasse 90 % pendant 1 minute, alors… » avec anti-rafale.
🩺 DiagnosticsPing, DNS, temps de réponse web, débit, benchmarks CPU / mémoire / disque.

À quoi ça sert concrètement ?

  • Comprendre une lenteur : l’historique montre quand le PC a ramé et quel processus était en cause.
  • Anticiper les pannes : une alerte quand le disque dépasse 95 % ou quand la batterie s’use anormalement.
  • Vérifier la sécurité d’un poste en un coup d’œil, pratique pour un petit parc de PC ou l’ordinateur de la famille.
  • Apprendre : le projet est structuré comme une application professionnelle, idéal pour un portfolio.

Les technologies du moniteur système Windows

Le choix de la stack est volontairement minimaliste : uniquement des outils déjà présents sur le poste d’un développeur PHP sous Windows.

CoucheTechnologiePourquoi ce choix
Serveur webApache (WampServer)Déjà installé chez la plupart des développeurs PHP sous Windows
BackendPHP 8.3, sans frameworkEnums, propriétés readonly, arguments nommés : le PHP moderne suffit largement
Accès systèmeWMI via l’extension COML’API officielle de Windows pour interroger le matériel et l’OS
Base de donnéesMySQL 8+ / MariaDB via PDOHistorique, utilisateurs, règles, alertes et journal
FrontendHTML + CSS + JavaScript natif (modules ES)Aucun build, aucune dépendance npm : tu ouvres, ça marche
GraphiquesChart.js 4 (copie locale)Léger, élégant, fonctionne sans connexion Internet
ArchitectureClean ArchitectureLe métier ne dépend ni de Windows, ni de MySQL, ni du HTTP
⚡
Zéro dépendance externe

Pas de Composer, pas de npm, pas d’étape de compilation. Tout le code est lisible tel quel : c’est parfait pour comprendre ce qui se passe réellement, ligne par ligne.

Prérequis pour installer le moniteur système Windows

  • Windows 10 ou 11
  • WampServer avec PHP 8.2 ou plus (testé en PHP 8.3.14)
  • Extensions PHP activées : com_dotnet, pdo_mysql, curl, mbstring
  • MySQL 8+ ou MariaDB 10.6+
  • Apache avec mod_rewrite et AllowOverride All (réglages par défaut de Wamp)
⚠️
N’oublie pas d’activer l’extension COM

Clic gauche sur l’icône Wamp → PHP → Extensions PHP → coche com_dotnet, puis redémarre les services. Sans elle, PHP est incapable de parler à Windows et l’application affichera une erreur explicite.

Installer le moniteur système Windows en 5 minutes

1. Récupérer le projet dans WampServer

Ouvre un terminal et clone le dépôt GitHub dans le dossier www de Wamp :

cd C:\wamp64\www
git clone https://github.com/LM-Code-Be/system-dashboard.git system-dashboard
Terminal (cmd)

Pas de Git ? Télécharge l’archive ZIP depuis GitHub (bouton Code → Download ZIP) et décompresse-la dans C:\wamp64\www\system-dashboard.

2. Ouvrir l’application et créer le compte administrateur

Rends-toi sur http://localhost/system-dashboard/. Au premier appel, l’API crée automatiquement la base de données et toutes ses tables : tu n’as rien à importer dans phpMyAdmin. Tu arrives directement sur l’écran de création du compte administrateur.

Écran de premier lancement du moniteur système Windows : création du compte administrateur
Premier lancement : la base est créée automatiquement, il ne reste qu’à choisir ton identifiant et ton mot de passe.

Choisis un mot de passe d’au moins 8 caractères, avec une lettre et un chiffre (la jauge de robustesse t’aide). Dès la validation, la supervision démarre.

3. (Optionnel) Adapter la connexion MySQL

Ton MySQL a un mot de passe root, ou écoute sur un autre port ? Crée le fichier backend/config/config.local.php. Il surcharge la configuration par défaut et n’est jamais versionné :

<?php
return [
    'base_de_donnees' => [
        'hote'         => '127.0.0.1',
        'port'         => 3306,
        'mot_de_passe' => 'mon-secret',
    ],
];
backend/config/config.local.php
🔐
Bonne pratique : séparer la configuration des secrets

Le fichier config.php contient des valeurs par défaut sans danger ; le fichier config.local.php, ignoré par Git, contient ce qui est propre à ta machine. Tu peux ainsi publier ton code sans jamais publier ton mot de passe.

L’architecture du moniteur système Windows

L’arborescence des fichiers

system-dashboard/ ├── .htaccess Routage : /api/* → backend, le reste → frontend ├── backend/ │ ├── bootstrap.php Autoload, configuration, conteneur │ ├── public/index.php Point d’entrée unique de l’API │ ├── config/ config.php (+ config.local.php optionnel) │ ├── database/ schema.sql, donnees_initiales.sql │ ├── bin/ installer.php, collecteur.php, planifier-collecte.bat │ ├── storage/ cache/ et logs/ (jamais exposés) │ └── src/ │ ├── Domain/ ← le cœur : règles métier, aucune dépendance │ ├── Application/ ← les cas d’utilisation │ ├── Infrastructure/ ← Windows, MySQL, cache, réseau… │ └── Presentation/ ← HTTP : routeur, contrôleurs, JSON └── frontend/ ├── index.html Application monopage └── assets/ ├── css/app.css Design system (thème sombre et clair) └── js/ ├── core/ API, flux temps réel, navigation ├── components/ Jauges, graphiques, tableaux └── pages/ Une page = un module chargé à la demande

La Clean Architecture expliquée simplement

Imagine un oignon. Au centre, le Domaine : ce qu’est une alerte, comment on calcule un score de santé. Autour, l’Application : les actions possibles (« se connecter », « lister les processus »). À l’extérieur, les détails techniques : l’Infrastructure (Windows, MySQL, fichiers de cache) et la Présentation (HTTP, JSON).

La règle d’or : les dépendances pointent toujours vers le centre. Le domaine ne sait pas que MySQL ou Windows existent. Comment récupère-t-il alors les données du système ? Grâce à une interface, qu’on appelle un « port » :

<?php
interface SondeSysteme
{
    /** Informations d'identité de la machine (OS, modèle, BIOS…). */
    public function informations(): array;

    /** Métriques temps réel calculées par différentiel de compteurs. */
    public function instantane(): Instantane;

    public function processus(): array;
    public function securite(): array;
    // …
}
backend/src/Domain/Port/SondeSysteme.php

Le domaine dit simplement : « j’ai besoin de quelque chose qui sache me donner un instantané du système ». L’infrastructure fournit l’implémentation concrète, SondeWindows. Demain, tu veux superviser un serveur Linux ? Tu écris une SondeLinux qui lit /proc, et rien d’autre ne change dans l’application.

Le branchement entre l’interface et son implémentation se fait à un seul endroit, le conteneur d’injection de dépendances :

private const LIAISONS = [
    Port\SondeSysteme::class   => SondeWindows::class,
    Port\Cache::class          => CacheFichier::class,
    Port\Session::class        => SessionPhp::class,
    Depot\DepotMesures::class  => Persistance\DepotMesuresPdo::class,
    // …
];
backend/src/Infrastructure/Conteneur.php

Le conteneur lit les constructeurs par réflexion (ReflectionClass) et fabrique automatiquement chaque objet avec ses dépendances, récursivement. Pas besoin de Symfony ou de PHP-DI : une centaine de lignes suffisent, et tu comprends enfin ce que font ces bibliothèques en coulisse.

🎓
Pour les débutants

Retiens surtout l’idée : séparer « ce que fait l’application » de « comment elle le fait techniquement ». C’est ce qui rend un code facile à tester, à comprendre et à faire évoluer sans tout casser.

Le routage Apache avec un seul .htaccess

Un unique fichier .htaccess à la racine sépare le frontend de l’API et interdit tout accès direct au backend (code source, logs, scripts) :

RewriteEngine On
RewriteBase /system-dashboard/

# 1. API REST : tout /api/* part vers le point d'entrée unique
RewriteRule ^api(/.*)?$ backend/public/index.php [QSA,END]

# 2. Accès direct interdit au backend, aux fichiers cachés et sensibles
RewriteRule ^backend(/|$) - [F,L]
RewriteRule \.(md|sql|log|bat|ps1|lock)$ - [F,L,NC]

# 3. Tout le reste → l'application monopage
RewriteRule ^$ frontend/index.html [END]
RewriteRule ^(.+)$ frontend/$1 [END]
.htaccess

Le drapeau [END] stoppe immédiatement la réécriture (contrairement à [L] qui peut relancer un tour), et [F] renvoie un 403 Forbidden. Même si quelqu’un devine le chemin d’un fichier PHP du backend, Apache refusera de le servir.

Étape 1 : la base de données du moniteur système Windows

La base system_dashboard est créée automatiquement au premier lancement. Ses tables sont nommées en français, comme tout le code du projet :

TableRôle
utilisateursComptes, rôles, verrouillage après échecs de connexion
parametresRéglages typés et bornés (seuils, fréquences, rétention…)
mesuresL’historique des métriques système
evenementsLe journal (sécurité, alertes, actions)
regles_automatisationLes règles « si … alors … »
alertesLes alertes levées et leur acquittement
diagnosticsLes résultats des tests de performance
geolocalisations_ipCache de géolocalisation des IP (7 jours)

La table centrale, celle qui alimente tout l’historique :

CREATE TABLE IF NOT EXISTS mesures (
    id                    BIGINT UNSIGNED  NOT NULL AUTO_INCREMENT,
    horodatage            DATETIME         NOT NULL,
    cpu_pourcentage       DECIMAL(5,2)     NOT NULL,
    memoire_pourcentage   DECIMAL(5,2)     NOT NULL,
    stockage_pourcentage  DECIMAL(5,2)     NOT NULL,
    reseau_reception_ko_s DECIMAL(12,2)    NULL,
    temperature_celsius   DECIMAL(5,2)     NULL,
    batterie_pourcentage  TINYINT UNSIGNED NULL,
    score_sante           TINYINT UNSIGNED NULL,
    -- …
    PRIMARY KEY (id),
    KEY idx_mesures_horodatage (horodatage)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
backend/database/schema.sql

Trois choix de conception méritent qu’on s’y arrête :

  • L’index sur horodatage : toutes les requêtes de l’historique filtrent par période (WHERE horodatage BETWEEN … AND …). Sans index, MySQL parcourrait des milliers de lignes à chaque affichage ; avec, il saute directement à la bonne plage.
  • Les colonnes NULL : un PC fixe n’a pas de batterie, et beaucoup de portables n’exposent pas leur température. Une donnée absente vaut NULL, jamais une fausse valeur comme 0 qui fausserait les moyennes.
  • DECIMAL plutôt que FLOAT : les pourcentages sont stockés de façon exacte, sans erreurs d’arrondi binaire dans les statistiques.
🕐
Les dates sont stockées en UTC

date_default_timezone_set('UTC') est appelé dans bootstrap.php, et c’est le navigateur qui convertit en heure locale. Résultat : aucun trou ni doublon dans l’historique au passage à l’heure d’été ou d’hiver.

Étape 2 : WMI PHP, interroger Windows depuis PHP

C’est la partie la plus originale du projet, et celle qu’on trouve rarement expliquée en français.

C’est quoi, WMI ?

WMI (Windows Management Instrumentation) est une base de données intégrée à Windows, interrogeable avec un langage très proche du SQL : le WQL. Processeur, mémoire, disques, services, batterie, antivirus… tout y est exposé sous forme de « classes » :

SELECT Name, PercentProcessorTime FROM Win32_PerfRawData_PerfOS_Processor
Requête WQL

Et PHP peut l’interroger grâce à l’extension COM (com_dotnet), qui permet d’instancier des composants Windows, exactement comme le ferait un script PowerShell ou VBScript.

Le client WMI

public function requete(string $wql, string $espace = 'root/cimv2'): array
{
    // L'accès direct aux propriétés nommées est ~7× plus rapide que l'énumération Properties_.
    $colonnes = preg_match('/^\s*SELECT\s+(.+?)\s+FROM\s/is', $wql, $m) && trim($m[1]) !== '*'
        ? array_map('trim', explode(',', $m[1]))
        : null;

    $resultats = [];
    foreach ($this->service($espace)->ExecQuery($wql, 'WQL', self::DRAPEAUX_RAPIDES) as $objet) {
        $ligne = [];
        foreach ($colonnes as $colonne) {
            $ligne[$colonne] = $this->convertir($objet->$colonne);
        }
        $resultats[] = $ligne;
    }
    return $resultats;
}
backend/src/Infrastructure/Systeme/Windows/ClientWmi.php

Ce qu’il faut comprendre :

  • ExecQuery envoie la requête WQL au service WMI de Windows ;
  • DRAPEAUX_RAPIDES (valeur 48 = ReturnImmediately + ForwardOnly) demande un parcours en flux, sans tout charger en mémoire ;
  • on ne lit que les colonnes citées dans le SELECT : c’est environ 7 fois plus rapide que de parcourir toutes les propriétés de l’objet ;
  • convertir() transforme les types COM (variants, dates CIM, tableaux) en types PHP propres.

L’astuce performance : les compteurs bruts

Windows propose des classes « formatées » (Win32_PerfFormattedData_*) qui donnent directement le pourcentage CPU… mais chacune met environ 300 ms à répondre, car WMI prend lui-même deux mesures et attend entre les deux. Avec une dizaine de métriques, l’interface mettrait 3 secondes à se rafraîchir : inutilisable.

La solution : lire les compteurs bruts (Win32_PerfRawData_*, environ 20 ms) et calculer soi-même la différence avec la mesure précédente, gardée en cache :

public function mesurer(): array
{
    return $this->cache->memoriser(self::CLE_MESURE, 1.0, function (): array {
        $precedent = $this->cache->lire(self::CLE_BRUTS);
        $actuel = $this->echantillonner();
        $age = is_array($precedent) ? $actuel['t'] - $precedent['t'] : null;

        // Pas d'échantillon récent exploitable : on en prend deux, espacés de 500 ms
        if ($age === null || $age < self::AGE_MIN_S || $age > self::AGE_MAX_S) {
            usleep(500_000);
            $precedent = $actuel;
            $actuel = $this->echantillonner();
        }

        $this->cache->ecrire(self::CLE_BRUTS, $actuel);
        return $this->calculer($precedent, $actuel);
    });
}
backend/src/Infrastructure/Systeme/Windows/EchantillonneurCompteurs.php

Le calcul du pourcentage processeur tient ensuite en une seule formule :

// % CPU = 100 × (1 − Δtemps_inactif / Δhorloge)
$dHorloge = $horloge - $avant['cpu'][$nom][1];
$valeur = $dHorloge > 0
    ? $this->borner(100 * (1 - ($inactif - $avant['cpu'][$nom][0]) / $dHorloge))
    : 0.0;
Calcul du % CPU

Windows compte en permanence le temps que chaque cœur passe « à ne rien faire ». Entre deux mesures, si le processeur a été inactif 70 % du temps, il a donc travaillé 30 %. C’est exactement la méthode utilisée par le Gestionnaire des tâches : les valeurs affichées concordent. Résultat : un rafraîchissement temps réel complet répond en ~50 ms.

Moniteur système Windows : page Processeur avec charge globale, charge par cœur logique et fréquence réelle
La page Processeur du moniteur système Windows : charge globale, cœur le plus sollicité, 8 cœurs logiques et fréquence réelle.

Un cache disque partagé, protégé par un verrou

Si tu ouvres trois onglets, ils interrogent tous l’API toutes les 3 secondes. Pour ne pas solliciter Windows trois fois, chaque donnée est mise en cache sur disque, avec un verrou exclusif (flock) qui garantit qu’un seul calcul a lieu à la fois :

public function memoriser(string $cle, float $dureeSecondes, callable $calcul): mixed
{
    $entree = $this->lireEntree($cle);
    if ($entree !== null && $entree['expire'] > microtime(true)) {
        return $entree['valeur'];               // encore frais : on le renvoie
    }

    $verrou = @fopen($this->chemin($cle) . '.lock', 'c');
    flock($verrou, LOCK_EX);                    // un seul calcul à la fois
    try {
        // Un autre processus a peut-être calculé la valeur pendant l'attente du verrou.
        $entree = $this->lireEntree($cle);
        if ($entree !== null && $entree['expire'] > microtime(true)) {
            return $entree['valeur'];
        }
        $valeur = $calcul();
        $this->ecrireEntree($cle, $valeur, microtime(true) + $dureeSecondes);
        return $valeur;
    } finally {
        flock($verrou, LOCK_UN);
        fclose($verrou);
    }
}
backend/src/Infrastructure/Cache/CacheFichier.php

Remarque la double vérification après l’obtention du verrou : c’est le patron double-checked locking. Pendant qu’un onglet attendait, un autre a peut-être déjà calculé la valeur ; inutile de recommencer. La durée de cache s’adapte à la volatilité de l’information : 1 s pour les compteurs, 10 s pour les services, 1 h pour le matériel (ta carte graphique ne change pas toutes les secondes).

Moniteur système Windows : page Mémoire avec utilisation de la RAM, composition et mémoire engagée
La page Mémoire du moniteur système Windows : utilisation de la RAM, composition et mémoire engagée.

Étape 3 : le domaine, le cœur métier

Le score de santé sur 100

Un seul chiffre de 0 à 100 résume l’état du PC. Chaque facteur a un poids, et un dépassement de seuil (avertissement ou critique) lui retire des points :

private const POIDS = [
    'cpu' => 20, 'memoire' => 20, 'stockage' => 20,
    'temperature' => 15, 'pare_feu' => 10, 'antivirus' => 10, 'batterie' => 5,
];

private function facteurSeuil(string $cle, string $libelle, float $valeur, string $unite, array $seuils, string $prefixe): array
{
    $avertissement = (float) ($seuils["seuil_{$prefixe}_avertissement"] ?? 80);
    $critique = (float) ($seuils["seuil_{$prefixe}_critique"] ?? 95);
    $statut = $valeur >= $critique ? 'critique' : ($valeur >= $avertissement ? 'avertissement' : 'ok');
    return $this->construire($cle, $libelle, round($valeur, 1), $unite, $statut);
}
backend/src/Domain/Service/CalculateurSante.php

Cette classe ne fait aucun appel à Windows ni à MySQL : elle reçoit des valeurs et renvoie un score. C’est du code pur, déterministe, idéal pour écrire tes premiers tests unitaires avec PHPUnit. Les seuils, eux, viennent de la table parametres : tu les ajustes depuis l’interface, sans toucher au code.

Les règles d’automatisation

Une règle se lit comme une phrase : « si <métrique> <opérateur> <seuil> pendant <durée>, alors <action> ». Toute l’intelligence se trouve dans l’entité elle-même :

public function evaluer(?float $valeur, DateTimeImmutable $maintenant): bool
{
    if (!$this->active || $valeur === null) {
        return false;
    }

    // Condition fausse : on remet le chronomètre à zéro
    if (!$this->operateur->comparer($valeur, $this->seuil)) {
        $this->conditionDepuis = null;
        return false;
    }

    // Condition vraie : depuis quand ?
    $this->conditionDepuis ??= $maintenant;
    $soutenue = ($maintenant->getTimestamp() - $this->conditionDepuis->getTimestamp()) >= $this->dureeSecondes;

    // Anti-rafale : on ne redéclenche pas pendant le délai de réactivation
    $enPause = $this->dernierDeclenchement !== null
        && ($maintenant->getTimestamp() - $this->dernierDeclenchement->getTimestamp()) < $this->delaiReactivationSecondes;
    // …
}
backend/src/Domain/Entite/RegleAutomatisation.php

Deux notions évitent les fausses alertes, le grand défaut des outils de supervision mal réglés :

  • La durée soutenue : un pic de CPU d’une seconde au lancement d’un programme n’est pas un problème ; 60 secondes à 95 % en est un.
  • Le délai de réactivation : une fois l’alerte levée, l’application ne te harcèle pas toutes les 3 secondes pendant que tu règles le souci.

Les opérateurs et les métriques sont des enums PHP 8.1 adossés à des chaînes : il devient impossible d’enregistrer une métrique qui n’existe pas, et la conversion depuis la base se fait avec Metrique::from().

enum Metrique: string
{
    case Cpu = 'cpu';
    case Memoire = 'memoire';
    case Stockage = 'stockage';
    case Temperature = 'temperature';
    case Batterie = 'batterie';
    // …
}
backend/src/Domain/Enum/Metrique.php

Étape 4 : l’API REST

Un seul point d’entrée

Toutes les requêtes /api/* arrivent dans backend/public/index.php, qui tient en trois lignes :

<?php
$conteneur = require dirname(__DIR__) . '/bootstrap.php';

$conteneur->noyau()
    ->gerer(static fn () => Requete::depuisGlobales())
    ->envoyer();
backend/public/index.php

La table de routage

Lisible d’un coup d’œil, avec le niveau d’accès de chaque route :

$r->ajouter('GET',    '/session',             [Auth::class, 'session'], Routeur::PUBLIC);
$r->ajouter('POST',   '/session/connexion',   [Auth::class, 'connecter'], Routeur::PUBLIC);
$r->ajouter('GET',    '/temps-reel',          [Surveillance::class, 'tempsReel']);
$r->ajouter('GET',    '/systeme/{ressource}', [Surveillance::class, 'ressource']);
$r->ajouter('DELETE', '/processus/{pid}',     [Surveillance::class, 'terminerProcessus'], Routeur::ADMINISTRATEUR);
$r->ajouter('GET',    '/historique',          [Historique::class, 'historique']);
backend/src/Presentation/Http/routes.php

Trois niveaux d’accès : PUBLIC (sans connexion), connecté (par défaut) et ADMINISTRATEUR. Les verbes HTTP ont leur vrai sens : GET lit, POST crée, PUT modifie, DELETE supprime.

Le noyau : le chef d’orchestre

Pour chaque requête, le Noyau enchaîne les vérifications avant d’appeler le contrôleur. C’est un mini-pipeline de middlewares, sans framework :

$route = $this->routeur->resoudre($requete->methode, $requete->chemin);

// 1. Protection CSRF sur toute écriture
if (!$requete->estLecture() && !$session->verifierJetonCsrf($requete->entete('X-CSRF-Token'))) {
    throw new AccesRefuse('Jeton de sécurité invalide ou expiré. Rechargez la page.');
}

// 2. Authentification
if ($route['acces'] !== Routeur::PUBLIC) {
    $utilisateurId = $session->utilisateurId() ?? throw new NonAuthentifie();
    // 3. Autorisation
    if ($route['acces'] === Routeur::ADMINISTRATEUR && !$utilisateur->role->peutModifier()) {
        throw new AccesRefuse();
    }
}

// 4. Appel du contrôleur
return $controleur->$action($requete);
backend/src/Presentation/Http/Noyau.php

Les exceptions métier sont converties en réponses JSON avec le bon code HTTP (401 non connecté, 403 interdit, 422 données invalides…). Toutes les réponses partagent le même format, ce qui simplifie énormément le frontend :

{ "succes": true,  "donnees": { "cpu": { "pourcentage": 36.4 } } }
{ "succes": false, "erreur": { "message": "Identifiant ou mot de passe incorrect.", "details": {} } }
Format des réponses
⚡
Rien à créer à la main

Si une table n’existe pas (erreur SQL 42S02), le noyau lance l’installateur puis rejoue la requête. C’est pour ça que l’installation se résume à « ouvre la page ».

Étape 5 : sécuriser ton moniteur système Windows

Une application capable de terminer des processus sur ton PC doit être sérieusement protégée. Voici les menaces prises en compte et la réponse apportée à chacune :

MenaceProtection
Accès depuis le réseauRequire local (réglage par défaut de WampServer) : seule ta machine peut ouvrir l’application
Vol de mot de passe en baseHachage Argon2id avec password_hash()
Force bruteVerrouillage du compte 15 min après 5 échecs
Vol de sessionCookie HttpOnly + SameSite=Strict, identifiant régénéré à la connexion
CSRFJeton obligatoire dans l’en-tête X-CSRF-Token pour tout POST / PUT / DELETE
Erreur de manipulationProcessus système et Wamp protégés contre l’arrêt
Lecture du code sourceLe dossier backend/ n’est jamais servi par Apache

Le cas d’utilisation de connexion illustre plusieurs de ces protections à la fois :

if ($utilisateur !== null && $utilisateur->estVerrouille($maintenant)) {
    throw new NonAuthentifie("Compte temporairement verrouillé après plusieurs échecs. Réessayez dans {$minutes} minute(s).");
}

if ($utilisateur === null || !$utilisateur->actif || !$this->hacheur->verifier($motDePasse, $utilisateur->motDePasseHache)) {
    if ($utilisateur !== null) {
        $utilisateur->enregistrerEchec($this->tentativesMax, $this->minutesVerrouillage, $maintenant);
    } else {
        // Temps constant : on ne révèle pas si l'identifiant existe
        password_verify($motDePasse, '$2y$10$usesomesillystringfore7hnbRJHxXVLeakoG8K30oukPsA.ztMG');
    }
    throw new NonAuthentifie('Identifiant ou mot de passe incorrect.');
}
backend/src/Application/CasUtilisation/Authentification/SeConnecter.php
🛡️
Le détail qui change tout : l’attaque temporelle

Quand l’identifiant n’existe pas, on calcule quand même un hachage. Sinon, un attaquant pourrait deviner les comptes existants en mesurant le temps de réponse : une réponse plus rapide trahirait un compte inexistant. Le message d’erreur est d’ailleurs volontairement identique dans les deux cas.

Page de connexion sécurisée du moniteur système Windows System Dashboard
L’écran de connexion : sessions sécurisées, jeton CSRF et verrouillage après 5 échecs.
Audit de sécurité Windows : pare-feu, Windows Defender, bureau à distance et ports sensibles exposés
Le module Sécurité audite le poste : antivirus, pare-feu, bureau à distance, ports sensibles exposés… avec un score sur 100.

Le module Sécurité applique la même philosophie au poste lui-même : il interroge l’espace WMI root/SecurityCenter2 et root/Microsoft/Windows/Defender pour vérifier l’antivirus, la protection en temps réel, les définitions, le pare-feu (profils domaine, privé, public), l’UAC, BitLocker, Secure Boot, et détecte les ports sensibles en écoute (RPC, SMB, MySQL…) avec un conseil de remédiation.

Étape 6 : le frontend temps réel du moniteur système Windows

Une application monopage sans framework

Le frontend utilise les modules ES natifs du navigateur (import / export). Chaque page est un module chargé à la demande avec import() dynamique quand tu cliques dans le menu : la première ouverture est ultra-rapide. Le routage repose sur le hash de l’URL (#/processeur, #/reseau…), ce qui ne demande aucun réglage serveur.

Le client API

Toutes les requêtes passent par une petite fonction qui gère le JSON, le jeton CSRF et les erreurs de manière centralisée :

async function requete(methode, chemin, corps) {
  const entetes = { Accept: 'application/json' };
  if (corps !== undefined) entetes['Content-Type'] = 'application/json';
  if (methode !== 'GET' && jetonCsrf) entetes['X-CSRF-Token'] = jetonCsrf;

  const reponse = await fetch(`api${chemin}`, {
    method: methode,
    headers: entetes,
    body: corps !== undefined ? JSON.stringify(corps) : undefined,
    credentials: 'same-origin',
  });
  const contenu = await reponse.json();

  if (!reponse.ok || !contenu?.succes) {
    // Session expirée : toute l'application est prévenue d'un coup
    if (reponse.status === 401) {
      window.dispatchEvent(new CustomEvent('sd:session-expiree'));
    }
    throw new ErreurApi(contenu?.erreur?.message, reponse.status);
  }
  return contenu.donnees;
}

export const api = {
  get: (chemin) => requete('GET', chemin),
  post: (chemin, corps = {}) => requete('POST', chemin, corps),
  // …
};
frontend/assets/js/core/api.js

Le flux temps réel et le patron observateur

Un seul objet interroge /temps-reel toutes les 3 secondes et diffuse chaque mesure à toutes les jauges et à tous les graphiques abonnés. C’est le patron observateur, construit sur l’EventTarget natif du navigateur :

class FluxTempsReel extends EventTarget {
  /** Le rappel reçoit (mesure, historique) ; retourne la fonction de désabonnement. */
  abonner(rappel) {
    const ecouteur = (e) => rappel(e.detail.mesure, e.detail.historique);
    this.addEventListener('mesure', ecouteur);
    if (this.dernier) rappel(this.dernier, this.historique);
    return () => this.removeEventListener('mesure', ecouteur);
  }

  async tick() {
    if (document.hidden) return this.definirEtat('pause');   // onglet masqué : on économise
    const mesure = await api.get('/temps-reel');
    this.historique.push(mesure);
    if (this.historique.length > TAILLE_HISTORIQUE) this.historique.shift();
    this.dispatchEvent(new CustomEvent('mesure', { detail: { mesure, historique: this.historique } }));
  }
}
frontend/assets/js/core/temps-reel.js

Une page n’a plus qu’à écrire une ligne pour s’abonner…

const seDesabonner = flux.abonner((mesure) => jauge.definir(mesure.cpu.pourcentage));

// … et quand on quitte la page :
seDesabonner();
frontend/assets/js/pages/processeur.js

…et à appeler seDesabonner() en la quittant, pour éviter les fuites mémoire (un piège classique des SPA « faites main »).

🔋
Bonus écologique

Quand l’onglet est masqué, l’événement visibilitychange met le flux en pause. Ton PC n’est pas sollicité pour rien, et le badge « En direct » repasse au vert dès que tu reviens.

Moniteur réseau Windows : débit en temps réel et connexions établies géolocalisées par pays
La page Réseau du moniteur système Windows : débit en direct et pays des connexions établies.
Moniteur de stockage Windows : volumes, espace libre, entrées/sorties disque en temps réel
La page Stockage du moniteur système Windows : remplissage des volumes et activité disque.

Étape 7 : alertes du moniteur système Windows et collecte 24 h/24

Le moteur de règles

À chaque mesure enregistrée, le MoteurRegles évalue toutes les règles actives et exécute l’action associée quand une condition est remplie :

public function evaluer(Instantane $instantane): array
{
    $creees = [];
    foreach ($this->regles->actives() as $regle) {
        $valeur = $instantane->valeur($regle->metrique);
        if ($regle->evaluer($valeur, $instantane->horodatage) && $valeur !== null) {
            $alerte = $this->executer($regle, $valeur, $instantane);
            if ($alerte !== null) {
                $creees[] = $alerte->versTableau();
            }
        }
    }
    return $creees;
}
backend/src/Application/Service/MoteurRegles.php

Trois actions sont possibles : lever une alerte (visible dans la cloche en haut de l’écran), écrire dans le journal, ou terminer un processus — par exemple un programme qui sature la mémoire, sauf s’il figure dans la liste des processus protégés.

Alertes et règles d'automatisation du moniteur système Windows : processeur saturé, mémoire pleine, batterie faible
Les alertes et règles du moniteur système Windows : seuils, durée soutenue et pause anti-rafale.

Collecter 24 h/24, même interface fermée

Tant qu’un onglet est ouvert, l’historique se remplit tout seul. Pour enregistrer des mesures en permanence, un script crée une tâche planifiée Windows qui lance le collecteur en ligne de commande chaque minute :

backend\bin\planifier-collecte.bat
À exécuter en tant qu’administrateur

En coulisse, il s’appuie sur schtasks, l’outil natif de Windows :

schtasks /Create /TN "SystemDashboard-Collecteur" /SC MINUTE /MO 1 /RU SYSTEM /RL HIGHEST /F ^
  /TR "\"%PHP%\" -c \"%INI%\" \"%RACINE%collecteur.php\""
backend/bin/planifier-collecte.bat

La tâche tourne sous le compte SYSTEM avec les privilèges les plus élevés : elle a donc accès aux compteurs protégés et fonctionne même quand personne n’est connecté. Les mesures de plus de 30 jours (valeur réglable) sont purgées automatiquement pour que la base reste légère.

Historique des performances Windows sur 7 jours : CPU, mémoire, trafic réseau, disque, température et batterie
L’historique du moniteur système Windows sur 7 jours : CPU, mémoire, réseau, disque, santé et batterie.

Visite guidée du moniteur système Windows

L’interface existe en thème sombre et en thème clair (bouton soleil/lune en haut à droite). Voici les modules que nous n’avons pas encore vus.

Batterie : niveau, usure et tension

Sur un portable, la page Batterie compare la capacité pleine charge à la capacité d’origine pour estimer l’usure. Ici, 27 % d’usure : la batterie ne tient plus que 73 % de sa capacité neuve, un indicateur précieux avant de la remplacer.

Santé de la batterie d'un PC portable Windows : capacité, usure, tension et alimentation
La page Batterie du moniteur système Windows : niveau, capacité restante, tension et alimentation.

Processus : tri, recherche et arrêt sécurisé

La liste se rafraîchit toutes les 3 secondes et se trie par processeur, mémoire, disque ou threads. Le bouton d’arrêt n’apparaît pas sur les processus système (cadenas) : impossible de tuer System ou httpd.exe par erreur.

Liste des processus Windows en temps réel avec utilisation CPU, mémoire, disque et threads
La page Processus du moniteur système Windows : tri, recherche et arrêt sécurisé.

Fichiers : qu’est-ce qui prend toute la place ?

Indique un dossier, clique sur Analyser : l’application parcourt l’arborescence et affiche les sous-dossiers les plus volumineux ainsi que la répartition par type de fichier.

Analyse de l'espace disque sous Windows : sous-dossiers les plus volumineux et répartition par type de fichier
L’analyse de l’espace disque du moniteur système Windows : dossiers et extensions les plus lourds.

Diagnostics : réseau et benchmarks

Sept tests à la demande : latence (ping), résolution DNS, temps de réponse web (avec temps de négociation TLS), débit descendant, et trois benchmarks maison (SHA-256 mono-cœur, copie mémoire, écriture/lecture disque synchronisée). Chaque résultat est historisé.

Tests de diagnostic Windows : ping, DNS, temps de réponse web, débit et benchmarks CPU, mémoire, disque
Les diagnostics du moniteur système Windows : tests réseau et benchmarks de performance.

Le thème clair

Tableau de bord du moniteur système Windows en thème clair
Le même tableau de bord en thème clair : le choix est mémorisé dans le navigateur.

Dépannage du moniteur système Windows : erreurs courantes

ProblèmeSolution
« L’extension PHP com_dotnet est requise »Active com_dotnet dans les extensions PHP de Wamp, puis redémarre tous les services
« Base de données inaccessible »Vérifie que MySQL est démarré (icône Wamp verte) et les identifiants dans config.local.php
Page blanche ou 404 sur /apiActive mod_rewrite dans Apache et vérifie AllowOverride All
L’interface ne reflète pas une modificationRecharge sans cache : Ctrl + F5
Température « non disponible »Ton matériel n’expose pas de capteur via WMI (très fréquent sur portable) : c’est normal
Installé ailleurs que /system-dashboard/Adapte RewriteBase dans le .htaccess racine
Une erreur serveur 500Consulte backend/storage/logs/php-erreurs.log

Améliorer ton moniteur système Windows

Le projet est volontairement conçu pour être étendu. Quelques idées pour le faire évoluer :

  • Notifications : envoyer les alertes critiques par e-mail, Telegram ou Discord en ajoutant une valeur à l’enum ActionAutomatisation.
  • Supervision de plusieurs PC : un petit agent sur chaque machine qui pousse ses mesures vers un serveur central.
  • Support de Linux : écrire une SondeLinux qui lit /proc/stat et /proc/meminfo — grâce à l’architecture, rien d’autre ne change.
  • Tests automatisés avec PHPUnit : CalculateurSante et RegleAutomatisation sont du code pur, parfaits pour débuter.
  • Temps réel par push avec les Server-Sent Events à la place de l’interrogation toutes les 3 secondes.
  • Application de bureau avec Electron ou Tauri, pour une icône dans la barre des tâches.

FAQ : moniteur système Windows en PHP

System Dashboard est-il gratuit ?

Oui. Le code source est disponible gratuitement sur GitHub : tu peux l’utiliser, l’étudier, le modifier et le partager.

Peut-on surveiller un PC Windows avec PHP ?

Oui, grâce à l’extension COM (com_dotnet) qui donne accès à WMI, l’interface officielle de Windows pour interroger le matériel, les performances, les services et la sécurité.

System Dashboard fonctionne-t-il sur Mac ou Linux ?

Pas en l’état : la sonde utilise WMI, propre à Windows. L’architecture est cependant prévue pour ajouter une autre sonde, par exemple une SondeLinux qui lit /proc.

Mes données partent-elles sur Internet ?

Non. Seules deux fonctions contactent l’extérieur : la géolocalisation des connexions (désactivable dans les Paramètres) et les diagnostics réseau, qui ne s’exécutent que lorsque tu les lances.

Est-ce que le moniteur ralentit mon PC ?

Très peu : une mesure complète prend environ 50 ms toutes les 3 secondes grâce aux compteurs bruts et au cache, et le flux se met en pause quand l’onglet est masqué.

Puis-je consulter le tableau de bord depuis mon téléphone ?

Par défaut non : WampServer n’accepte que les connexions locales, et c’est voulu. Pour un accès distant, passe par un VPN comme WireGuard ou Tailscale plutôt que d’exposer l’application sur Internet.

Ça fonctionne avec MariaDB ?

Oui, à partir de MariaDB 10.6, ainsi qu’avec MySQL 8 et plus.

Conclusion

Tu as maintenant un vrai moniteur système Windows, codé de tes mains… et surtout, tu as vu comment construire une application PHP propre et professionnelle sans framework :

  • une Clean Architecture qui isole le métier des détails techniques ;
  • un accès à Windows via WMI, optimisé grâce aux compteurs bruts et à un cache verrouillé ;
  • une API REST sécurisée (Argon2id, CSRF, verrouillage, rôles, protection contre les attaques temporelles) ;
  • un frontend temps réel en JavaScript natif, basé sur le patron observateur ;
  • un moteur de règles qui veille sur ta machine même quand tu n’es pas là.

Clone le projet, teste-le, casse-le, améliore-le — et si tu ajoutes les notifications Discord ou la supervision multi-PC, n’hésite pas à partager ton travail !

Envie d’autres projets PHP sans framework ? Découvre aussi comment créer une application de planning en PHP et MySQL, une application de gestion de stock en PHP/MySQL ou ton Netflix maison MediaCloud. Et si ton API renvoie une erreur 404 ou 500, notre guide des codes HTTP t’aidera à la corriger.

Comments

No comments yet. Why don’t you start the discussion?

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *