Vue d'ensemble
Ce fichier fait une seule chose : recevoir une question tapée dans un formulaire, et afficher à quel point chacun des 3 documents lui ressemble. C'est le Round 3 de votre histoire — la recherche sémantique.
cosineSimilarity), une fonction qui simule un embedding (getEmbeddingSimule), une liste de documents, puis le formulaire HTML qui utilise tout ça.Bloc 1 — cosineSimilarity() : mesurer une ressemblance
1function cosineSimilarity(array $a, array $b): float 2{ 3 $dot = $normA = $normB = 0.0; 4 foreach ($a as $i => $x) { 5 $dot += $x * $b[$i]; 6 $normA += $x * $x; 7 $normB += $b[$i] * $b[$i]; 8 } 9 return ($normA && $normB) ? $dot / (sqrt($normA) * sqrt($normB)) : 0.0; 10}
Ce que fait chaque ligne
| Ligne | Explication |
|---|---|
array $a, array $b | La fonction attend deux vecteurs (deux tableaux de nombres) à comparer. |
: float | Elle renverra toujours un nombre décimal (le score). |
$dot = $normA = $normB = 0.0; | Trois compteurs à zéro, remplis pendant la boucle. |
foreach ($a as $i => $x) | On parcourt le vecteur $a ; $i est la position (0, 1, 2…), $x est la valeur à cette position. |
$dot += $x * $b[$i] | On multiplie les nombres à la même position dans les deux vecteurs, et on additionne au fil de la boucle — c'est le produit scalaire. |
$normA += $x * $x | On additionne le carré de chaque nombre de $a — pour calculer sa « longueur ». |
return (...) ? ... : 0.0; | Un raccourci d'écriture (opérateur ternaire) qui veut dire : si les deux normes ne sont pas nulles, on divise ; sinon, on renvoie 0 pour éviter une division par zéro. |
Bloc 2 — getEmbeddingSimule() : deviner un vecteur à partir de mots
1function getEmbeddingSimule(string $texte): array 2{ 3 $texte = strtolower($texte); 4 if (str_contains($texte, "durée") || str_contains($texte, "dure") 5 || str_contains($texte, "heures") || str_contains($texte, "temps")) { 6 return [0.76, 0.19, -0.27, 0.80]; 7 } 8 if (str_contains($texte, "prix") || ...) { 9 return [-0.58, 0.22, 0.68, 0.18]; 10 } 11 if (str_contains($texte, "apprend") || ...) { 12 return [0.28, 0.73, 0.38, -0.08]; 13 } 14 return [0, 0, 0, 0]; 15}
Ce que fait chaque ligne
| Ligne | Explication |
|---|---|
strtolower($texte) | Met le texte en minuscules, pour que « Durée » et « durée » soient traités pareil. |
str_contains($texte, "durée") | Teste si le mot « durée » apparaît dans le texte, peu importe où. |
|| str_contains(...) | « OU » : si n'importe lequel de ces mots est présent, la condition est vraie. |
return [0.76, 0.19, -0.27, 0.80]; | Un vecteur fixe, préparé à l'avance, qu'on renvoie dès qu'un des mots de la catégorie « durée » est détecté. |
return [0, 0, 0, 0]; | Si aucun mot connu n'a été trouvé, on renvoie un vecteur neutre. |
Bloc 3 — $documents : la base de connaissances
1$documents = [ 2 ["contenu" => "La formation Laravel dure 40 heures.", 3 "vecteur" => [0.80, 0.18, -0.30, 0.85]], 4 ["contenu" => "...", "vecteur" => [...]], 5 ["contenu" => "...", "vecteur" => [...]], 6];
Ce que fait chaque ligne
$documents est un tableau de tableaux : une liste, où chaque élément est lui-même un petit tableau avec deux informations — le texte (contenu) et son vecteur déjà calculé (vecteur).
Bloc 4 — Traiter la question envoyée par le formulaire
1$texte = trim($_POST['texte'] ?? ''); 2$resultat = null; 3if ($texte !== '') { 4 $vQuestion = getEmbeddingSimule($texte); 5 $resultat = []; 6 foreach ($documents as $document) { 7 $resultat[] = [ 8 'contenu' => $document['contenu'], 9 'score' => cosineSimilarity($vQuestion, $document['vecteur']), 10 ]; 11 } 12}
Ce que fait chaque ligne
| Ligne | Explication |
|---|---|
$_POST['texte'] ?? '' | Récupère le texte envoyé par le formulaire ; ?? évite une erreur si le champ n'existe pas encore (première visite de la page). |
trim(...) | Enlève les espaces inutiles au début et à la fin. |
if ($texte !== '') | On ne calcule rien si la personne n'a encore rien tapé. |
$vQuestion = getEmbeddingSimule($texte) | On transforme la question en vecteur (Bloc 2). |
foreach ($documents as $document) | On examine chaque document, un par un. |
$resultat[] = [...] | On ajoute à $resultat le contenu du document et son score de similarité. |
Bloc 5 — e() : la fonction de sécurité
1function e(string $s): string 2{ 3 return htmlspecialchars($s, ENT_QUOTES, 'UTF-8'); 4}
Pourquoi cette fonction existe
Avant d'afficher un texte tapé par l'utilisateur dans la page, on doit le « nettoyer ». Sans ça, quelqu'un pourrait taper du code HTML ou JavaScript dans le champ, qui s'exécuterait dans la page — une faille appelée XSS. htmlspecialchars() transforme les caractères dangereux (<, >, "…) en texte inoffensif.
$_POST, $_GET…) doit passer par e() avant d'être affiché dans le HTML.Bloc 6 — Le mélange HTML / PHP
1<input type="text" name="texte" value="<?= e($texte !== '' ? $texte : "Quel est le tarif ?") ?>"> 2 3<?php if ($resultat !== null): 4 $meilleurScore = max(array_column($resultat, 'score')); ?> 5 <?php foreach ($resultat as $r): ?> 6 <span><?= $r['score'] == $meilleurScore ? '✅ ' : '' ?><?= e($r['contenu']) ?></span> 7 <?php endforeach; ?> 8<?php endif; ?>
Ce que fait chaque ligne
| Élément | Explication |
|---|---|
<?= ... ?> | Raccourci de <?php echo ... ?> : affiche directement une valeur PHP dans le HTML. |
if (...) : ... endif; | Syntaxe alternative du if { }, pratique quand on mélange PHP et HTML — plus lisible que des accolades. |
array_column($resultat, 'score') | Extrait uniquement la colonne « score » de tous les résultats, pour en calculer le maximum juste après. |
$r['score'] == $meilleurScore ? '✅ ' : '' | Opérateur ternaire : si ce score est le meilleur, on affiche une coche verte, sinon rien. |
foreach (...) : ... endforeach; | Même idée que le foreach classique, en syntaxe alternative pour le HTML. |
Trace en direct : exécutez le code sous vos yeux
Choisissez une question : ce panneau rejoue exactement ce que PHP ferait, étape par étape.