Aller au contenu

Un mot pour Fahmio

Tu écris. Tu continues.

Un problème, une idée — assez pour qu’on comprenne, puis tu retournes à ta copie.

On lit. Tu n’as rien d’autre à faire.

Où veux-tu aller ?

↑ ↓ pour parcourir · Entrée pour ouvrir · Échap pour fermer

Fahmio
Fahmio
Bac Tunisie
WB-HWeb et TIC

PHP/mysqli — compléter un script serveur depuis une liste, exploiter num_rows, affected_rows et fetch, prédire son comportement

Idée directrice

Au bac Sciences de l’informatique, le PHP ne s’écrit plus d’un bloc : un script serveur qui reçoit un formulaire est donné, souvent à trous. Il faut le compléter à partir d’une liste de fonctions, relier les clés de $_POST aux name des champs, choisir entre mysqli_num_rows, mysqli_affected_rows, mysqli_fetch_row et mysqli_fetch_array, puis prédire ce qu’affiche le script (require, or die, clé inexistante, résultat vide). C’est la lecture d’un flux : formulaire → $_POST → requête → résultat → echo. La notation attendue est mysqli_… ; les équivalents mysql_… restent acceptés.

Ce qui aide à reconnaître la démarche

« compléter la colonne « Fonction » en écrivant la fonction correspondante à partir de la liste » ; « Quel est le comportement du script dans le cas où … » ; « L’instruction de la ligne … peut être remplacée par » ; « Suite à l’exécution de l’instruction de la ligne …, la variable … contient » ; « compléter le fichier … par les instructions nécessaires à la récupération et la mise à jour des données »

Besoin d’un point de départ ? Voir la méthode
  • Relier chaque clé de $_POST ou $_GET à l’attribut name du champ (jamais à son id) ; une liste envoie la value de l’option choisie ; un span, un champ sans name ou une case non cochée n’envoient rien (tester une case par isset).
  • Enchaîner : connexion $con = mysqli_connect(hôte, utilisateur, mot de passe, base) or die(...) → requête construite dans une chaîne → mysqli_query($con, $req) → exploitation → mysqli_close($con). Les équivalents mysql_… sont acceptés.
  • Après un SELECT : mysqli_num_rows compte les lignes, mysqli_fetch_row (indices), mysqli_fetch_assoc (noms) ou mysqli_fetch_array (les deux) lisent une ligne à chaque tour de while. Après INSERT, UPDATE ou DELETE : mysqli_affected_rows, appliquée à la connexion.
  • Prédire : require d’un fichier absent arrête le script ; or die("…") affiche son message puis arrête ; dérouler ensuite les echo sur les extraits de tables.
  • Tout contrôle fait en JavaScript se refait dans le script PHP avant d’utiliser la donnée.
Les pièges à vérifier dans ta démarche

Compter les caractères d’une chaîne avec count (c’est strlen) ; appeler mysqli_query sans la connexion ; utiliser mysqli_num_rows après un UPDATE ou un DELETE ; lire $_POST["id du champ"] au lieu de son name ; attendre le texte d’une option au lieu de sa value ; croire qu’après or die le script continue ; lire $lig["champ"] sur un résultat de mysqli_fetch_row ; croire qu’un contrôle JavaScript protège la base.

Sujet principal

Exercice 4 — analyse et interprétation du code PHP, dans l’esprit des sessions 2022–2025

5 questionsCorrigé masqué

Le site VéloCité permet à un abonné de consulter l’historique de ses trajets d’un mois. La page historique.html (figure 1) envoie ses données au script historique.php (figure 2), incomplet, qui interroge la base de données « velocite » dont on donne des extraits (figure 3). La fonction JavaScript verifier(), non reproduite, refuse l’envoi du formulaire si le téléphone saisi ne comporte pas exactement 8 chiffres.

Figure 1 — code HTML de historique.html :

 1 <form id="f" name="f" action="historique.php" method="post" onsubmit="return verifier()">
 2   <label>N° d'abonné :</label>
 3   <input type="number" id="num" name="abonne"><br>
 4   <label>Téléphone :</label>
 5   <input type="text" id="telAb" name="tel"><br>
 6   <label>Mois :</label>
 7   <select id="mois" name="mois">
 8     <option value="">Choisir un mois</option>
 9     <option value="2026-01">Janvier 2026</option>
10     <option value="2026-02">Février 2026</option>
11     <option value="2026-03">Mars 2026</option>
12   </select><br>
13   <label>Dernier total affiché :</label> <span id="total">0.000</span> D<br>
14   <input type="submit" value="Afficher"> <input type="reset" value="Annuler">
15 </form>

