PHP Leçon 8
Héritage et fichiers séparés
Ranger chaque classe dans son fichier, créer des classes enfants avec extends, et signaler une erreur avec une exception.
À la fin de cette leçon, tu sauras
- Ranger une classe dans son propre fichier et l'inclure avec require_once
- Créer une classe enfant avec extends et appeler le parent avec parent::
- Choisir entre private et protected
- Lancer et attraper une exception avec throw, try et catch
Une classe, un fichier
Dans un vrai projet, on ne met pas tout dans index.php. L'habitude est de ranger chaque classe dans son propre fichier, qui porte le même nom qu'elle : la classe Compte va dans Compte.php. Le fichier principal l'inclut avec require_once :
<?php
// Fichier Compte.php : uniquement la classe
class Compte
{
public function __construct(public string $titulaire, protected float $solde = 0)
{
}
public function decrire(): string
{
return "$this->titulaire : $this->solde €";
}
}<?php
// Fichier index.php
require_once 'Compte.php'; // copie le contenu de Compte.php ici
$compte = new Compte("Léa", 150);
echo $compte->decrire();requireinclut un fichier ; s'il n'existe pas, le programme s'arrête avec une erreur fatale ;includefait pareil, mais continue avec un simple avertissement si le fichier manque ;- la version
_once(require_once,include_once) n'inclut le fichier qu'une seule fois, même si on le demande plusieurs fois. C'est celle qu'on utilise pour les classes : déclarer deux fois la même classe est une erreur.
Dans les exercices : ils ont plusieurs fichiers, avec des onglets au-dessus de l'éditeur. ▶ Exécuter lance toujours index.php, qui inclut les autres.
L'héritage : extends
Un livret d'épargne est un compte, avec un taux d'intérêt en plus. Plutôt que de tout réécrire, on crée une classe enfant qui hérite de la classe parente avec extends : elle reçoit toutes ses propriétés et méthodes, et peut en ajouter.
<?php
class Livret extends Compte
{
public function __construct(string $titulaire, float $solde, private float $taux)
{
parent::__construct($titulaire, $solde); // on laisse le parent faire sa part
}
public function ajouterInterets(): void
{
$this->solde += $this->solde * $this->taux / 100;
}
}
$livret = new Livret("Tom", 1000, 3);
$livret->ajouterInterets();
echo $livret->decrire(); // Tom : 1030 € (decrire() vient de Compte)parent::__construct(…)appelle le constructeur de la classe parente, pour qu'elle remplisse ses propres propriétés ;Livretn'a pas écritdecrire(): elle l'a reçue deCompte.
private ou protected ?
Une propriété private n'est visible que dans sa classe : même une classe enfant ne peut pas la lire. C'est pour ça que $solde est protected dans Compte : protected = visible dans la classe et dans ses classes enfants, mais toujours pas depuis l'extérieur.
| Mot | Dans la classe | Dans une classe enfant | Ailleurs |
|---|---|---|---|
public | oui | oui | oui |
protected | oui | oui | non |
private | oui | non | non |
Redéfinir une méthode
Une classe enfant peut redéfinir une méthode du parent en écrivant une méthode du même nom. Avec parent::nom(), elle peut quand même réutiliser la version du parent :
<?php
class Livret extends Compte
{
// … constructeur et ajouterInterets() comme plus haut …
public function decrire(): string
{
return parent::decrire() . " (taux : $this->taux %)";
}
}
echo $livret->decrire(); // Tom : 1030 € (taux : 3 %)Signaler une erreur : les exceptions
Quand une méthode reçoit une valeur impossible (un dépôt de -50 €), le plus propre est de lancer une exception avec throw. Le programme s'arrête net… sauf si le code qui a appelé la méthode l'attrape avec try / catch.
<?php
class Compte
{
// …
public function deposer(float $montant): void
{
if ($montant <= 0) {
throw new InvalidArgumentException("Montant invalide : $montant");
}
$this->solde += $montant;
}
}
try {
$compte->deposer(-50);
echo "Dépôt fait"; // jamais exécuté : l'exception saute directement au catch
} catch (InvalidArgumentException $e) {
echo "Erreur : " . $e->getMessage(); // Erreur : Montant invalide : -50
}InvalidArgumentException est une classe d'exception fournie par PHP (« argument invalide »). $e est l'objet exception, et sa méthode getMessage() renvoie le texte donné à new.
À retenir
- Une classe par fichier, au même nom (
Compte.php), inclus avecrequire_once 'Compte.php';. requirearrête tout si le fichier manque,includecontinue ;_onceévite d'inclure deux fois.class Livret extends Compte: la classe enfant hérite de tout et peut ajouter ou redéfinir des méthodes.parent::__construct(…)etparent::methode()appellent la version de la classe parente.protected= visible dans la classe et ses enfants ;private= seulement dans la classe.throw new InvalidArgumentException("…")lance une exception ;try { … } catch (InvalidArgumentException $e) { $e->getMessage(); }l'attrape.
Voir la fiche de révision de cette leçon (à imprimer ou à relire avant un contrôle)
Exercice 1 · Ranger la classe dans son fichier
Facile ✓ RéussiCet exercice a deux fichiers. La classe Produit est déjà écrite dans Produit.php, mais index.php ne la connaît pas encore : clique sur ▶ Exécuter, tu verras l'erreur « Class "Produit" not found ».
- en haut de
index.php, inclusProduit.phpavecrequire_once; - crée ensuite un produit « Clavier » à 49 € ;
- affiche sa description avec la méthode
decrire().
Clique sur ▶ Exécuter (ou Ctrl + Entrée) pour lancer ton code.
Données envoyées par le formulaire
Consignes à réussir
- À faire : index.php inclut Produit.php avec require_once
- À faire : Un produit est créé avec new Produit(…)
- À faire : La classe reste dans Produit.php
- À faire : La page affiche « Clavier : 49 € » ▶ vérifié quand tu exécutes
Bravo, cet exercice est réussi !
- Indice 1 : La première ligne après
<?php:require_once 'Produit.php';. - Indice 2 : Créer l'objet :
$produit = new Produit("Clavier", 49);. Les deux valeurs vont au constructeur. - Indice 3 : Afficher :
echo $produit->decrire();.
Toujours bloqué ? Voir une solution
index.php
<?php
require_once 'Produit.php';
$produit = new Produit("Clavier", 49);
echo $produit->decrire(); Exercice 2 · Une classe enfant
Moyen ✓ RéussiLa classe Animal (dans Animal.php) a un nom protégé et une méthode decrire(). Dans Chien.php :
- déclare une classe
Chienqui hérite d'Animal; - ajoute-lui une méthode
aboyer()qui renvoie « Rex dit Wouf ! » (en utilisant le nom de l'animal, pas en écrivant « Rex » en dur).
Puis, dans index.php, inclus les deux fichiers (Animal.php d'abord, car Chien en a besoin), et le code existant affichera les deux phrases.
Clique sur ▶ Exécuter (ou Ctrl + Entrée) pour lancer ton code.
Données envoyées par le formulaire
Consignes à réussir
- À faire : Chien hérite d'Animal (extends)
- À faire : aboyer() utilise $this->nom
- À faire : index.php inclut Animal.php puis Chien.php
- À faire : La page affiche « Je m'appelle Rex. » puis « Rex dit Wouf ! » ▶ vérifié quand tu exécutes
Bravo, cet exercice est réussi !
- Indice 1 : Dans
Chien.php:class Chien extends Animal { … }. Pas besoin de constructeur : celui d'Animalest hérité. - Indice 2 : La méthode :
public function aboyer(): string { return "$this->nom dit Wouf !"; }.$nomestprotected, donc la classe enfant peut le lire. - Indice 3 : En haut de
index.php:require_once 'Animal.php';puisrequire_once 'Chien.php';.
Toujours bloqué ? Voir une solution
index.php
<?php
require_once 'Animal.php';
require_once 'Chien.php';
$rex = new Chien("Rex");
echo "<p>" . $rex->decrire() . "</p>";
echo "<p>" . $rex->aboyer() . "</p>"; Chien.php
<?php
class Chien extends Animal
{
public function aboyer(): string
{
return "$this->nom dit Wouf !";
}
} Exercice 3 · Livret d'épargne et exceptions
Pour aller plus loin ✓ RéussiCompte.php contient la classe Compte. À toi d'écrire :
- dans
Compte.php, complètedeposer(): si le montant est négatif ou nul, lance uneInvalidArgumentExceptionavec le message « Montant invalide » ; - dans
Livret.php, une classeLivretqui hérite deCompte, avec un constructeur qui reçoit le titulaire, le solde et un taux (propriété privée), et qui appelleparent::__construct; - toujours dans
Livret, redéfinisdecrire()pour ajouter « (taux : 3 %) » à la description du parent, avecparent::decrire().
index.php est déjà écrit : il doit afficher « Tom : 1000 € (taux : 3 %) » puis « Erreur : Montant invalide ».
Clique sur ▶ Exécuter (ou Ctrl + Entrée) pour lancer ton code.
Données envoyées par le formulaire
Consignes à réussir
- À faire : deposer() lance une InvalidArgumentException
- À faire : Livret hérite de Compte
- À faire : Le constructeur appelle parent::__construct
- À faire : decrire() réutilise parent::decrire()
- À faire : La page affiche « Tom : 1000 € (taux : 3 %) » ▶ vérifié quand tu exécutes
- À faire : L'exception est attrapée : « Erreur : Montant invalide » ▶ vérifié quand tu exécutes
Bravo, cet exercice est réussi !
- Indice 1 : Dans
deposer(), sur la ligne vide :if ($montant <= 0) { throw new InvalidArgumentException("Montant invalide"); }. - Indice 2 : Le constructeur de
Livret:public function __construct(string $titulaire, float $solde, private float $taux) { parent::__construct($titulaire, $solde); }. - Indice 3 : La redéfinition :
public function decrire(): string { return parent::decrire() . " (taux : $this->taux %)"; }.
Toujours bloqué ? Voir une solution
Compte.php
<?php
class Compte
{
public function __construct(public string $titulaire, protected float $solde = 0)
{
}
public function deposer(float $montant): void
{
if ($montant <= 0) {
throw new InvalidArgumentException("Montant invalide");
}
$this->solde += $montant;
}
public function decrire(): string
{
return "$this->titulaire : $this->solde €";
}
} Livret.php
<?php
class Livret extends Compte
{
public function __construct(string $titulaire, float $solde, private float $taux)
{
parent::__construct($titulaire, $solde);
}
public function decrire(): string
{
return parent::decrire() . " (taux : $this->taux %)";
}
}