FORMATION DÉVELOPPEMENT WEB

De HTML5 au Git professionnel

Cours complet et progressif : HTML5 (structure, sémantique, formulaires), CSS3 (Box Model, Flexbox, Grid, responsive), JavaScript (DOM, événements, fetch), MySQL (bases, CRUD, fonctions, jointures), PHP (PDO, CRUD, sécurité), Git & GitHub, puis un projet final complet.

HTML5CSS3JavaScriptMySQLPHPGitProjet final
HTML5

HTML5

Le point de départ de tout site web : structurer un document, écrire du texte, insérer des images, des listes, des tableaux et des formulaires avec un balisage propre et sémantique.

LEÇON 1
⏱ 45 minPrérequis : leçon précédente

Structure d'une page HTML

Objectif : comprendre le squelette obligatoire d'un document HTML.

1.1 — Le squelette de base

<!doctype html>
<html lang="fr">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>Ma page</title>
</head>
<body>
    <h1>Bonjour</h1>
</body>
</html>

1.2 — Rôle de chaque partie

<!doctype>Type de document
<head>Métadonnées, titre
<body>Contenu visible

1.3 — Attributs essentiels

L'attribut lang déclare la langue de la page, charset="utf-8" garantit l'affichage correct des accents, et la balise viewport rend la page utilisable sur mobile.

Pratique guidée

  1. Créer un fichier index.html.
  2. Écrire le squelette complet.
  3. Ajouter un titre et l'ouvrir dans le navigateur.

Validation

  • À quoi sert la balise <head> ?
  • Pourquoi déclarer charset="utf-8" ?
  • Que se passe-t-il sans la balise viewport sur mobile ?
Voir les réponses

head : métadonnées + titre, non visible. charset : accents corrects. Sans viewport : page minuscule/zoom sur mobile.

Exercice : créer une page HTML minimale avec un titre de page et un titre <h1> affichant votre prénom.
Voir le corrigé
<!doctype html>
<html lang="fr">
<head><meta charset="utf-8"><title>Rina</title></head>
<body><h1>Rina</h1></body>
</html>

Critères : doctype + html lang + head avec charset + title + h1 avec prénom.

LEÇON 2
⏱ 45 minPrérequis : leçon précédente

Textes et liens

Objectif : structurer du texte et créer des liens entre les pages.

2.1 — Titres et paragraphes

<h1>Titre principal</h1>
<h2>Sous-titre</h2>
<p>Un paragraphe de texte.</p>

2.2 — Mise en emphase

<p>Ceci est <strong>important</strong> et ceci est <em>en italique</em>.</p>

2.3 — Créer des liens

<a href="page2.html">Page suivante</a>
<a href="https://exemple.com" target="_blank">Site externe</a>
<a href="#contact">Aller au contact</a>

Un seul <h1> par page, puis des <h2>, <h3>… pour organiser une hiérarchie logique, sans sauter de niveau.

Validation

  • Différence entre strong et em ?
  • À quoi sert target="_blank" ?
  • Que fait un lien commençant par # ?
Voir les réponses

strong = importance (SEO/lecteur), em = emphase. target=_blank = nouvel onglet. #... = ancre dans la page.

Défi : créer une mini page « À propos » avec un titre, deux paragraphes et un lien vers une autre page.
Voir le corrigé
<h1>À propos</h1>
<p>Paragraphe 1...</p>
<p>Paragraphe 2...</p>
<a href="index.html">Accueil</a>
LEÇON 3
⏱ 45 minPrérequis : leçon précédente

Images et médias

Objectif : insérer des images, du son et de la vidéo dans une page.

3.1 — Insérer une image

<img src="photo.jpg" alt="Description de l'image" width="400">

L'attribut alt est obligatoire : il décrit l'image pour l'accessibilité et s'affiche si l'image ne charge pas.

3.2 — Vidéo et audio

<video src="demo.mp4" controls width="480"></video>
<audio src="intro.mp3" controls></audio>

3.3 — Image cliquable

<a href="index.html">
    <img src="logo.png" alt="Retour à l'accueil">
</a>

Validation

  • Pourquoi l'attribut alt est-il important ?
  • Que fait l'attribut controls sur une vidéo ?
Voir les réponses

alt : accessibilité + affiché si image cassée. controls : affiche play/volume/plein écran.

Exercice : créer une page « Galerie » avec trois images légendées et une vidéo de démonstration.
Voir le corrigé
<img src="img1.jpg" alt="Description 1" width="300">
<img src="img2.jpg" alt="Description 2" width="300">
<img src="img3.jpg" alt="Description 3" width="300">
<video src="demo.mp4" controls width="480"></video>

Chaque image a un alt différent et explicite.

LEÇON 4
⏱ 45 minPrérequis : leçon précédente

Listes et tableaux

Objectif : organiser des données sous forme de listes ou de tableaux.

4.1 — Listes

Non ordonnée

<ul>
    <li>HTML</li>
    <li>CSS</li>
</ul>

Ordonnée

<ol>
    <li>Analyser</li>
    <li>Développer</li>
</ol>

4.2 — Tableaux

<table>
    <tr><th>Cours</th><th>Durée</th></tr>
    <tr><td>HTML5</td><td>2 semaines</td></tr>
</table>

Un tableau HTML sert à afficher des données tabulaires, jamais à construire la mise en page d'un site.

Validation

  • Quand utiliser ul plutôt que ol ?
  • Différence entre th et td ?
Voir les réponses

ul = ordre sans importance, ol = étapes ordonnées. th = en-tête gras/centré, td = donnée.

Exercice : créer un tableau listant les modules de la formation avec leur durée approximative.
Voir le corrigé
<table>
 <tr><th>Cours</th><th>Durée</th></tr>
 <tr><td>HTML5</td><td>2 semaines</td></tr>
 <tr><td>CSS3</td><td>3 semaines</td></tr>
 <tr><td>JavaScript</td><td>4 semaines</td></tr>
</table>
LEÇON 5
⏱ 45 minPrérequis : leçon précédente

Formulaires

Objectif : collecter des informations saisies par l'utilisateur.

5.1 — Structure d'un formulaire

<form action="traitement.php" method="post">
    <label for="nom">Nom</label>
    <input type="text" id="nom" name="nom" required>

    <label for="email">Email</label>
    <input type="email" id="email" name="email" required>

    <button type="submit">Envoyer</button>
</form>

5.2 — Types de champs courants

text / email → saisie libre

checkbox → case à cocher

radio → choix unique

select → liste déroulante

5.3 — Validation native

<input type="text" required minlength="3">
<input type="number" min="0" max="100">

L'attribut for d'un <label> doit correspondre à l'id du champ pour que le clic sur le texte active le champ.

Validation

  • Différence entre GET et POST au niveau du formulaire ?
  • À quoi sert l'attribut required ?
Voir les réponses

GET = URL visible (recherche), POST = corps invisible (sensible). required = bloque l'envoi si vide.

Défi : créer un formulaire de contact complet (nom, email, message, case « j'accepte ») avec validation native.
Voir le corrigé
<form method="post">
 <label for="nom">Nom</label><input id="nom" name="nom" required minlength="3">
 <label for="email">Email</label><input id="email" type="email" name="email" required>
 <label for="msg">Message</label><textarea id="msg" name="msg" required></textarea>
 <label><input type="checkbox" required> J'accepte</label>
 <button>Envoyer</button>
</form>

🐞 Trouvez le bug — HTML

<img src="photo.jpg">
<a href="page.html">clique</a>
Voir la solution

1) alt manquant (accessibilité). 2) lien peu explicite. Corrige : alt="..." + texte « Voir la page cours ».

LEÇON 6
⏱ 45 minPrérequis : leçon précédente

HTML sémantique

Objectif : structurer une page avec des balises qui décrivent le sens du contenu.

6.1 — Balises sémantiques

<header>...</header>
<nav>...</nav>
<main>
    <section>...</section>
    <article>...</article>
</main>
<aside>...</aside>
<footer>...</footer>

6.2 — Pourquoi c'est important

<div> partout

Fonctionne visuellement mais n'indique aucun sens : mauvais pour l'accessibilité et le référencement.

Balises sémantiques

Décrivent la structure réelle : lecteurs d'écran et moteurs de recherche comprennent mieux la page.

C'est exactement cette structure (header, nav, main, section, footer) qui est utilisée dans ce cours depuis le cours HTML5.

Validation

  • Différence entre <section> et <div> ?
  • Pourquoi le HTML sémantique aide l'accessibilité ?
Voir les réponses