Figure 2 — code incomplet de historique.php :

 1 <?php
 2 require("connexion.php");
 3 //Tâche N° 1 : Établir une connexion à la base de données.
 4 $con = ……①……($host, $user, $pass, $base) or die("Connexion impossible");
 5 $num = $_POST["abonne"];
 6 //Tâche N° 2 : Supprimer les espaces au début et à la fin du téléphone saisi.
 7 $tel = ……②……;
 8 $mois = $_POST["mois"];
 9 //Tâche N° 3 : Retourner la longueur de la chaîne "tel".
10 if (……③…… != 8)
11   die("Le téléphone doit comporter 8 chiffres.");
12 $req1 = "select nomAbonne, codeFormule from Abonne
13          where numAbonne = $num and telAbonne = '$tel'";
14 //Tâche N° 4 : Exécuter la requête "req1".
15 $res1 = ……④…… or die("Requête erronée : " . mysqli_error($con));
16 //Tâche N° 5 : Retourner le nombre d'enregistrements retournés par "req1".
17 if (……⑤…… == 0)
18   echo "Abonné inconnu ou téléphone erroné.";
19 else
20 {
21   //Tâche N° 6 : Retourner l'enregistrement sous forme d'un tableau indicé.
22   $ab = ……⑥……;
23   echo "<h2>Trajets de $ab[0] (formule $ab[1]) - $mois</h2>";
24   $req2 = "select dateTrajet, dureeMin, montant from Trajet
25            where numAbonne = $num and dateTrajet like '$mois%'
26            order by dateTrajet";
27   $res2 = mysqli_query($con, $req2) or die("Requête erronée");
28   $total = 0;
29   echo "<table border=1><tr><th>Date</th><th>Durée</th><th>Montant</th></tr>";
30   //Tâche N° 7 : Retourner un enregistrement accessible par les noms des champs.
31   while ($t = ……⑦……)
32   {
33     echo "<tr><td>" . $t["dateTrajet"] . "</td><td>" . $t["dureeMin"];
34     echo " min</td><td>" . $t["montant"] . "</td></tr>";
35     $total = $total + $t["montant"];
36   }
37   echo "</table><p>Total du mois : $total D</p>";
38 }
39 //Tâche N° 8 : Se déconnecter de la base de données.
40 ……⑧……;
41 ?>

N.B. : le fichier connexion.php définit les variables $host, $user, $pass et $base. Les fonctions mysqli_… admettent leurs équivalents mysql_… (mysql_connect / mysql_select_db, mysql_query, mysql_num_rows, mysql_fetch_row, mysql_fetch_array, mysql_close). Comme dans les sujets, une requête qui échoue fait retourner false à mysqli_query.

Figure 3 — extraits des tables (la table Trajet ci-dessous contient tous les trajets de 2026 des abonnés 1001 et 1004) :

Abonne

numAbonnenomAbonnetelAbonnecodeFormule
1001Amel Karray20111222F2
1002Hedi Ellouze22333444F3
1004Karim Jmal98777888F2

Trajet

idTrajetdateTrajetdureeMinmontantidVelonumAbonne
82026-01-09150.6001061001
92026-01-21555.5001051004
112026-03-15412.0501021004
122026-03-16607.2001041001
132026-03-10381.5201061003
142026-02-27703.5001011004
152026-03-21303.0001051004
162026-03-28251.2501021004

1. Pour chaque ligne de tâche manquante, compléter le tableau en écrivant la fonction correspondante à partir de la liste suivante : mysqli_num_rows($res1) | count($tel) | mysqli_connect | trim($_POST["tel"]) | mysqli_affected_rows($con) | mysqli_fetch_row($res1) | mysqli_query($req1) | mysqli_close($con) | strlen($tel) | mysqli_fetch_rows($res1) | mysqli_query($con, $req1) | mysqli_fetch_array($res2).

TâcheFonction
① (ligne 4)
② (ligne 7)
③ (ligne 10)
④ (ligne 15)
⑤ (ligne 17)
⑥ (ligne 22)
⑦ (ligne 31)
⑧ (ligne 40)

2. L’abonné saisit le numéro 1004, le téléphone 98777888, choisit « Mars 2026 », puis clique sur « Afficher ». On considère le script complété.
a. Compléter le tableau suivant.

Variable du scriptLigne du champ dans historique.htmlValeur reçue
$num (ligne 5) | |
$tel (ligne 7) | |
$mois (ligne 8) | |


