Chapitre 222 minIntermédiaire
Les bases de PHP : variables, types, conditions et boucles
Par Josselyn C., prof de BTS SIO. Mis à jour le .
À la fin de ce chapitre, tu sauras
- déclarer des variables et connaître les types de PHP ;
- activer le typage strict et comparer avec === ;
- manipuler des chaînes de caractères, accents compris ;
- écrire des conditions avec if et match, et des boucles.
4 exercices corrigés
Les bases du langage PHP tiennent en quelques règles : une variable commence par $, une instruction se termine par ;, les types sont vérifiés à l'exécution et l'on compare avec ===. Ce chapitre présente les variables, les types et le typage strict, les opérateurs, les chaînes de caractères, les conditions avec if et match, et les boucles. Si tu as déjà programmé dans un autre langage, lis-le vite, mais lis attentivement les encadrés : ils signalent les pièges propres à PHP.
Variables et constantes
Une variable commence par le signe $, suivi d'un nom qui commence par une lettre ou _. Elle n'a pas besoin d'être déclarée : elle existe dès qu'on lui affecte une valeur.
<?php
$reference = 'PC-0101';
$valeur = 1180.00;
$nbJoursMax = 14;
$estDisponible = true;
$dateRetour = null; // pas encore de date de retour
Les noms de variables sont sensibles à la casse : $valeur et $Valeur sont deux variables différentes. L'usage, dans le code PHP moderne, est le camelCase ($nbJoursMax). Écris des noms en français ou en anglais, mais pas un mélange des deux dans le même projet.
Une constante garde la même valeur pendant toute l'exécution. Elle s'écrit sans $, en majuscules par convention :
<?php
const DUREE_PRET_MAX = 14; // en jours
const CATEGORIES = ['portable', 'videoprojecteur', 'tablette', 'camera', 'autre'];
echo DUREE_PRET_MAX; // 14
Les types de données
PHP est un langage à typage dynamique : c'est la valeur qui a un type, pas la variable. La même variable peut contenir un entier, puis une chaîne.
| Type | Exemple | Remarque |
|---|---|---|
int | 42, -7, 0x1A | Entier sur 64 bits : jusqu'à 9 223 372 036 854 775 807 |
float | 1180.0, 3.5e3 | Nombre à virgule, avec un point décimal |
string | 'PC-0101', "Bonjour" | Suite d'octets : un caractère accentué en UTF-8 en occupe deux |
bool | true, false | Insensible à la casse, mais écris-le en minuscules |
null | null | Absence de valeur |
array | [1, 2, 3], ['id' => 4] | Liste ou tableau associatif (chapitre 3) |
object | new DateTimeImmutable() | Instance d'une classe (chapitre 11) |
Pour connaître le type et la valeur d'une variable pendant que tu développes, utilise var_dump() : c'est l'outil de débogage le plus simple de PHP.
<?php
var_dump(1180.00); // float(1180)
var_dump('PC-0101'); // string(7) "PC-0101"
var_dump(null); // NULL
echo get_debug_type(14); // int
Le typage strict avec declare(strict_types=1)
Quand une fonction attend un int et reçoit la chaîne '14', PHP la convertit silencieusement en mode par défaut. C'est pratique, mais cela cache des erreurs : '14 jours' donne une erreur, '14.0' une valeur arrondie, et tu ne sais plus d'où viennent tes données.
La directive declare(strict_types=1);, placée en première instruction du fichier, désactive ces conversions pour les appels de fonctions écrits dans ce fichier : un mauvais type déclenche une erreur TypeError au lieu d'être converti.
<?php
declare(strict_types=1);
function dateRetourPrevue(DateTimeImmutable $debut, int $nbJours): DateTimeImmutable
{
return $debut->modify("+{$nbJours} days");
}
$debut = new DateTimeImmutable('2026-11-02');
echo dateRetourPrevue($debut, 14)->format('d/m/Y'); // 16/11/2026
echo dateRetourPrevue($debut, '14')->format('d/m/Y'); // TypeError : string given
Les données d'un formulaire arrivent toujours sous forme de chaînes ($_POST['nb_jours'] vaut '14', pas 14). En typage strict, tu dois donc les valider et les convertir toi-même, ce qui est exactement ce qu'il faut faire (chapitre 4).
Conversions et comparaisons : == ou ===
PHP propose deux opérateurs d'égalité :
==(égalité souple) compare après avoir converti les deux valeurs dans un type commun ;===(égalité stricte) compare la valeur et le type, sans conversion.
| Expression | Résultat en PHP 8 | Explication |
|---|---|---|
'1' == '01' | true | Deux chaînes numériques : comparées comme des nombres |
'10' == '1e1' | true | 1e1 vaut 10 en notation scientifique |
100 == '1e2' | true | Même raison |
0 == 'a' | false | Depuis PHP 8 ; c'était true en PHP 7 |
null == false | true | Les deux sont « vides » |
[] == false | true | Un tableau vide est « faux » |
null == 0 | true | Même raison |
'1' === 1 | false | Une chaîne n'est pas un entier |
Pour convertir explicitement, utilise un transtypage (cast) :
<?php
var_dump((int) '12'); // int(12)
var_dump((int) '12abc'); // int(12) : PHP lit le début et ignore le reste
var_dump((int) 'abc'); // int(0)
var_dump((int) 12.9); // int(12) : tronqué, pas arrondi
var_dump((float) '3.5'); // float(3.5)
var_dump((bool) '0'); // bool(false) : la chaîne '0' est fausse
var_dump((bool) '0.0'); // bool(true) : mais pas '0.0'
Le transtypage ne vérifie rien : (int) 'abc' donne 0 sans erreur. Pour une saisie, préfère une validation qui refuse ce qui n'est pas un entier, avec filter_var() (chapitre 4).
Les opérateurs
| Famille | Opérateurs | Exemple | Résultat |
|---|---|---|---|
| Arithmétiques | + - * / % ** | 7 / 2, 7 % 2, 2 ** 10 | 3.5, 1, 1024 |
| Division entière | intdiv() | intdiv(7, 2) | 3 |
| Concaténation | . et .= | 'PC-' . '0101' | 'PC-0101' |
| Affectation combinée | += -= *= /= .= ??= | $total += $valeur; | |
| Incrément | ++ -- | $nb++; | |
| Comparaison | === !== < > <= >= | $nb >= 3 | true ou false |
| Vaisseau spatial | <=> | 10 <=> 5 | 1 (ou 0, ou -1) |
| Logiques | && || ! | $dispo && !$enPanne | |
| Fusion null | ?? | $_GET['page'] ?? 1 | La valeur, ou 1 si elle n'existe pas ou vaut null |
Deux pièges reviennent souvent :
+n'est pas la concaténation.'5' + '3'vaut8(une addition),'5' . '3'vaut'53'.- Les nombres à virgule sont approximatifs.
0.1 + 0.2 === 0.3est faux, car ces nombres n'ont pas de représentation binaire exacte. Pour de l'argent, stocke desDECIMALen base et calcule en centimes entiers, ou arrondis avecround($x, 2)au moment d'afficher.
L'opérateur ?? est très utilisé avec les données reçues : $_GET['page'] ?? 1 vaut le paramètre page s'il existe, 1 sinon, sans avertissement « Undefined array key ». Sa variante ??= n'affecte que si la variable est null ou inexistante : $_SESSION['panier'] ??= [];.
Les chaînes de caractères
Une chaîne s'écrit entre apostrophes ou entre guillemets. La différence : entre guillemets, PHP remplace les variables par leur valeur (c'est l'interpolation) et interprète les séquences comme \n (saut de ligne).
<?php
$libelle = 'Portable Dell';
$materiel = ['reference' => 'PC-0101', 'valeur' => 1180.5];
echo 'Matériel : $libelle'; // Matériel : $libelle
echo "Matériel : $libelle"; // Matériel : Portable Dell
echo "Référence : {$materiel['reference']}"; // les accolades pour un tableau
echo 'Référence : ' . $materiel['reference']; // ou la concaténation
Pour un long texte sur plusieurs lignes, la syntaxe heredoc interpole comme des guillemets ; l'indentation de la ligne de fermeture est retirée de chaque ligne :
<?php
$nom = 'Karim Morel';
$reference = 'PC-0102';
$message = <<<TXT
Bonjour {$nom},
le matériel {$reference} est à rendre demain.
TXT;
echo $message;
Les fonctions de chaînes que tu utiliseras le plus :
| Fonction | Exemple | Résultat |
|---|---|---|
mb_strlen() | mb_strlen('Étagère') | 7 caractères (strlen() donne 9 octets) |
mb_strtoupper() | mb_strtoupper('étagère') | 'ÉTAGÈRE' (strtoupper() laisse les accents en minuscule) |
trim() | trim(' karim ') | 'karim' : retire les espaces au début et à la fin |
str_contains() | str_contains('PC-0101', 'PC') | true |
str_starts_with() | str_starts_with('PC-0101', 'PC-') | true |
str_replace() | str_replace('-', '', 'PC-0101') | 'PC0101' |
substr() | substr('PC-0101', 3) | '0101' |
explode() | explode('-', 'PC-0101') | ['PC', '0101'] |
implode() | implode(', ', ['a', 'b']) | 'a, b' |
str_pad() | str_pad('42', 4, '0', STR_PAD_LEFT) | '0042' |
sprintf() | sprintf('PC-%04d', 42) | 'PC-0042' |
number_format() | number_format(1180.5, 2, ',', ' ') | '1 180,50' |
ucfirst() | ucfirst('karim') | 'Karim' |
Conditions : if, elseif, else
<?php
$etat = 'en_reparation';
$empruntEnCours = false;
if ($etat !== 'disponible') {
$message = 'Ce matériel ne peut pas être prêté.';
} elseif ($empruntEnCours) {
$message = 'Ce matériel est déjà prêté.';
} else {
$message = 'Ce matériel peut être prêté.';
}
Pour choisir entre deux valeurs, l'opérateur ternaire est plus court : $libelle = $nb > 1 ? 'matériels' : 'matériel';. Ne l'imbrique pas : deux ternaires imbriqués sans parenthèses sont même une erreur de syntaxe depuis PHP 8.
Une condition accepte n'importe quelle valeur, convertie en booléen. Sont fausses : false, 0, 0.0, '', '0', [] et null. Tout le reste est vrai. La fonction empty($x) teste exactement cela, et isset($x) teste qu'une variable existe et n'est pas null.
match : le switch moderne
match choisit une valeur selon une expression. Il remplace avantageusement switch :
<?php
$etat = 'en_reparation';
$libelle = match ($etat) {
'disponible' => 'Disponible',
'en_reparation' => 'En réparation',
'reforme' => 'Réformé',
};
echo $libelle; // En réparation
switch | match | |
|---|---|---|
| Comparaison | souple (==) | stricte (===) |
Oubli de break | passe au cas suivant, souvent par erreur | impossible : chaque bras renvoie une valeur |
| Valeur renvoyée | non, c'est une instruction | oui, c'est une expression |
| Aucun cas ne correspond | rien ne se passe | erreur UnhandledMatchError, sauf bras default |
Plusieurs valeurs peuvent partager un bras ('portable', 'tablette' => 14,). Pour des intervalles, la forme match (true) teste des conditions dans l'ordre :
<?php
$joursDeRetard = 9;
$niveau = match (true) {
$joursDeRetard <= 0 => 'à l\'heure',
$joursDeRetard <= 7 => 'relance',
default => 'blocage des emprunts',
};
echo $niveau; // blocage des emprunts
Les boucles : for, while, foreach
<?php
// for : quand tu connais le nombre de tours
for ($i = 1; $i <= 3; $i++) {
echo "Relance n° $i\n";
}
// while : tant qu'une condition est vraie (testée avant chaque tour)
$stock = 3;
while ($stock > 0) {
echo "Il reste $stock portable(s)\n";
$stock--;
}
// foreach : pour parcourir un tableau, la boucle la plus utilisée en PHP
$references = ['PC-0101', 'PC-0102', 'VP-0001'];
foreach ($references as $reference) {
echo $reference . "\n";
}
break sort de la boucle, continue passe directement au tour suivant. La boucle do { … } while (…); teste sa condition après le tour : elle s'exécute au moins une fois. Le chapitre 3 détaille foreach sur les tableaux associatifs, qui est la façon normale de parcourir les lignes renvoyées par la base.
La syntaxe alternative dans les gabarits HTML
Dans une page qui mélange HTML et PHP, les accolades deviennent difficiles à suivre. PHP propose une syntaxe alternative : : à la place de {, et endif;, endforeach;, endwhile; à la place de }.
<?php $materiels = [['reference' => 'PC-0101', 'disponible' => true], ['reference' => 'VP-0001', 'disponible' => false]]; ?>
<?php if ($materiels === []): ?>
<p>Aucun matériel enregistré.</p>
<?php else: ?>
<ul>
<?php foreach ($materiels as $m): ?>
<li>
<?= htmlspecialchars($m['reference']) ?>
<?php if (!$m['disponible']): ?><em>(prêté)</em><?php endif; ?>
</li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
C'est la syntaxe des vues : les fichiers qui affichent les données, sans logique métier (chapitre 12).
Exercices
Exercice 1 sur 4 Prédire l'affichage Réussi À revoir
Sans exécuter le code, donne ce qu'affiche chaque ligne.
<?php
var_dump('5' + '3');
var_dump('5' . '3');
var_dump(7 / 2);
var_dump(intdiv(7, 2));
var_dump('1' == '01');
var_dump('1' === '01');
var_dump(0 == 'a');
var_dump((int) '3 portables');
Voir la réponseMasquer la réponse de l'exercice 1
int(8)
string(2) "53"
float(3.5)
int(3)
bool(true)
bool(false)
bool(false)
int(3)
'1' == '01' est vrai, car deux chaînes numériques sont comparées comme des nombres ; avec ===, ce sont deux chaînes différentes. 0 == 'a' est faux depuis PHP 8. (int) '3 portables' lit le début de la chaîne et donne 3, sans erreur.
Tu avais trouvé ?
Exercice 2 sur 4 Remplacer un switch par match Réussi À revoir
Réécris ce code avec match. Quelle différence de comportement apparaît si $categorie vaut 'drone' ?
<?php
switch ($categorie) {
case 'portable':
case 'tablette':
$duree = 14;
break;
case 'videoprojecteur':
case 'camera':
$duree = 3;
break;
default:
$duree = 7;
}
Voir la réponseMasquer la réponse de l'exercice 2
<?php
$duree = match ($categorie) {
'portable', 'tablette' => 14,
'videoprojecteur', 'camera' => 3,
default => 7,
};
Avec ce default, 'drone' donne 7 dans les deux versions. Sans bras default, match lèverait une UnhandledMatchError pour 'drone', alors qu'un switch sans default laisserait $duree non définie, sans rien signaler : match rend l'oubli visible. Autre différence : match compare avec ===, donc la valeur 0 ne correspondrait à aucun bras chaîne.
Tu avais trouvé ?
Exercice 3 sur 4 Le niveau de relance Réussi À revoir
Un emprunt devait être rendu il y a $joursDeRetard jours (0 ou négatif : pas en retard). Écris le code qui affiche, avec match (true) :
- « Rien à faire » s'il n'y a pas de retard ;
- « Relance par mail » de 1 à 3 jours de retard ;
- « Appel du gestionnaire » de 4 à 10 jours ;
- « Signalement au responsable » au-delà.
Puis écris une boucle qui affiche le niveau pour chaque valeur de 0 à 12 et vérifie les limites.
Voir la réponseMasquer la réponse de l'exercice 3
<?php
declare(strict_types=1);
function niveauRelance(int $joursDeRetard): string
{
return match (true) {
$joursDeRetard <= 0 => 'Rien à faire',
$joursDeRetard <= 3 => 'Relance par mail',
$joursDeRetard <= 10 => 'Appel du gestionnaire',
default => 'Signalement au responsable',
};
}
for ($j = 0; $j <= 12; $j++) {
echo $j . ' : ' . niveauRelance($j) . "\n";
}
Les bras sont testés dans l'ordre : pour 3, $joursDeRetard <= 0 est faux, $joursDeRetard <= 3 est vrai, on s'arrête. La boucle affiche « Rien à faire » pour 0, « Relance par mail » de 1 à 3, « Appel du gestionnaire » de 4 à 10, « Signalement au responsable » pour 11 et 12. Teste toujours les limites (0, 1, 3, 4, 10, 11) : c'est là que se cachent les erreurs de < au lieu de <=.
Tu avais trouvé ?
Exercice 4 sur 4 Corriger une erreur de type Réussi À revoir
Ce script, en typage strict, lève une TypeError. Explique pourquoi et corrige-le sans retirer declare(strict_types=1).
<?php
declare(strict_types=1);
function formaterPrix(float $prix): string
{
return number_format($prix, 2, ',', ' ') . ' €';
}
$saisie = '1180.5'; // valeur reçue d'un formulaire
echo formaterPrix($saisie);
Voir la réponseMasquer la réponse de l'exercice 4
$saisie est une chaîne (tout ce qui vient d'un formulaire est une chaîne) et la fonction attend un float : en typage strict, PHP refuse de convertir. On valide et on convertit à l'entrée :
<?php
declare(strict_types=1);
function formaterPrix(float $prix): string
{
return number_format($prix, 2, ',', ' ') . ' €';
}
$saisie = '1180.5';
$prix = filter_var($saisie, FILTER_VALIDATE_FLOAT);
if ($prix === false) {
exit('Prix invalide');
}
echo formaterPrix($prix); // 1 180,50 €
filter_var() renvoie un vrai float, ou false si la saisie n'est pas un nombre : la conversion est contrôlée, pas subie. Un simple (float) $saisie ferait disparaître l'erreur, mais transformerait aussi 'abc' en 0.0 sans prévenir.
Tu avais trouvé ?