section = sens (chapitre), div = neutre. Sémantique = lecteurs d'écran + SEO comprennent la page.

Exercice : reprendre une page construite uniquement avec des <div> et la réécrire avec des balises sémantiques.
Voir le corrigé
<header><nav>...</nav></header>
<main><section><article>...</article></section></main>
<footer>...</footer>

Remplace chaque div générique par header/nav/main/section/article/footer selon le sens.

✅ Checklist — HTML5 (je sais faire seul)

PROJET HTML

Site vitrine

Construire, en HTML seul, la structure complète d'un site vitrine prêt à être stylisé dans le cours CSS3.

Contraintes obligatoires

  • Structure sémantique complète (header, nav, main, section, footer).
  • Au moins une page avec un formulaire de contact fonctionnel (attributs de validation).
  • Images avec attribut alt sur chaque page.
  • Une liste et un tableau utilisés à bon escient.
  • Navigation avec des liens internes (ancres) et vers d'autres pages.
  • Aucun style CSS à ce stade : uniquement du HTML propre et valide.

Structure sémantique30%

Formulaire et validation native20%

Images et médias15%

Listes et tableaux15%

Navigation et liens10%

Validité du code HTML10%

OBJECTIF

De HTML simple à une interface Web responsive

L'élève apprend à transformer une structure HTML en interface propre et responsive. Chaque leçon suit : explication → exemple → pratique → validation → défi.

Comprendre

Syntaxe, sélecteurs, cascade, Box Model et mise en page.

Construire

Utiliser Flexbox, Grid, media queries et animations.

Pratiquer

Réaliser des exercices puis un projet final sans framework CSS.

LEÇON 1
⏱ 45 minPrérequis : leçon précédente

Premiers styles CSS

Objectif : comprendre le rôle de CSS, les méthodes d'intégration et la syntaxe.

1.1 — CSS, c'est quoi ?

CSS signifie Cascading Style Sheets. HTML structure le contenu ; CSS contrôle principalement sa présentation : couleurs, tailles, espacements, bordures, positionnement et mise en page.

HTMLStructure
CSSPrésentation
JavaScriptComportement

1.2 — Trois méthodes

Inline

<p style="color:red">Bonjour</p>

Rapide pour un test, mais à éviter pour un projet important.

Interne

<style>
p { color:red; }
</style>

Pratique pour une petite page ou une démonstration.

Externe — recommandée

<link rel="stylesheet" href="style.css">

Le fichier CSS peut être partagé par plusieurs pages.

1.3 — Syntaxe

p {
    color: blue;
    font-size: 20px;
}

p → sélecteur

color → propriété

blue → valeur

color: blue; → déclaration

1.4 — Commentaires

/* commentaire CSS */
.card { padding: 20px; }

Pratique guidée

  1. Créer index.html et style.css.
  2. Relier les fichiers.
  3. Créer titre, paragraphes et bouton.
  4. Modifier couleurs, tailles et espacements.

Validation

  • Quelle différence entre HTML et CSS ?
  • Pourquoi préférer un CSS externe ?
  • Quelle différence entre propriété et valeur ?
Voir les réponses

HTML = structure, CSS = présentation. Externe = partagé, cache, maintenable. color:blue; = déclaration (propriété + valeur).