b. Pourquoi la ligne 5 lit-elle $_POST["abonne"] et non $_POST["num"] ?
c. Le script peut-il récupérer le « Dernier total affiché » (ligne 13 de historique.html) par $_POST["total"] ? Justifier.
d. Si l’attribut method de la ligne 1 valait "get", quelles modifications faudrait-il apporter aux lignes 5, 7 et 8 du script ? Quelle différence l’abonné verrait-il dans la barre d’adresse de son navigateur ?

3. L’administrateur du site utilise le script tarifs.php suivant (figure 4) pour augmenter le tarif à la minute des vélos électriques.

Figure 4 — code incomplet de tarifs.php :

 1 <?php
 2 require("connexion.php");
 3 $con = mysqli_connect($host, $user, $pass, $base) or die("Connexion impossible");
 4 $hausse = $_POST["hausse"];
 5 $req = "update Modele set tarifMinute = tarifMinute + $hausse
 6         where typeModele = 'Electrique'";
 7 mysqli_query($con, $req) or die("Mise à jour impossible");
 8 echo ……………………… . " modèle(s) modifié(s)";
 9 mysqli_close($con);
10 ?>

Extrait de la table Modele

codeModelelibModeletypeModeletarifMinute
M1Urbain 3VClassique0.040
M2Urbain 7VClassique0.050
M5E-CityElectrique0.100
M6E-TrekElectrique0.120

a. Parmi mysqli_num_rows($res), mysqli_affected_rows($con) et count($req), quelle expression faut-il écrire à la ligne 8 pour afficher le nombre de modèles dont le tarif a été modifié, sachant que $con désigne la connexion ?
b. L’administrateur envoie la valeur 0.010 dans le champ hausse. Donner le message affiché et les nouveaux tarifs des modèles concernés.
c. Pourquoi la fonction mysqli_num_rows ne convient-elle pas ici ?

4. Pour chacune des questions suivantes, mettre une croix dans la case correspondant à la réponse correcte (script historique.php complété).

  1. Si le fichier « connexion.php » appelé à la ligne 2 est inexistant : ☐ une erreur est générée et le script s’arrête ☐ une erreur est générée et le script continue à s’exécuter ☐ le script se connecte avec des paramètres par défaut
  2. En cas d’erreur de syntaxe dans la requête des lignes 12 et 13, le script : ☐ affiche « Connexion impossible » et s’arrête ☐ affiche « Requête erronée : » suivi du message d’erreur renvoyé par MySQL, et s’arrête ☐ affiche « Abonné inconnu ou téléphone erroné. »
  3. Si on remplace, à la ligne 22, mysqli_fetch_row($res1) par mysqli_fetch_array($res1) : ☐ une erreur de syntaxe est générée ☐ le comportement du script ne change pas ☐ le nom de l’abonné n’est plus affiché
  4. Si on remplace, à la ligne 31, mysqli_fetch_array($res2) par mysqli_fetch_row($res2) : ☐ le comportement du script ne change pas ☐ le tableau affiche des lignes aux cases vides (clés inexistantes) et le total vaut 0 ☐ la boucle while ne s’exécute plus
  5. La fonction mysqli_error (ligne 15) retourne : ☐ une chaîne contenant le message d’erreur de la dernière requête ☐ un tableau contenant la liste des erreurs ☐ le numéro de la ligne erronée du script

5. Donner ce qu’affiche le script complété dans chacun des cas suivants :
a. l’exemple de la question 2 ;
b. numéro 1004, téléphone 98777880, mois « Mars 2026 » ;
c. numéro 1001, téléphone 20111222, mois « Février 2026 ».

6. La fonction verifier() refuse déjà un téléphone qui ne comporte pas 8 chiffres. Expliquer pourquoi le script refait ce contrôle aux lignes 9 à 11, avant de construire la requête.

Voir la correction commentéeAprès avoir posé votre démarche

1. Idée : lire le commentaire de chaque tâche, reconnaître la famille (connexion, chaîne, requête, résultat), puis vérifier les paramètres : mysqli_query prend la connexion et la requête, strlen s’applique à une chaîne, count à un tableau.

TâcheFonction
①mysqli_connect
②trim($_POST["tel"])
③strlen($tel)
④mysqli_query($con, $req1)
⑤mysqli_num_rows($res1)
⑥mysqli_fetch_row($res1)
⑦mysqli_fetch_array($res2)
⑧mysqli_close($con)

Les quatre autres propositions sont écartées : mysqli_fetch_rows n’existe pas ; count($tel) compte les éléments d’un tableau, pas les caractères d’une chaîne ; mysqli_query($req1) oublie la connexion $con ; mysqli_affected_rows($con) compte les lignes touchées par INSERT, UPDATE ou DELETE, pas celles d’un SELECT.

En syntaxe mysql_…, également acceptée, la connexion s’écrit mysql_connect($host, $user, $pass) suivie de mysql_select_db($base), puis mysql_query($req1), mysql_num_rows($res1), mysql_fetch_row($res1), mysql_fetch_array($res2) et mysql_close().

2.a Idée : une clé de $_POST est la valeur de l’attribut name d’un champ ; une liste déroulante envoie la value de l’option choisie.

Variable du scriptLigne du champValeur reçue
$num | 3 (name="abonne") | "1004"
$tel | 5 (name="tel") | "98777888"
$mois | 7 (<select name="mois">) | "2026-03" (la value de l’option, pas le texte « Mars 2026 »)

2.b Le navigateur envoie chaque champ sous le nom donné par son attribut name : le champ de la ligne 3 a name="abonne". L’attribut id="num" ne sert qu’au JavaScript (getElementById) et au CSS : la clé $_POST["abonne"] existe, $_POST["num"] n’existe pas.

2.c Non. Un <span> n’est pas un champ de formulaire : seuls les input, select et textarea qui ont un name sont envoyés. $_POST["total"] n’existe pas (avertissement « clé indéfinie », valeur vide). Pour transmettre ce total, il faudrait un champ, par exemple <input type="hidden" name="total">, que le script JavaScript remplirait ; le script PHP pourrait alors lire $_POST["total"].

2.d Il faudrait lire $_GET["abonne"] (ligne 5), trim($_GET["tel"]) (ligne 7) et $_GET["mois"] (ligne 8), au lieu de $_POST[…]. Avec GET, les données sont ajoutées à l’adresse : la barre d’adresse afficherait historique.php?abonne=1004&tel=98777888&mois=2026-03, téléphone compris ; avec POST, elles voyagent dans le corps de la requête HTTP.

3.a Idée : UPDATE ne renvoie pas de lignes ; ce qui compte, ce sont les lignes touchées, que donne la connexion $con. Ligne 8 : echo mysqli_affected_rows($con) . " modèle(s) modifié(s)";

3.b Seuls les modèles de type Electrique sont modifiés : M5 (E-City) passe de 0.100 à 0.110 D/min et M6 (E-Trek) passe de 0.120 à 0.130 D/min. Le script affiche « 2 modèle(s) modifié(s) » ; les modèles Classique gardent leur tarif.

3.c mysqli_num_rows compte les lignes d’un résultat de SELECT. Pour un UPDATE, mysqli_query ne renvoie que true ou false (il n’y a pas de jeu de résultats, d’ailleurs le script ne garde aucune variable $res) : le nombre de lignes modifiées se lit sur la connexion, par mysqli_affected_rows($con).

4. Idée : require arrête le script si le fichier manque ; or die(...) affiche son message puis arrête ; fetch_row ne donne que des indices, fetch_array des indices et des noms.

  • 1. Une erreur est générée et le script s’arrête : require provoque une erreur fatale (include aurait seulement produit un avertissement).
  • 2. « Requête erronée : » suivi du message d’erreur renvoyé par MySQL, et arrêt : mysqli_query retourne false, donc la partie or die(...) de la ligne 15 s’exécute.
  • 3. Le comportement ne change pas : mysqli_fetch_array retourne un tableau accessible par indices et par noms, $ab[0] et $ab[1] existent toujours.
  • 4. Le tableau affiche des lignes aux cases vides et le total vaut 0 : mysqli_fetch_row ne crée que les clés 0, 1, 2 ; $t["dateTrajet"], $t["dureeMin"] et $t["montant"] n’existent pas (valeur vide, avec avertissement : la colonne Durée n’affiche plus que « min »), et $total reste à 0 (0 + une valeur vide donne 0). La boucle, elle, s’exécute autant de fois.
  • 5. Une chaîne contenant le message d’erreur de la dernière requête (le code numérique serait donné par mysqli_errno).

5.a Idée : dérouler les echo dans l’ordre, en appliquant chaque requête aux extraits de tables. Le téléphone a 8 caractères ; la requête des lignes 12–13 trouve l’abonné 1004 (1 ligne). La requête des lignes 24–26 retient ses trajets dont la date commence par « 2026-03 », triés par date : seuls les trajets 11, 15, 16 vérifient les deux conditions. Le navigateur affiche :

Trajets de Karim Jmal (formule F2) - 2026-03

DateDuréeMontant
2026-03-1541 min2.050
2026-03-2130 min3.000
2026-03-2825 min1.250


Total du mois : 6.3 D