Exercice : créer une carte « Profil étudiant » entièrement stylisée dans style.css.
Voir le corrigé
.profil{background:#fff;border:1px solid #dbe3ee;border-radius:14px;padding:24px;max-width:320px}
.profil h2{margin:0}.profil p{color:#64748b}
LEÇON 2
⏱ 45 minPrérequis : leçon précédente

Sélecteurs et cascade

Objectif : sélectionner précisément les éléments et comprendre les conflits entre règles.

2.1 — Sélecteurs de base

p { color: blue; }       /* élément */
.important { color:red; } /* classe */
#contact { padding:20px; } /* ID */
* { box-sizing:border-box; } /* universel */

2.2 — Sélecteurs combinés

h1, h2, h3 { font-family: Arial; }
.card p { color:#475569; }
.card > h2 { margin-top:0; }
input[type="email"] { border:2px solid blue; }

2.3 — Pseudo-classes et pseudo-éléments

a:hover { text-decoration:underline; }
input:focus { outline:2px solid blue; }
li:first-child { font-weight:bold; }
li:nth-child(2) { color:blue; }

.title::after {
    content:"";
    display:block;
    width:50px;
    height:3px;
}

2.4 — Cascade et spécificité

Plusieurs règles peuvent cibler le même élément. Le navigateur tient compte notamment de l'importance, de la spécificité et de l'ordre des règles.

p { color:blue; }
.message { color:green; }
#special { color:red; }

<p id="special" class="message">Bonjour</p>

Fort : #id

Moyen : classe, attribut, pseudo-classe

Faible : élément

À spécificité égale, une règle déclarée plus tard peut gagner. !important doit rester exceptionnel.

Validation

  • Différence classe / ID ?
  • Que signifie .card p ?
  • Que signifie .card > p ?
  • Pourquoi #special gagne-t-il ?
Voir les réponses

Classe = réutilisable (.), ID = unique (#). .card p = tous descendants, .card > p = enfants directs. #special = spécificité max.

Défi : écrire plusieurs règles sur le même paragraphe et prédire la couleur finale avant d'ouvrir le navigateur.
Voir le corrigé

#special gagne (ID = spécificité forte). À spécificité égale, la dernière règle gagne. !important à éviter.

LEÇON 3
⏱ 45 minPrérequis : leçon précédente

Box Model

Objectif : comprendre content, padding, border et margin.

3.1 — Les quatre zones

margin
border
padding
content
  • Content : contenu.
  • Padding : espace intérieur.
  • Border : bordure.
  • Margin : espace extérieur.

3.2 — Exemple

.card {
    width: 300px;
    padding: 20px;
    border: 2px solid #334155;
    margin: 30px;
}

3.3 — box-sizing

* { box-sizing: border-box; }

Avec border-box, la largeur déclarée inclut contenu, padding et bordure.

3.4 — Dimensions, bordures et overflow

.card {
    max-width: 600px;
    min-height: 150px;
    border-radius: 12px;
    overflow: hidden;
}
Exercice : créer trois cartes de même largeur avec des padding, borders et margins différents. Observer le Box Model avec les DevTools.
Voir le corrigé
.card{width:300px;box-sizing:border-box}
.c1{padding:10px;border:2px solid}.c2{padding:20px;border:5px solid}.c3{padding:30px;border:10px solid}

Vérifie dans DevTools → onglet Computed → Box Model.

LEÇON 4
⏱ 45 minPrérequis : leçon précédente

Couleurs et typographie

Objectif : construire une identité visuelle lisible et cohérente.

4.1 — Formats de couleur

p { color: red; }
.box { color:#1d4ed8; background-color:rgb(241,245,249); }
.overlay { background-color:rgba(15,23,42,.8); }

4.2 — Variables CSS

:root {
    --primary:#2563eb;
    --text:#1e293b;
    --surface:#ffffff;
}
body { color:var(--text); }
.button { background:var(--primary); }

4.3 — Typographie

body {
    font-family:Arial, sans-serif;
    font-size:16px;
    line-height:1.6;
}
h1 { font-size:2.5rem; font-weight:700; }
p { letter-spacing:.01em; }

4.4 — Alignement et décoration

h1 { text-align:center; }
a { text-decoration:none; }
.upper { text-transform:uppercase; }

4.5 — Unités utiles

px — unité fixe

% — proportion

rem — relatif à la racine

em — relatif au contexte

vw / vh — fenêtre

Exercice : définir une palette avec couleur principale, texte, surface et accent, puis styliser titre, paragraphe et bouton.
Voir le corrigé
:root{--primary:#2563eb;--text:#1e293b;--surface:#fff;--accent:#22c55e}
h1{color:var(--text)}p{color:var(--text)}.btn{background:var(--primary);color:#fff}
LEÇON 5
⏱ 45 minPrérequis : leçon précédente

Flexbox

Objectif : aligner et distribuer des éléments sur une dimension principale.

5.1 — Activer Flexbox

.container { display:flex; }

5.2 — Direction et axes

.menu {
    display:flex;
    flex-direction:row;
    gap:20px;
}

Avec row, l'axe principal est horizontal ; avec column, il est vertical.

5.3 — justify-content et align-items

.menu {
    justify-content:space-between;
    align-items:center;
}

Valeurs courantes de justify-content : flex-start, center, flex-end, space-between, space-around, space-evenly.

5.4 — gap, wrap et flex

.cards {
    display:flex;
    flex-wrap:wrap;
    gap:20px;
}
.card { flex:1; }
Bloc 1
Bloc 2
Bloc 3

Pratique

  1. Créer une navigation Flexbox.
  2. Aligner logo et menu.
  3. Créer trois cartes avec gap.
  4. Ajouter flex-wrap.
Défi : centrer un bloc horizontalement et verticalement dans une zone de 300px.
Voir le corrigé
.zone{height:300px;display:flex;align-items:center;justify-content:center}
LEÇON 6
⏱ 45 minPrérequis : leçon précédente

CSS Grid

Objectif : créer des mises en page en lignes et colonnes.

6.1 — Activer Grid

.grid { display:grid; }

6.2 — Colonnes et gap

.grid {
    display:grid;
    grid-template-columns:repeat(3, 1fr);
    gap:20px;
}

6.3 — auto-fit et minmax

.gallery {
    display:grid;
    grid-template-columns:
        repeat(auto-fit, minmax(220px, 1fr));
    gap:20px;
}

6.4 — Faire occuper plusieurs colonnes

.featured { grid-column:span 2; }

6.5 — Flexbox ou Grid ?

Flexbox

Excellent pour un alignement principalement sur un axe.

Grid

Excellent pour une organisation en deux dimensions.

B
C
D
E
Exercice : créer une galerie de 8 cartes avec Grid et la rendre adaptable avec auto-fit/minmax.
Voir le corrigé
.galerie{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:20px}
LEÇON 7
⏱ 45 minPrérequis : leçon précédente

Responsive Design

Objectif : rendre une interface utilisable sur mobile, tablette et ordinateur.

7.1 — Viewport

<meta name="viewport" content="width=device-width, initial-scale=1.0">

7.2 — Media queries

@media (max-width:768px) {
    .layout { grid-template-columns:1fr; }
    .menu { flex-direction:column; }
}

7.3 — Mobile-first

.cards { grid-template-columns:1fr; }

@media (min-width:768px) {
    .cards { grid-template-columns:repeat(2,1fr); }
}

@media (min-width:1100px) {
    .cards { grid-template-columns:repeat(3,1fr); }
}

7.4 — Images flexibles

img {
    max-width:100%;
    height:auto;
}

7.5 — Méthode de test

  • Redimensionner la fenêtre.
  • Utiliser le mode appareil des DevTools.
  • Vérifier les débordements horizontaux.
  • Tester les champs et boutons sur mobile.
Défi : transformer une page desktop à trois colonnes en une page mobile à une colonne sans modifier le HTML.
Voir le corrigé
@media(max-width:768px){.layout{grid-template-columns:1fr}.menu{flex-direction:column}}

HTML inchangé : seule la CSS s'adapte.

🐞 Trouvez le bug — CSS

.card { width:300px; padding:30px; border:5px solid; }
Voir la solution

Sans box-sizing:border-box la carte fait 370px, pas 300. Ajoute *{box-sizing:border-box}.

LEÇON 8
⏱ 45 minPrérequis : leçon précédente

Transitions et animations

Objectif : ajouter des effets visuels simples, utiles et maîtrisés.

8.1 — Transition

.button {
    background:#2563eb;
    transition:background-color .2s ease, transform .2s ease;
}
.button:hover {
    background:#1d4ed8;
    transform:translateY(-2px);
}

8.2 — Transform

.card:hover { transform:translateY(-5px); }
.image:hover { transform:scale(1.05); }

Fonctions courantes : translate, scale, rotate, skew.

8.3 — @keyframes

@keyframes pulse {
    0% { transform:scale(1); }
    50% { transform:scale(1.08); }
    100% { transform:scale(1); }
}
.badge { animation:pulse 1.5s infinite; }
CSS3

8.4 — Accessibilité

Respecter les utilisateurs qui préfèrent réduire les animations.

@media (prefers-reduced-motion:reduce) {
    *,*::before,*::after {
        animation-duration:.01ms !important;
        transition-duration:.01ms !important;
    }
}
Défi : créer une carte avec hover et une animation d'apparition sobre.
Voir le corrigé
.carte{transition:transform .3s,box-shadow .3s}
.carte:hover{transform:translateY(-5px);box-shadow:0 14px 30px rgba(0,0,0,.12)}
@keyframes apparait{from{opacity:0;transform:translateY(12px)}to{opacity:1}}

✅ Checklist — CSS3 (je sais faire seul)

PROJET FINAL

Landing page responsive d'un centre de formation

Construire une page professionnelle comprenant hero, formations, avantages/statistiques, témoignages, appel à l'action et footer.

Contraintes obligatoires

  • HTML structuré et CSS externe.
  • Au moins 6 cartes de formations.
  • Classes réutilisables.
  • Flexbox et Grid utilisés.
  • Responsive mobile/tablette/desktop.
  • Variables CSS.
  • Au moins une transition et une animation.
  • Aucun framework CSS.
  • Tester avec les DevTools.
  • Chaque élève doit expliquer son code.

Grille d'évaluation

Structure HTML15%

Maîtrise CSS20%

Sélecteurs / Box Model15%

Flexbox / Grid15%

Responsive15%

Design / lisibilité10%

Explication / autonomie10%

Validation du module

L'élève doit être capable de prendre une page HTML simple et de produire seul une interface propre, responsive et cohérente en CSS avant de passer à JavaScript.

JavaScript

JavaScript

Donner vie aux pages HTML/CSS : variables, logique, tableaux, DOM, événements, formulaires, stockage local et communication avec une API.

LEÇON 1
⏱ 45 minPrérequis : leçon précédente

Variables et types

Objectif : déclarer des variables et distinguer les types primitifs de JavaScript.

1.1 — Déclarer une variable

let age = 20;
const nom = "Nina";
age = 21; // let peut être réassigné
// nom = "Léa"; // erreur : const est constant

On utilise let pour une valeur qui change et const par défaut. var est ancien et à éviter.

1.2 — Types primitifs

number → 12, 3.5

string → "bonjour"

boolean → true / false

undefined / null → valeur absente

1.3 — Opérateurs et template literals

let a = 5, b = 2;
console.log(a + b, a > b, a === "5");

const message = `${nom} a ${age} ans.`;

=== compare la valeur et le type, contrairement à ==.

Pratique guidée

  1. Créer un fichier script.js et le relier au HTML.
  2. Déclarer plusieurs variables de types différents.
  3. Les afficher avec un template literal dans console.log.

Validation

  • Différence entre let et const ?
  • Pourquoi éviter var ?
  • Différence entre === et == ?
Voir les réponses

let = réassignable, const = constant. var = fuites de scope. === = valeur + type.

Exercice : créer les variables d'une fiche produit (nom, prix, disponible) et les afficher avec un template literal.
Voir le corrigé
const nom="Ordinateur", prix=250000, disponible=true;
console.log(`${nom} : ${prix} Ar, dispo : ${disponible}`);
LEÇON 2
⏱ 45 minPrérequis : leçon précédente

Conditions

Objectif : exécuter du code différent selon une condition.

2.1 — if / else

if (age >= 18) {
    console.log("Majeur");
} else if (age >= 13) {
    console.log("Adolescent");
} else {
    console.log("Enfant");
}

2.2 — Opérateur ternaire

const statut = age >= 18 ? "Majeur" : "Mineur";

2.3 — switch

switch (niveau) {
    case "debutant": console.log("HTML5"); break;
    case "intermediaire": console.log("JavaScript"); break;
    default: console.log("Niveau inconnu");
}

Les opérateurs logiques &&, || et ! permettent de combiner plusieurs conditions.

Validation

  • Quand utiliser un ternaire plutôt qu'un if ?
  • Pourquoi le break est important dans un switch ?
Voir les réponses

Ternaire = 1 valeur courte. Sans break, tous les case suivants s'exécutent (fallthrough).

Défi : écrire une fonction qui affiche la mention (A, B, C, D) selon une note sur 20.
Voir le corrigé
function mention(n){if(n>=16)return "A";if(n>=14)return "B";if(n>=10)return "C";return "D"}
LEÇON 3
⏱ 45 minPrérequis : leçon précédente

Boucles et tableaux

Objectif : répéter des actions et manipuler des listes de données.

3.1 — Boucles

for (let i = 0; i < 5; i++) {
    console.log(i);
}

let n = 3;
while (n > 0) {
    console.log(n);
    n--;
}

3.2 — Créer et parcourir un tableau

const fruits = ["pomme", "banane", "mangue"];
console.log(fruits.length);
console.log(fruits[0]);

fruits.forEach(f => console.log(f));

3.3 — Méthodes utiles

push / map

fruits.push("kiwi");
const majuscules = fruits.map(f => f.toUpperCase());

filter / find

const longs = fruits.filter(f => f.length > 5);
const trouve = fruits.find(f => f === "kiwi");

Validation

  • Différence entre for et while ?
  • Que renvoie map par rapport à forEach ?
Voir les réponses

for = répétitions connues, while = condition. map retourne un nouveau tableau, forEach non.

Exercice : créer un tableau d'élèves et afficher uniquement ceux dont la note est ≥ 10 avec filter.
Voir le corrigé
const eleves=[{nom:"Rina",note:14},{nom:"Toto",note:8}];
console.log(eleves.filter(e=>e.note>=10));
LEÇON 4
⏱ 45 minPrérequis : leçon précédente

Fonctions et objets

Objectif : structurer du code réutilisable et regrouper des données liées.

4.1 — Déclarer une fonction

function addition(a, b) {
    return a + b;
}

const multiplier = (a, b) => a * b;

4.2 — Paramètres par défaut

function saluer(nom = "invité") {
    return `Bonjour ${nom}`;
}

4.3 — Objets littéraux

const etudiant = {
    nom: "Rina",
    note: 15,
    reussi() { return this.note >= 10; }
};
console.log(etudiant.nom, etudiant["note"], etudiant.reussi());

Validation

  • Différence entre function classique et fonction fléchée ?
  • Deux façons d'accéder à une propriété d'objet ?
Voir les réponses

Classique = this dynamique, fléchée = this lexical. Accès : obj.prop ou obj["prop"].

Défi : créer un objet "produit" (nom, prix, stock) avec une méthode qui indique s'il est en rupture de stock.
Voir le corrigé
const produit={nom:"Riz",prix:5000,stock:0,enRupture(){return this.stock===0}};
LEÇON 5
⏱ 45 minPrérequis : leçon précédente

Le DOM

Objectif : lire et modifier le contenu d'une page HTML depuis JavaScript.

5.1 — Sélectionner un élément

const titre = document.querySelector("h1");
const carte = document.getElementById("carte-1");
const items = document.querySelectorAll(".item");

5.2 — Modifier le contenu

titre.textContent = "Nouveau titre";
carte.innerHTML = "<strong>Mis à jour</strong>";

5.3 — Modifier des classes et du style

carte.classList.add("actif");
carte.classList.toggle("cachee");
carte.style.color = "blue";
SélectionnerquerySelector
Lire / ModifiertextContent, classList
CréercreateElement

Validation

  • Différence entre textContent et innerHTML ?
  • Pourquoi préférer classList à style direct ?
Voir les réponses

textContent = texte brut sûr, innerHTML = HTML (risque XSS). classList = sépare style et logique.

Exercice : sélectionner toutes les cartes d'une page et ajouter une classe "vu" au clic (préparation de la leçon suivante).
Voir le corrigé
document.querySelectorAll(".card").forEach(c=>c.addEventListener("click",()=>c.classList.add("vu")));
LEÇON 6
⏱ 45 minPrérequis : leçon précédente

Événements

Objectif : réagir aux actions de l'utilisateur (clic, saisie, soumission).

6.1 — addEventListener

const bouton = document.querySelector(".btn");
bouton.addEventListener("click", () => {
    console.log("Bouton cliqué");
});

6.2 — L'objet event

document.querySelector("form").addEventListener("submit", (e) => {
    e.preventDefault();
    console.log("Formulaire intercepté");
});

6.3 — Événements courants

click → clic souris

input → saisie en direct

submit → envoi de formulaire

keydown → touche pressée

Validation

  • À quoi sert preventDefault ?
  • Différence entre input et change ?
Voir les réponses

preventDefault = empêche recharge/soumission native. input = chaque frappe, change = à la sortie du champ.

Défi : ajouter un bouton qui incrémente un compteur affiché à l'écran à chaque clic.
Voir le corrigé
let compteur=0;btn.addEventListener("click",()=>{compteur++;affiche.textContent=compteur});

🐞 Trouvez le bug — JS

if (note == "10") { console.log("Admis"); }
Voir la solution

== convertit les types : "10abc" peut passer. Utilise === + Number().

LEÇON 7
⏱ 45 minPrérequis : leçon précédente

Validation des formulaires

Objectif : vérifier la saisie utilisateur avant l'envoi d'un formulaire.

7.1 — Lire les valeurs

const email = document.querySelector("#email").value;
const age = Number(document.querySelector("#age").value);

7.2 — Vérifier et afficher une erreur

form.addEventListener("submit", (e) => {
    e.preventDefault();
    if (!email.includes("@")) {
        erreur.textContent = "Email invalide";
        return;
    }
    erreur.textContent = "";
    console.log("Formulaire valide");
});

7.3 — Expressions régulières (introduction)

const regexEmail = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
console.log(regexEmail.test(email));

Validation

  • Pourquoi valider aussi côté serveur plus tard (module PHP) ?
  • Comment afficher un message d'erreur sans recharger la page ?
Voir les réponses

JS = contournable → PHP doit revalider. Erreur via textContent dans un <span> sans recharger.

Exercice : valider un formulaire d'inscription (nom, email, mot de passe ≥ 6 caractères) avec messages d'erreur par champ.
Voir le corrigé
if(nom.trim().length<3)errNom.textContent="Nom trop court";
if(!regexEmail.test(email))errEmail.textContent="Email invalide";
if(mdp.length<6)errMdp.textContent="6 caractères minimum";
LEÇON 8
⏱ 45 minPrérequis : leçon précédente

localStorage et JSON

Objectif : conserver des données côté navigateur entre deux visites.

8.1 — Stocker et lire une valeur simple

localStorage.setItem("theme", "sombre");
console.log(localStorage.getItem("theme"));
localStorage.removeItem("theme");

8.2 — JSON.stringify / JSON.parse

const taches = [{ texte: "Réviser CSS", faite: false }];
localStorage.setItem("taches", JSON.stringify(taches));

const stockees = JSON.parse(localStorage.getItem("taches"));

localStorage ne stocke que du texte : un tableau ou objet doit être converti en chaîne JSON avant d'être sauvegardé.

Validation

  • Pourquoi utiliser JSON.stringify avant de stocker un tableau ?
  • Que se passe-t-il si getItem ne trouve pas la clé ?
Voir les réponses

localStorage = texte seul → stringify obligatoire. getItem inexistant = null.

Défi : sauvegarder une liste de favoris dans localStorage et la recharger automatiquement à l'ouverture de la page.
Voir le corrigé
const fav=JSON.parse(localStorage.getItem("favoris")||"[]");
function sauver(){localStorage.setItem("favoris",JSON.stringify(fav))}
LEÇON 9
⏱ 45 minPrérequis : leçon précédente

fetch et API

Objectif : récupérer des données depuis un serveur ou une API externe.

9.1 — fetch avec then

fetch("https://api.exemple.com/produits")
    .then(reponse => reponse.json())
    .then(donnees => console.log(donnees))
    .catch(erreur => console.error(erreur));

9.2 — async / await

async function chargerProduits() {
    try {
        const reponse = await fetch("https://api.exemple.com/produits");
        const donnees = await reponse.json();
        console.log(donnees);
    } catch (erreur) {
        console.error("Erreur :", erreur);
    }
}

9.3 — Afficher les données dans le DOM

donnees.forEach(p => {
    liste.innerHTML += `<li>${p.nom} — ${p.prix} €</li>`;
});

Validation

  • Pourquoi async/await est-il plus lisible que .then imbriqués ?
  • Pourquoi toujours gérer les erreurs d'une requête réseau ?
Voir les réponses

async/await = lecture linéaire. Erreur réseau = page vide/crash → toujours try/catch.

Exercice : afficher dans une liste HTML les résultats d'une API publique (ex. liste de pays) avec fetch et async/await.
Voir le corrigé
async function charger(){try{const r=await fetch("https://restcountries.com/v3.1/all");const d=await r.json();liste.innerHTML=d.slice(0,10).map(p=>`<li>${p.name.common}</li>`).join("")}catch(e){console.error(e)}}

✅ Checklist — JavaScript (je sais faire seul)

PROJET JAVASCRIPT

Todo App

Construire une application de liste de tâches entièrement fonctionnelle côté navigateur, sans rechargement de page.

Contraintes obligatoires

  • Ajouter, cocher et supprimer une tâche.
  • Filtrer les tâches (toutes / en cours / terminées).
  • Sauvegarder la liste dans localStorage.
  • Recharger automatiquement les tâches à l'ouverture.
  • Validation de la saisie (pas de tâche vide).
  • Interface stylisée en réutilisant les acquis CSS3 (Flexbox/Grid, transitions).

Logique JavaScript30%

Manipulation du DOM20%

Gestion des événements20%

localStorage / JSON15%

Interface et responsive15%

MySQL

MySQL

Stocker et interroger des données comme un pro : créer une base, construire des tables liées, pratiquer le CRUD, calculer avec COUNT / AVG / SUM et relier les tables avec ou sans INNER JOIN.

LEÇON 1
⏱ 45 minPrérequis : leçon précédente

Créer une base de données

Objectif : créer une base, comprendre les types et créer des tables liées.

1.1 — Créer et utiliser une base

CREATE DATABASE formation CHARACTER SET utf8mb4;
USE formation;

1.2 — Table exemple : etudiants

CREATE TABLE etudiants (
    id INT AUTO_INCREMENT PRIMARY KEY,
    nom VARCHAR(100) NOT NULL,
    email VARCHAR(150) UNIQUE NOT NULL,
    age INT,
    note DECIMAL(4,2) DEFAULT 0,
    date_inscription DATE DEFAULT (CURRENT_DATE)
);

1.3 — Deuxième table : cours + liaison

CREATE TABLE cours (
    id INT AUTO_INCREMENT PRIMARY KEY,
    titre VARCHAR(150) NOT NULL,
    duree_heures INT DEFAULT 20,
    prix DECIMAL(8,2) DEFAULT 0
);

CREATE TABLE inscriptions (
    id INT AUTO_INCREMENT PRIMARY KEY,
    etudiant_id INT NOT NULL,
    cours_id INT NOT NULL,
    date_inscription DATE DEFAULT (CURRENT_DATE),
    FOREIGN KEY (etudiant_id) REFERENCES etudiants(id) ON DELETE CASCADE,
    FOREIGN KEY (cours_id) REFERENCES cours(id) ON DELETE CASCADE
);
etudiantsid, nom, email, note
coursid, titre, prix
inscriptionsetudiant_id, cours_id

Types à retenir : INT pour les nombres entiers, VARCHAR(n) pour le texte court, DECIMAL pour les prix/notes, DATE pour les dates, AUTO_INCREMENT pour un identifiant automatique.

Validation

  • Différence entre CREATE DATABASE et CREATE TABLE ?
  • À quoi sert PRIMARY KEY ?
  • Pourquoi mettre UNIQUE sur email ?
Voir les réponses

DATABASE = conteneur, TABLE = structure. PK = identifiant unique. UNIQUE = pas de doublon d'email.

Exercice : créez la base formation puis les 3 tables ci-dessus dans phpMyAdmin ou en ligne de commande. Vérifiez avec SHOW TABLES;
Voir le corrigé
CREATE DATABASE formation CHARACTER SET utf8mb4; USE formation;
-- puis les 3 CREATE TABLE du cours, puis SHOW TABLES; doit afficher 3 lignes.

🐞 Trouvez le bug — MySQL

DELETE FROM etudiants;
Voir la solution

Sans WHERE tu supprimes TOUTE la table ! Toujours DELETE ... WHERE id = ? + sauvegarde avant.

LEÇON 2
⏱ 45 minPrérequis : leçon précédente

CRUD avec une table exemple

Objectif : maîtriser Create, Read, Update, Delete sur la table etudiants.

2.1 — Create : INSERT

INSERT INTO etudiants (nom, email, age, note) VALUES
('Rina Rakoto', 'rina@ecole.mg', 21, 14.5),
('Léa Randria', 'lea@ecole.mg', 19, 16),
('Nina Razafy', 'nina@ecole.mg', 22, 9.5);

Create

INSERT INTO etudiants (nom, email)
VALUES ('Toto', 'toto@ecole.mg');

Read

-- tout lire
SELECT * FROM etudiants;
-- lire filtré et trié
SELECT id, nom, note FROM etudiants
WHERE note >= 10
ORDER BY note DESC;

Update

UPDATE etudiants
SET note = 12.5
WHERE id = 3;

Delete

DELETE FROM etudiants
WHERE id = 3;

2.3 — Filtres utiles du Read

SELECT * FROM etudiants WHERE age BETWEEN 18 AND 25;
SELECT * FROM etudiants WHERE nom LIKE 'R%';
SELECT * FROM etudiants WHERE id IN (1, 2, 4);
SELECT * FROM etudiants ORDER BY nom ASC LIMIT 5;

Validation

  • Quelle instruction correspond à chaque lettre de CRUD ?
  • Pourquoi oublier WHERE sur UPDATE / DELETE est dangereux ?
Voir les réponses

C=INSERT, R=SELECT, U=UPDATE, D=DELETE. Sans WHERE tu modifies/supprimes TOUTE la table !

Exercice : ajoutez 5 étudiants, affichez ceux qui ont ≥ 10, corrigez une note avec UPDATE, supprimez un étudiant de test avec DELETE.
Voir le corrigé
INSERT INTO etudiants(nom,email,age,note) VALUES (...5 lignes...);
SELECT * FROM etudiants WHERE note>=10;
UPDATE etudiants SET note=15 WHERE id=1;
DELETE FROM etudiants WHERE email='test@ecole.mg';
LEÇON 3
⏱ 45 minPrérequis : leçon précédente

Fonctions MySQL : COUNT, AVG, SUM et plus

Objectif : calculer des statistiques sur les données avec les fonctions d'agrégation.

3.1 — Les 5 fonctions incontournables

-- Nombre d'étudiants
SELECT COUNT(*) AS total FROM etudiants;
-- Moyenne des notes
SELECT AVG(note) AS moyenne FROM etudiants;
-- Somme des prix des cours
SELECT SUM(prix) AS chiffre FROM cours;
-- Meilleure et moins bonne note
SELECT MAX(note) AS meilleure, MIN(note) AS moins_bonne
FROM etudiants;

3.2 — GROUP BY : calculer par groupe

SELECT
    CASE WHEN note >= 10 THEN 'Admis' ELSE 'Ajourné' END AS resultat,
    COUNT(*) AS nb,
    AVG(note) AS moyenne
FROM etudiants
GROUP BY resultat;

3.3 — HAVING : filtrer après calcul

SELECT cours_id, COUNT(*) AS nb_inscrits
FROM inscriptions
GROUP BY cours_id
HAVING COUNT(*) > 5;

3.4 — Autres fonctions utiles

ROUND(AVG(note),2) → arrondir à 2 décimales

UPPER(nom) / LOWER(nom) → majuscules / minuscules

CONCAT(nom,' (',email,')') → assembler du texte

NOW() / CURDATE() → date et heure actuelles

SELECT nom, UPPER(nom) AS nom_maj, ROUND(note,1) AS note_arrondie
FROM etudiants;

SELECT titre, prix, prix * 1.2 AS prix_ttc
FROM cours;

Validation

  • Différence entre WHERE et HAVING ?
  • Pourquoi COUNT(*) et COUNT(note) peuvent différer si note est NULL ?
Voir les réponses

WHERE = avant calcul, HAVING = après GROUP BY. COUNT(note) ignore les NULL, COUNT(*) les compte.

Défi : affichez total, moyenne, meilleure et moins bonne note des étudiants admis (note ≥ 10) en une seule requête.
Voir le corrigé
SELECT COUNT(*) total, AVG(note) moy, MAX(note) maxi, MIN(note) mini
FROM etudiants WHERE note>=10;
LEÇON 4
⏱ 45 minPrérequis : leçon précédente

Jointures SANS INNER JOIN

Objectif : relier deux tables avec la syntaxe implicite (virgule + WHERE), encore très utilisée.

4.1 — Principe : lister puis filtrer

-- SANS le mot JOIN : on cite les 2 tables, on relie avec WHERE
SELECT etudiants.nom, cours.titre
FROM etudiants, inscriptions, cours
WHERE inscriptions.etudiant_id = etudiants.id
  AND inscriptions.cours_id = cours.id;

Sans le WHERE, MySQL fait un produit cartésien (chaque étudiant × chaque cours). Le WHERE garde uniquement les bonnes correspondances.

4.2 — Exemple complet avec alias

SELECT e.nom AS etudiant, c.titre AS cours, i.date_inscription
FROM etudiants e, inscriptions i, cours c
WHERE e.id = i.etudiant_id
  AND c.id = i.cours_id
ORDER BY e.nom;

4.3 — Avec COUNT et AVG

-- Nombre d'inscriptions par étudiant (sans JOIN)
SELECT e.nom, COUNT(*) AS nb_cours
FROM etudiants e, inscriptions i
WHERE e.id = i.etudiant_id
GROUP BY e.nom;

Validation

  • Que se passe-t-il si on oublie le WHERE dans une jointure implicite ?
  • À quoi servent les alias e, i, c ?
Voir les réponses

Sans WHERE = produit cartésien (explosion). Alias = raccourcis lisibles.

Exercice : affichez nom de l'étudiant + titre du cours avec la syntaxe à virgule, puis ajoutez la moyenne des notes par cours.
Voir le corrigé
SELECT e.nom,c.titre FROM etudiants e,inscriptions i,cours c
WHERE e.id=i.etudiant_id AND c.id=i.cours_id;
SELECT c.titre,AVG(e.note) FROM etudiants e,inscriptions i,cours c
WHERE e.id=i.etudiant_id AND c.id=i.cours_id GROUP BY c.titre;
LEÇON 5
⏱ 45 minPrérequis : leçon précédente

Jointures AVEC INNER JOIN (et LEFT JOIN)

Objectif : utiliser la syntaxe moderne explicite et comprendre LEFT JOIN.

5.1 — Même résultat, syntaxe moderne

-- AVEC INNER JOIN : relation écrite avec ON
SELECT e.nom AS etudiant, c.titre AS cours
FROM etudiants e
INNER JOIN inscriptions i ON e.id = i.etudiant_id
INNER JOIN cours c ON c.id = i.cours_id;
Sans INNER JOIN

FROM etudiants, inscriptions WHERE etudiants.id = inscriptions.etudiant_id. Court mais mélange filtres et relations.

Avec INNER JOIN

FROM etudiants INNER JOIN inscriptions ON .... Sépare la relation (ON) du filtre (WHERE) : plus lisible sur 3-4 tables.

5.2 — LEFT JOIN : garder même sans correspondance

-- Tous les étudiants, même ceux sans inscription
SELECT e.nom, c.titre
FROM etudiants e
LEFT JOIN inscriptions i ON e.id = i.etudiant_id
LEFT JOIN cours c ON c.id = i.cours_id;

INNER JOIN ne garde que les lignes qui correspondent des deux côtés. LEFT JOIN garde toutes les lignes de gauche, avec NULL à droite s'il n'y a rien.

5.3 — Statistique complète

SELECT c.titre,
       COUNT(i.id) AS nb_inscrits,
       ROUND(AVG(e.note),2) AS moyenne_notes
FROM cours c
LEFT JOIN inscriptions i ON c.id = i.cours_id
LEFT JOIN etudiants e ON e.id = i.etudiant_id
GROUP BY c.titre;

Validation

  • Quand préférer INNER JOIN à la syntaxe à virgule ?
  • Différence entre INNER JOIN et LEFT JOIN ?
Voir les réponses

INNER JOIN = lisible dès 3 tables (ON vs WHERE). INNER = correspondances seules, LEFT = garde la gauche + NULL.

Défi : réécrivez votre requête de la leçon 4 en INNER JOIN, puis faites une version LEFT JOIN pour voir les étudiants sans cours.
Voir le corrigé
SELECT e.nom,c.titre FROM etudiants e
INNER JOIN inscriptions i ON e.id=i.etudiant_id
INNER JOIN cours c ON c.id=i.cours_id;
SELECT e.nom,c.titre FROM etudiants e
LEFT JOIN inscriptions i ON e.id=i.etudiant_id
LEFT JOIN cours c ON c.id=i.cours_id;

✅ Checklist — MySQL (je sais faire seul)

PROJET MYSQL

Base Centre de formation

Créer de zéro une base complète et prouver qu'on sait l'interroger.

Contraintes obligatoires

  • Base formation avec tables etudiants, cours, inscriptions liées par clés étrangères.
  • Au moins 5 étudiants et 3 cours insérés.
  • CRUD complet testé sur etudiants.
  • Une requête avec COUNT, une avec AVG, une avec SUM.
  • Une jointure sans INNER JOIN (virgule + WHERE).
  • La même jointure réécrite avec INNER JOIN + une version LEFT JOIN.

Création base et tables25%

CRUD25%

Fonctions COUNT / AVG / SUM25%

Jointures avec et sans INNER JOIN25%

PHP

PHP

Passer du navigateur au serveur : traiter des formulaires, gérer des sessions, stocker des données dans une base MySQL et sécuriser une application.

LEÇON 1
⏱ 45 minPrérequis : leçon précédente

Premiers pas avec PHP

Objectif : écrire et exécuter du code PHP côté serveur.

1.1 — Syntaxe de base

<?php
$nom = "Rina";
$age = 21;
echo "Bonjour " . $nom;
?>

1.2 — Types et concaténation

$prix = 19.90;
$actif = true;
echo "Prix : " . $prix . " € (actif : " . var_export($actif, true) . ")";

1.3 — Mélanger HTML et PHP

<p>Bonjour <?= $nom ?>, vous avez <?= $age ?> ans.</p>

Un fichier .php doit être exécuté par un serveur (ex. via XAMPP/MAMP ou php -S localhost:8000), pas ouvert directement dans le navigateur.

Validation

  • Comment déclarer une variable en PHP ?
  • Différence entre echo et print ?
  • Que fait <?= ?> ?
Voir les réponses

$nom avec $. echo affiche, print retourne 1. <?= = echo court.

Exercice : créer une page qui affiche une fiche étudiant (nom, âge, moyenne) mélangeant HTML et PHP.
Voir le corrigé
<p>Bonjour <?=$nom?>, vous avez <?=$age?> ans, moyenne <?=$moy?></p>
LEÇON 2
⏱ 45 minPrérequis : leçon précédente

Fonctions et tableaux PHP

Objectif : structurer le code et manipuler des listes de données.

2.1 — Fonctions

function addition($a, $b) {
    return $a + $b;
}
echo addition(3, 4);

2.2 — Tableaux indexés et associatifs

$fruits = ["pomme", "banane", "mangue"];
$etudiant = ["nom" => "Léa", "note" => 15];

echo $fruits[0];
echo $etudiant["nom"];

2.3 — Parcourir un tableau

foreach ($fruits as $fruit) {
    echo $fruit . "<br>";
}

foreach ($etudiant as $cle => $valeur) {
    echo "$cle : $valeur<br>";
}

Validation

  • Différence entre tableau indexé et associatif ?
  • Pourquoi utiliser une fonction plutôt que copier le code ?
Voir les réponses

Indexé = [0,1,2], associatif = ["nom"=>...]. Fonction = réutilisable, testable, lisible.

Défi : créer un tableau associatif de produits et afficher un tableau HTML avec foreach.
Voir le corrigé
$produits=["Riz"=>5000,"Huile"=>12000];
foreach($produits as $n=>$p){echo "<tr><td>$n</td><td>$p</td></tr>";}
LEÇON 3
⏱ 45 minPrérequis : leçon précédente

GET et POST

Objectif : récupérer les données envoyées par un formulaire HTML.

3.1 — Formulaire HTML

<form method="post" action="traitement.php">
    <input type="text" name="nom">
    <button>Envoyer</button>
</form>

3.2 — Récupérer les données

$nom = $_POST["nom"] ?? "";
echo "Bonjour " . htmlspecialchars($nom);

3.3 — GET vs POST

GET

Données visibles dans l'URL, adaptées à une recherche ou un filtre.

POST

Données invisibles dans l'URL, adaptées à un formulaire sensible (connexion, inscription).

Validation

  • Pourquoi utiliser htmlspecialchars sur une donnée reçue ?
  • Quand préférer GET à POST ?
Voir les réponses

htmlspecialchars = anti-XSS. GET = recherche/filtre, POST = données sensibles.

Exercice : créer un formulaire de contact qui affiche un message de confirmation avec les données reçues en POST.
Voir le corrigé
$nom=htmlspecialchars($_POST["nom"]??"");
echo "Merci $nom, message reçu !";
LEÇON 4
⏱ 45 minPrérequis : leçon précédente

Sessions et authentification

Objectif : garder un utilisateur connecté d'une page à l'autre.

4.1 — Démarrer une session

<?php
session_start();
$_SESSION["utilisateur"] = "Rina";
?>

4.2 — Lire et détruire une session

session_start();
if (isset($_SESSION["utilisateur"])) {
    echo "Connecté en tant que " . $_SESSION["utilisateur"];
}

// déconnexion
session_destroy();

4.3 — Connexion simple

session_start();
if ($_POST["email"] === "test@ecole.com" && $_POST["mdp"] === "1234") {
    $_SESSION["connecte"] = true;
    header("Location: dashboard.php");
}

Validation

  • Pourquoi session_start() doit être appelé avant tout affichage ?
  • Comment protéger une page réservée aux membres connectés ?
Voir les réponses

session_start avant tout HTML sinon "headers already sent". Protège avec if(empty($_SESSION["connecte"])) redirect.

Défi : créer une page login.php et une page dashboard.php accessible uniquement si $_SESSION["connecte"] existe.
Voir le corrigé
// login.php : session_start(); if ok $_SESSION["connecte"]=true;
// dashboard.php : session_start(); if(empty($_SESSION["connecte"])){header("Location: login.php");exit;}
LEÇON 5
⏱ 45 minPrérequis : leçon précédente

MySQL et modèle relationnel

Objectif : concevoir des tables liées entre elles pour organiser des données.

5.1 — Créer une table

CREATE TABLE etudiants (
    id INT AUTO_INCREMENT PRIMARY KEY,
    nom VARCHAR(100) NOT NULL,
    email VARCHAR(150) UNIQUE
);

5.2 — Clé primaire et clé étrangère

CREATE TABLE inscriptions (
    id INT AUTO_INCREMENT PRIMARY KEY,
    etudiant_id INT,
    cours_id INT,
    FOREIGN KEY (etudiant_id) REFERENCES etudiants(id),
    FOREIGN KEY (cours_id) REFERENCES cours(id)
);
etudiantsid, nom, email
inscriptionsetudiant_id, cours_id
coursid, titre

Validation

  • À quoi sert une clé primaire ?
  • Pourquoi une table de liaison "inscriptions" plutôt qu'une colonne unique ?
Voir les réponses

PK = unicité. Table liaison = relation N-N (un étudiant → N cours).

Exercice : concevoir sur papier les tables etudiants, cours et inscriptions pour un centre de formation.
Voir le corrigé

etudiants(id PK, nom, email UNIQUE), cours(id PK, titre), inscriptions(id PK, etudiant_id FK, cours_id FK).

LEÇON 6
⏱ 45 minPrérequis : leçon précédente

PDO et requêtes préparées

Objectif : connecter PHP à MySQL de façon sûre et fiable.

6.1 — Se connecter avec PDO

$pdo = new PDO(
    "mysql:host=localhost;dbname=formation;charset=utf8",
    "root",
    ""
);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

6.2 — Requête préparée

$stmt = $pdo->prepare("SELECT * FROM etudiants WHERE id = ?");
$stmt->execute([$id]);
$etudiant = $stmt->fetch();

Une requête préparée sépare le code SQL des données envoyées : c'est la protection principale contre les injections SQL.

Validation

  • Pourquoi ne jamais coller directement une variable dans une requête SQL ?
  • Que fait execute() sur une requête préparée ?
Voir les réponses

Variable collée = injection SQL. execute() envoie les valeurs séparément, protégées.

Défi : se connecter à une base et afficher la liste des étudiants avec une requête préparée.
Voir le corrigé
$pdo=new PDO("mysql:host=localhost;dbname=formation;charset=utf8","root","");
$stmt=$pdo->prepare("SELECT * FROM etudiants");$stmt->execute();
foreach($stmt->fetchAll() as $e){echo htmlspecialchars($e["nom"])."<br>";}
LEÇON 7
⏱ 45 minPrérequis : leçon précédente

CRUD

Objectif : réaliser les quatre opérations de base sur une table : Create, Read, Update, Delete.

Create

$stmt = $pdo->prepare(
  "INSERT INTO etudiants (nom, email) VALUES (?, ?)"
);
$stmt->execute([$nom, $email]);

Read

$stmt = $pdo->query("SELECT * FROM etudiants");
$tous = $stmt->fetchAll();

Update

$stmt = $pdo->prepare(
  "UPDATE etudiants SET nom = ? WHERE id = ?"
);
$stmt->execute([$nouveauNom, $id]);

Delete

$stmt = $pdo->prepare(
  "DELETE FROM etudiants WHERE id = ?"
);
$stmt->execute([$id]);

Validation

  • Quelle instruction SQL correspond à chaque lettre de CRUD ?
  • Pourquoi toujours passer par une requête préparée, même pour un DELETE ?
Voir les réponses

C=INSERT, R=SELECT, U=UPDATE, D=DELETE. Préparée partout = injection impossible même en DELETE.

Exercice : créer les quatre pages (ajouter, lister, modifier, supprimer) pour la table etudiants.
Voir le corrigé

4 pages : ajouter (INSERT), lister (SELECT), modifier (UPDATE ... WHERE id=?), supprimer (DELETE ... WHERE id=?), toujours en préparée.

LEÇON 8
⏱ 45 minPrérequis : leçon précédente

POO PHP

Objectif : organiser le code autour de classes et d'objets réutilisables.

8.1 — Créer une classe

class Etudiant {
    public string $nom;
    public float $note;

    public function __construct(string $nom, float $note) {
        $this->nom = $nom;
        $this->note = $note;
    }

    public function aReussi(): bool {
        return $this->note >= 10;
    }
}

8.2 — Utiliser la classe

$e = new Etudiant("Rina", 14);
echo $e->nom . " : " . ($e->aReussi() ? "Admis" : "Ajourné");

Validation

  • À quoi sert le constructeur __construct ?
  • Différence entre une classe et un objet ?
Voir les réponses

__construct = initialise l'objet. Classe = moule, objet = exemplaire.

Défi : créer une classe Cours (titre, duree) avec une méthode qui affiche une description formatée.
Voir le corrigé
class Cours{public function __construct(public string $titre,public int $duree){}
public function description():string{return "$this->titre ($this->duree h)";}}

🐞 Trouvez le bug — PHP

$sql = "SELECT * FROM users WHERE email = '$email'";
Voir la solution

Injection SQL ! Utilise prepare("... WHERE email = ?") + execute([$email]).

LEÇON 9
⏱ 45 minPrérequis : leçon précédente

Sécurité

Objectif : connaître les failles courantes et les réflexes pour les éviter.

9.1 — Injection SQL

// Dangereux :
$sql = "SELECT * FROM users WHERE email = '$email'";
// Sûr :
$stmt = $pdo->prepare("SELECT * FROM users WHERE email = ?");
$stmt->execute([$email]);

9.2 — XSS et échappement

echo htmlspecialchars($commentaireUtilisateur);

9.3 — Mots de passe

$hash = password_hash($motDePasse, PASSWORD_DEFAULT);
// à la connexion :
password_verify($motDePasseSaisi, $hash);

Toujours : requêtes préparées

Toujours : htmlspecialchars à l'affichage

Jamais : mot de passe en clair

Validation

  • Pourquoi ne jamais stocker un mot de passe en clair ?
  • Comment se protéger d'une injection SQL ?
Voir les réponses

Clair = volé = compromis. Protection = préparée + htmlspecialchars + password_hash.

Exercice : reprendre le formulaire d'inscription du module PHP et sécuriser l'enregistrement du mot de passe avec password_hash.
Voir le corrigé
$hash=password_hash($_POST["mdp"],PASSWORD_DEFAULT);
$stmt=$pdo->prepare("INSERT INTO users(email,mdp) VALUES(?,?)");
$stmt->execute([$_POST["email"],$hash]);

✅ Checklist — PHP (je sais faire seul)

PROJET PHP/MYSQL

Student Management

Développer une application de gestion d'étudiants avec base de données, en appliquant le CRUD complet et les bonnes pratiques de sécurité.

Contraintes obligatoires

  • Base de données MySQL avec au moins deux tables liées.
  • Connexion via PDO et requêtes préparées.
  • CRUD complet sur les étudiants (ajout, liste, modification, suppression).
  • Formulaire de connexion avec session.
  • Mots de passe hashés avec password_hash.
  • Toutes les sorties utilisateur échappées avec htmlspecialchars.

Base de données et modèle relationnel20%

PDO et requêtes préparées20%

CRUD fonctionnel25%

Sessions / authentification15%

Sécurité20%

Git

Git & GitHub

Suivre l'historique d'un projet, travailler en branches et collaborer via GitHub comme dans un vrai environnement professionnel.

LEÇON 1
⏱ 45 minPrérequis : leçon précédente

Git et commits

Objectif : initialiser un dépôt et enregistrer l'historique d'un projet.

1.1 — Initialiser un dépôt

git init
git status

1.2 — Ajouter et committer

git add index.html style.css
git add .
git commit -m "Ajout de la page d'accueil"

1.3 — Historique

git log --oneline

Un commit est une photo datée et commentée du projet. Un message de commit clair explique ce qui a changé et pourquoi.

Validation

  • Différence entre git add et git commit ?
  • Pourquoi éviter les messages de commit du type "modif" ?
Voir les réponses

add = prépare, commit = photo. "modif" = inutile dans 6 mois, décris le pourquoi.

Exercice : initialiser un dépôt sur le projet CSS3 du module 2 et créer trois commits avec des messages clairs.
Voir le corrigé
git init; git add .; git commit -m "Base CSS3";
...modifs...; git commit -m "Ajoute Flexbox"; git commit -m "Ajoute Grid"; git log --oneline
LEÇON 2
⏱ 45 minPrérequis : leçon précédente

Branches et merge

Objectif : développer une fonctionnalité sans perturber le code principal.

2.1 — Créer et changer de branche

git branch feature-navbar
git checkout feature-navbar
# ou en une commande :
git checkout -b feature-navbar

2.2 — Fusionner une branche

git checkout main
git merge feature-navbar

2.3 — Bonnes pratiques

On garde main toujours stable et on développe chaque fonctionnalité dans sa propre branche, fusionnée une fois terminée et testée.

Validation

  • Pourquoi ne pas travailler directement sur main ?
  • Que fait git merge concrètement ?
Voir les réponses

main = stable. merge = fusionne la branche dans la courante.

Défi : créer une branche pour une nouvelle section de la page, la développer puis la fusionner dans main.
Voir le corrigé
git checkout -b feature-section; ...travail...; git add .; git commit -m "Ajoute section avis";
git checkout main; git merge feature-section
LEÇON 3
⏱ 45 minPrérequis : leçon précédente

GitHub — push / pull

Objectif : synchroniser un dépôt local avec un dépôt distant sur GitHub.

3.1 — Lier un dépôt distant

git remote add origin https://github.com/utilisateur/projet.git
git push -u origin main

3.2 — Récupérer les changements

git pull origin main

3.3 — Cloner un projet existant

git clone https://github.com/utilisateur/projet.git

Validation

  • Différence entre git push et git pull ?
  • Quand utiliser git clone plutôt que git init ?
Voir les réponses

push = envoie, pull = récupère. clone = copie un existant, init = crée un nouveau.

Exercice : créer un dépôt sur GitHub, y pousser le projet CSS3 puis vérifier le résultat en ligne.
Voir le corrigé
git remote add origin https://github.com/pseudo/projet.git; git push -u origin main

🐞 Trouvez le bug — Git

git commit -m "modif"
Voir la solution

Message inutile. Écris : git commit -m "Corrige le menu mobile (flex-direction)".

LEÇON 4
⏱ 45 minPrérequis : leçon précédente

Conflits et workflow

Objectif : résoudre un conflit de fusion et suivre un workflow d'équipe.

4.1 — Repérer un conflit

<<<<<<< HEAD
<h1>Bienvenue sur le site</h1>
=======
<h1>Accueil de la formation</h1>
>>>>>>> feature-navbar

Git ne sait pas laquelle des deux versions garder : il faut modifier le fichier à la main, choisir le contenu final puis committer.

4.2 — Workflow en équipe

Feature branch

Une branche par fonctionnalité, fusionnée après relecture.

Pull request

Proposition de fusion sur GitHub, discutée avant d'être acceptée.

Validation

  • Comment reconnaît-on un conflit dans un fichier ?
  • À quoi sert une pull request ?
Voir les réponses

Marqueurs <<</===/>>>. PR = demande de relecture avant fusion.

Défi : provoquer volontairement un conflit entre deux branches puis le résoudre proprement.
Voir le corrigé

Repère <<<<<<<, =======, >>>>>>>. Garde la bonne version, supprime les marqueurs, git add + commit.

✅ Checklist — Git (je sais faire seul)

PROJET GITHUB

Repository professionnel

Transformer un projet local en dépôt GitHub propre et présentable, comme dans une équipe de développement.

Contraintes obligatoires

  • Historique de commits clair et régulier.
  • Au moins une branche de fonctionnalité fusionnée via pull request.
  • Fichier .gitignore adapté au projet.
  • README.md décrivant le projet et son installation.
  • Dépôt public sur GitHub avec une description soignée.

Qualité des commits30%

Utilisation des branches25%

README et .gitignore20%

Organisation du dépôt25%

SUIVI

Mon tableau de compétences

Coche quand tu sais faire seul, sans copier. Ta progression est sauvegardée dans ton navigateur.

CompétenceVuPratiquéAutonome
Page HTML5 sémantique + formulaire
Interface CSS3 responsive (Flex/Grid)
Todo JS + localStorage + fetch
Base MySQL + CRUD + jointures
CRUD PHP/PDO sécurisé + sessions
Git : branches + GitHub + PR

0% autonome

PIÈGES

Erreurs fréquentes (à éviter)

Écran blanc PHP

session_start() après du HTML → "headers already sent". Toujours en 1ʳᵉ ligne.

DELETE sans WHERE

Supprime tout ! Toujours tester le SELECT d'abord.

Mot de passe en clair

Jamais ! password_hash + password_verify.

INNER JOIN qui multiplie

Vérifie tes ON et tes clés étrangères.

PRATIQUE

▶ Essaie sans installer

Écris du code et vois le résultat aussitôt. Aucune installation requise.

Éditeur HTML / CSS / JS en direct

Simulateur SQL (table etudiants)

Teste SELECT, COUNT, AVG, WHERE sur des données d'exemple.

Résultat ici...
ÉVALUATION

QCM finaux notés

3 questions par cours. Objectif : 3/3 = validé, sinon revois la leçon indiquée. Le score est calculé en direct.

QCM HTML5

  1. Quelle balise pour le contenu principal ?
  2. Attribut obligatoire d'une image ?
  3. Envoi sensible → méthode ?

QCM CSS3

  1. Centrer en Flexbox ?
  2. box-sizing:border-box fait quoi ?
  3. Responsive mobile ?

QCM JavaScript

  1. Différence === vs == ?
  2. Stocker un tableau en localStorage ?
  3. Empêcher la recharge d'un form ?

QCM MySQL

  1. Lettre R de CRUD ?
  2. Danger majeur ?
  3. Filtrer après GROUP BY ?

QCM PHP

  1. Anti-injection SQL ?
  2. Afficher une donnée utilisateur ?
  3. Stocker un mot de passe ?

QCM Git

  1. Enregistrer l'historique ?
  2. Fusionner une branche ?
  3. Conflit = ?

FORMATEUR

Suivi + attestation

Exporter la progression

Télécharge les cases cochées (compétences + checklists + QCM) en fichier texte pour le dossier de l'élève.

Attestation de réussite

Entre le nom de l'élève, génère une attestation à afficher.

ATTESTATION DE RÉUSSITE

Formation Développement Web — HTML5 • CSS3 • JavaScript • MySQL • PHP • Git

Progression validée sur la plateforme. Projets : site vitrine, landing responsive, Todo App, base MySQL, CRUD PHP sécurisé, dépôt GitHub.

PROJET FINAL

Centre de formation — de l'analyse à la présentation

Le projet final réunit tous les modules : CSS3, JavaScript, PHP/MySQL et Git. L'objectif est de livrer un site complet de centre de formation, avec un espace d'administration relié à une base de données.

Étape 1 — Analyse

Rédiger un cahier des charges simple : pages nécessaires, fonctionnalités, utilisateurs (visiteur / administrateur), puis dessiner un wireframe rapide de chaque page.

Étape 2 — Base de données

Concevoir le schéma MySQL (formations, étudiants, inscriptions), créer les tables et insérer quelques données de test.

Étape 3 — Développement

Construire le site avec HTML/CSS3 pour l'interface, JavaScript pour l'interactivité côté client, et PHP/PDO pour le CRUD des formations et des inscriptions.

Étape 4 — Tests

Vérifier chaque formulaire, tester le site sur mobile/tablette/desktop, corriger les erreurs de sécurité (requêtes préparées, échappement) et de responsive.

Étape 5 — Présentation finale

Déposer le projet sur GitHub avec un historique de commits propre, préparer une courte soutenance expliquant les choix techniques.

Grille d'évaluation

Analyse et conception10%

Base de données15%

Front-end (HTML/CSS3/JS)25%

Back-end (PHP/MySQL)25%

Sécurité et tests15%

Git/GitHub et présentation10%

Fin de la formation

L'élève est désormais capable de concevoir, développer et livrer seul une application web complète : interface responsive, logique côté client, back-end sécurisé et gestion de version professionnelle.