$total est un nombre : 2.050 + 3.000 + 1.250 = 6.3, affiché 6.3 (PHP n’écrit pas les zéros inutiles), alors que chaque $t["montant"] est affiché tel que MySQL le renvoie, avec trois décimales.

5.b Aucun abonné n’a le numéro 1004 et le téléphone 98777880 : mysqli_num_rows retourne 0. Le script affiche seulement « Abonné inconnu ou téléphone erroné. », puis ferme la connexion (ligne 40).

5.c L’abonné 1001 existe (1 ligne), mais il n’a aucun trajet en février 2026 : la boucle while ne s’exécute pas. Le script affiche le titre « Trajets de Amel Karray (formule F2) - 2026-02 », la ligne d’en-tête du tableau (Date, Durée, Montant) sans aucune ligne de trajet, puis « Total du mois : 0 D ».

6. Idée : tout ce qui s’exécute dans le navigateur peut être contourné ; le serveur ne doit jamais faire confiance aux données reçues. verifier() s’exécute sur le poste de l’utilisateur : il suffit de désactiver JavaScript, de modifier la page ou d’envoyer directement une requête à historique.php pour que le contrôle n’ait pas lieu. Le script refait donc le test côté serveur, avant de placer le téléphone dans la requête, pour ne jamais interroger la base avec une donnée non contrôlée. Le contrôle JavaScript reste utile : il évite un aller-retour inutile vers le serveur.

Changer d’énoncé, garder le raisonnement

Choisis un seul transfert après le sujet principal. Qu’est-ce qui change ? Quel résultat ou quelle méthode peux-tu réutiliser ? Les autres exercices restent disponibles pour une prochaine séance.

Variantes rencontrées : compléter huit tâches à partir d’une liste de douze fonctions (mysqli_connect, count, strlen, substr, mysqli_query, mysqli_num_rows, mysqli_fetch_array, mysqli_close) (contrôle 2025) ; QCM sur la value d’une option envoyée, un span non envoyé, date(), or die, fetch_row, affected_rows et mysqli_error (principale 2025) ; comportement du script si connexion.php manque, str_replace, or die et $lig[0] au lieu de $lig["idBar"] (principale 2024) ; trace d’une fonction PHP (variables $i, $trouve et $unMot) (contrôle 2024) ; compléter la récupération des données du formulaire et la requête UPDATE de modifier.php (principale 2022) ; valeurs de variables après trim, substr, strpos et isset sur les clés de $_POST dans une boucle (contrôle 2026) ; V/F sur la concaténation, strtolower/trim, num_rows et substr (principale 2026). Cette trajectoire remplace WB-C (script complet en mysql_… d’un devoir de 2015–2016) : les épreuves nationales font compléter et interpréter un script mysqli, et acceptent les équivalents mysql_… (principale 2024).

Exercices d'entraînement — une nuance à la fois

1 Drill WB-H.1 — lire un résultat : mysqli_fetch_row (indices), fetch_assoc (noms), fetch_array (les deux)
Entraînement 01 / 04

Drill WB-H.1 — lire un résultat : mysqli_fetch_row (indices), fetch_assoc (noms), fetch_array (les deux)

1 questionCorrigé masqué

Le script station.php du site VéloCité affiche les vélos d’une station. Il est appelé par le lien station.php?station=3. Extraits des tables :

Velo

idVelodateServicecodeModeleidStation
1012017-05-10M11
1022018-03-02M22
1032021-06-15M53
1042023-09-01M63
1052024-02-20M52
1062019-11-11M14

Modele

codeModelelibModeletypeModeletarifMinute
M1Urbain 3VClassique0.040
M2Urbain 7VClassique0.050
M5E-CityElectrique0.100
M6E-TrekElectrique0.120
 1 <?php
 2 require("connexion.php");
 3 $con = mysqli_connect($host, $user, $pass, $base) or die("Erreur B.D.");
 4 $st = $_GET["station"];
 5 $req = "select idVelo, libModele, typeModele from Velo V, Modele M
 6         where V.codeModele = M.codeModele and idStation = $st
 7         order by idVelo";
 8 $res = mysqli_query($con, $req) or die("Requête erronée");
 9 while ($v = mysqli_fetch_row($res))
10   echo "$v[0] : $v[1] ($v[2])<br>";
11 mysqli_close($con);
12 ?>

1. Donner l’affichage produit par le script.

2. Donner le contenu (clés et valeurs) du tableau retourné par mysqli_fetch_row au premier tour de la boucle (ligne 9).

3. Pour chacune des propositions suivantes, qui remplacent les lignes 9 et 10, répondre par V si l’affichage reste identique ou par F dans le cas contraire.

Proposition 1 :

while ($v = mysqli_fetch_array($res))
  echo "$v[0] : $v[1] ($v[2])<br>";


Proposition 2 :

while ($v = mysqli_fetch_assoc($res))
  echo "$v[0] : $v[1] ($v[2])<br>";


Proposition 3 :

while ($v = mysqli_fetch_row($res))
  echo $v["idVelo"] . " : " . $v["libModele"] . " (" . $v["typeModele"] . ")<br>";


Proposition 4 :

while ($v = mysqli_fetch_array($res))
  echo $v["idVelo"] . " : " . $v["libModele"] . " (" . $v["typeModele"] . ")<br>";

4. Pourquoi la ligne 4 lit-elle $_GET["station"] et non $_POST["station"] ?

Voir la correction commentéeAprès avoir posé votre démarche

1. Idée : appliquer la requête aux extraits, puis dérouler un echo par ligne du résultat. Les vélos de la station 3 sont 103 (M5) et 104 (M6) ; la jointure donne leur libellé et leur type. Affichage :

103 : E-City (Electrique)

104 : E-Trek (Electrique)

2. mysqli_fetch_row retourne un tableau indicé : [0 => "103", 1 => "E-City", 2 => "Electrique"], dans l’ordre des champs du SELECT. Il n’a pas de clé "idVelo" (les valeurs arrivent sous forme de chaînes).

3. Idée : pour chaque proposition, se demander quelles clés crée la fonction (indices, noms ou les deux) et quelles clés lit l’echo.

  • Proposition 1 : V — fetch_array crée les indices 0, 1, 2 en plus des noms.
  • Proposition 2 : F — fetch_assoc ne crée que les clés nommées : les clés 0, 1 et 2 n’existent pas, chaque ligne affiche « : () » avec des avertissements.
  • Proposition 3 : F — fetch_row ne crée que les indices : les clés "idVelo", "libModele" et "typeModele" n’existent pas.
  • Proposition 4 : V — fetch_array crée aussi les clés nommées, qui correspondent aux noms des champs du SELECT.

4. La station est transmise dans l’adresse (?station=3) : c’est une requête GET, lue par $_GET. $_POST ne contient que les données d’un formulaire envoyé avec method="post".

2 Drill WB-H.2 — compter : mysqli_num_rows après un SELECT, mysqli_affected_rows après un DELETE
Entraînement 02 / 04

Drill WB-H.2 — compter : mysqli_num_rows après un SELECT, mysqli_affected_rows après un DELETE

1 questionCorrigé masqué

Dans l’atelier VéloCité, le formulaire de la page nettoyer.html envoie, par la méthode POST, le numéro d’un vélo (champ nommé velo) et une date (champ nommé avant) au script nettoyer.php. Celui-ci affiche le nombre de réparations du vélo, puis supprime celles qui sont antérieures à la date donnée. Extrait de la table Reparation :

idVelodateReppannecout
1012025-03-01Chaîne cassée18.000
1032025-08-12Batterie120.000
1042025-10-05Frein avant25.000
1012026-01-15Pneu crevé12.000
1052026-02-02Écran de bord85.000
 1 <?php
 2 require("connexion.php");
 3 $con = mysqli_connect($host, $user, $pass, $base) or die("Erreur B.D.");
 4 $v = $_POST["velo"];
 5 $d = $_POST["avant"];
 6 $res1 = mysqli_query($con, "select * from Reparation where idVelo = $v");
 7 echo "Le vélo $v a " . mysqli_num_rows($res1) . " réparation(s).<br>";
 8 $res2 = mysqli_query($con, "delete from Reparation
 9                             where idVelo = $v and dateRep < '$d'");
10 if ($res2)
11   echo mysqli_affected_rows($con) . " réparation(s) supprimée(s).";
12 else
13   echo "Suppression impossible : " . mysqli_error($con);
14 mysqli_close($con);
15 ?>

1. Donner l’affichage produit pour velo = 101 et avant = 2026-01-01.

2. Le technicien renvoie aussitôt le même formulaire (mêmes valeurs). Donner le nouvel affichage.

3. Donner l’affichage pour velo = 102 et avant = 2026-01-01. Le message de la ligne 13 est-il affiché ? Justifier.

4. Répondre par V (vrai) ou F (faux).
a. La ligne 11 peut être remplacée par echo mysqli_num_rows($res2) . " réparation(s) supprimée(s).";
b. La ligne 7 peut être remplacée par echo "Le vélo $v a " . count($res1) . " réparation(s).<br>";
c. Le test de la ligne 10 est vrai dès que la requête DELETE est correcte, même si aucune ligne n’est supprimée.
d. Pour savoir si une requête INSERT a bien ajouté une ligne, on peut tester mysqli_affected_rows($con) == 1.

Voir la correction commentéeAprès avoir posé votre démarche

1. Idée : le SELECT compte les lignes du résultat (num_rows), le DELETE compte les lignes touchées (affected_rows). Le vélo 101 a 2 réparations (2025-03-01, 2026-01-15) ; seule celle du 2025-03-01 est antérieure au 2026-01-01. Affichage :

Le vélo 101 a 2 réparation(s).

1 réparation(s) supprimée(s).

2. La première exécution a supprimé une ligne : il reste une réparation, qui n’est pas antérieure à la date. Affichage :

Le vélo 101 a 1 réparation(s).

0 réparation(s) supprimée(s).

3. Affichage :

Le vélo 102 a 0 réparation(s).

0 réparation(s) supprimée(s).

Le message de la ligne 13 n’est pas affiché : la requête DELETE est correcte, mysqli_query retourne true même si aucune ligne ne correspond ; mysqli_affected_rows vaut alors 0.

  • 4.a F — après un DELETE, mysqli_query retourne true ou false, pas un résultat de SELECT : mysqli_num_rows ne s’y applique pas.
  • 4.b F — count compte les éléments d’un tableau ; $res1 est un résultat de requête, dont le nombre de lignes se lit par mysqli_num_rows($res1).
  • 4.c V — le test porte sur le succès de la requête, pas sur le nombre de lignes supprimées.
  • 4.d V — une insertion réussie d’une ligne donne 1 pour mysqli_affected_rows (−1 si la requête a échoué).
3 Drill WB-H.3 — ce que reçoit le serveur : value d’une option, case non cochée, champ sans name, span
Entraînement 03 / 04

Drill WB-H.3 — ce que reçoit le serveur : value d’une option, case non cochée, champ sans name, span

1 questionCorrigé masqué

Voici le formulaire de la page location.html (figure 1) et le script louer.php qui le reçoit (figure 2).

Figure 1 :

 1 <form action="louer.php" method="post">
 2   <select name="station">
 3     <option value="1">Bab Bhar</option>
 4     <option value="4" selected>Gare</option>
 5   </select>
 6   <input type="radio" name="type" value="C" checked>Classique
 7   <input type="radio" name="type" value="E">Électrique
 8   <input type="checkbox" name="casque" value="oui">Casque
 9   <input type="checkbox" name="panier" value="oui">Panier
10   Durée : <input type="text" id="duree" value="30"> min
11   Prix estimé : <span id="prix">1.500</span> D
12   <input type="submit" name="ok" value="Louer">
13 </form>

Figure 2 :

 1 <?php
 2 $st = $_POST["station"];
 3 $type = $_POST["type"];
 4 echo "Station $st, vélo $type<br>";
 5 if (isset($_POST["casque"]))
 6   echo "Casque : oui<br>";
 7 else
 8   echo "Casque : non<br>";
 9 $nb = count($_POST);
10 echo "$nb données reçues";
11 ?>

L’utilisateur laisse la station proposée par défaut, choisit « Électrique », coche seulement « Panier », puis clique sur « Louer ».

1. Donner les valeurs des variables $st et $type.

2. Donner l’affichage produit par louer.php.

3. Donner le contenu du tableau $_POST (clés et valeurs) et la valeur de $nb. Pourquoi la durée saisie à la ligne 10 n’y figure-t-elle pas, et que faut-il modifier pour qu’elle soit envoyée ?

4. On ajoute au script l’instruction $prix = $_POST["prix"];. Que se passe-t-il ? Comment transmettre le prix estimé au serveur ?

5. Que se passerait-il si la ligne 5 était if ($_POST["casque"] == "oui") au lieu de if (isset($_POST["casque"])), la case « Casque » n’étant pas cochée ?

Voir la correction commentéeAprès avoir posé votre démarche

1. Idée : une liste envoie la value de l’option choisie, un groupe de boutons radio la value du bouton coché. $st = "4" (option « Gare », sélectionnée par défaut, et non son texte) ; $type = "E".

2. Affichage :

Station 4, vélo E

Casque : non

4 données reçues

La case « Casque » n’est pas cochée, donc elle n’est pas envoyée : le test isset de la ligne 5 est faux.

3. | Clé | Valeur |
|---|---|
| "station" | "4" |
| "type" | "E" |
| "panier" | "oui" |
| "ok" | "Louer" |

count($_POST) vaut donc 4. Le bouton submit est envoyé parce qu’il a un name et qu’il a servi à l’envoi ; la case « Casque », non cochée, ne l’est pas. Le champ de la ligne 10 n’a pas d’attribut name (seulement un id) : il n’est pas envoyé. Il faut écrire <input type="text" id="duree" name="duree" value="30">, puis lire $_POST["duree"].

4. Un <span> (ligne 11) n’est pas un champ de formulaire : $_POST["prix"] n’existe pas, PHP signale une clé indéfinie et $prix reste vide. Pour l’envoyer, on ajoute un champ caché <input type="hidden" name="prix"> que le script JavaScript remplit en même temps que le span.

5. La clé "casque" n’existe pas : PHP génère un avertissement (clé indéfinie), la comparaison est fausse et « Casque : non » s’affiche quand même. isset() permet de tester l’existence de la clé sans erreur : c’est la bonne façon de lire une case à cocher.

4 Drill WB-H.4 — tracer une fonction PHP : $i, strpos et $trouve
Entraînement 04 / 04

Drill WB-H.4 — tracer une fonction PHP : $i, strpos et $trouve

1 questionCorrigé masqué

L’avis d’un usager, saisi dans le formulaire de la page avis.html du site VéloCité, est envoyé par la méthode POST au script avis.php. Avant de l’enregistrer, le script appelle la fonction motInterdit ; l’avis est refusé si elle retourne true.

 1 function motInterdit($avis)
 2 {
 3   $liste = array("arnaque", "nul", "voleur");
 4   $avis = strtolower($avis);
 5   $i = 0;
 6   $trouve = false;
 7   while ($i < count($liste) && !$trouve)
 8   {
 9     if (strpos($avis, $liste[$i]) !== false)
10       $trouve = true;
11     else
12       $i = $i + 1;
13   }
14   return $trouve;
15 }

Rappel : strpos($ch, $mot) retourne la position (à partir de 0) de la première occurrence de $mot dans $ch, ou false s’il n’y figure pas ; strtolower($ch) retourne $ch écrite en minuscules.

1. Compléter la trace de la fonction (une ligne par tour de la boucle while) et donner la valeur retournée pour chacun des avis suivants :
- Avis A : « Vélo propre, service rapide »
- Avis B : « NUL ! Le frein arrière ne marche pas »

2. En déduire le rôle de la fonction motInterdit.

3. On remplace, à la ligne 9, l’opérateur !== par !=. Quelle valeur retourne alors la fonction pour l’avis B ? Expliquer.

4. Quelle valeur retourne la fonction (ligne 9 d’origine) pour l’avis C : « Il faut annuler si le vélo est sale » ? Quelle limite de la fonction cet exemple montre-t-il ?

Voir la correction commentéeAprès avoir posé votre démarche

1. Idée : la boucle s’arrête dès qu’un mot est trouvé ($trouve devient vrai) ou quand les 3 mots ont été testés ($i atteint 3). La ligne 4 met l’avis en minuscules avant la recherche.

Avis A (« vélo propre, service rapide ») :

Tour$i | $liste[$i] | strpos($avis, $liste[$i])$trouve | $i en fin de tour
10"arnaque"falsefalse1
21"nul"falsefalse2
32"voleur"falsefalse3


Aucun mot n’est trouvé ; la boucle s’arrête avec $i = 3 et $trouve = false. Valeur retournée : false.

Avis B (« nul ! le frein arrière ne marche pas ») :

Tour$i | $liste[$i] | strpos($avis, $liste[$i])$trouve | $i en fin de tour
10"arnaque"falsefalse1
21"nul"0true1


« nul » est trouvé en position 0 au 2ᵉ tour ; la boucle s’arrête car $trouve est vrai ($i reste à 1). Valeur retournée : true.

2. motInterdit retourne true si l’avis contient l’un des mots de la liste (sans tenir compte des majuscules), false sinon : elle sert à refuser les avis injurieux.

3. Pour l’avis B, strpos retourne 0 (le mot est au tout début). Avec !=, la comparaison est « large » : 0 est considéré comme égal à false, donc 0 != false est faux, le mot n’est pas vu, et la boucle continue jusqu’à $i = 3, avec $trouve toujours faux. La fonction retourne false : l’avis injurieux serait accepté. !== compare aussi le type (0 est un entier, false un booléen) : il ne confond pas « trouvé en position 0 » et « non trouvé ».

4. « nul » apparaît à l’intérieur du mot « annuler » (position 10) : la fonction retourne true et l’avis, pourtant correct, est refusé. strpos cherche une suite de caractères, pas un mot entier.

Relier ce raisonnement au bac

Choisis une filière pour voir les sujets qui étayent ce scénario.