đ» Application POO en PHPâïž
note
création à l'aide de ChatGPT5
đŠ Exercice 1 â Classe CompteBancaireâïž
đŻ Objectifsâïž
- Définir une classe simple en PHP
- Manipuler les notions : attributs privés, constructeur, getters et méthodes métier simples
- Introduire la notion dâencapsulation
đ ĂnoncĂ©âïž
On souhaite modéliser un compte bancaire en PHP.
-
Créer une classe
CompteBancaireavec les attributs suivants (tous privés) : -
titulaire : stringâ nom du titulaire du compte -
solde : floatâ solde du compte -
Le constructeur initialise le titulaire et le solde initial (qui ne peut pas ĂȘtre nĂ©gatif).
-
Ajouter les méthodes suivantes :
-
getSolde(): retourne le solde actuel deposer(float $montant): ajoute de lâargent au solde (uniquement si le montant est positif)retirer(float $montant): retire de lâargent (uniquement si le montant est positif et †solde actuel)afficher(): affiche le titulaire et le solde du compte
â Attendus
- Encapsulation respectée (
private) - ContrÎle des valeurs (pas de solde négatif, pas de dépÎt/retrait impossible)
- Affichage clair du titulaire et du solde
đ» Squelette de code Ă complĂ©terâïž
<?php
declare(strict_types=1);
class CompteBancaire {
// 1. Définir les attributs privés
// 2. Constructeur
// 3. getSolde()
// 4. deposer()
// 5. retirer()
// 6. afficher()
}
// --- Tests ---
$c1 = new CompteBancaire("Alice", 200);
$c1->deposer(50);
$c1->retirer(100);
$c1->afficher(); // Devrait afficher : Alice - Solde : 150 âŹ
Correction
<?php
declare(strict_types=1);
class CompteBancaire {
// đ Attributs privĂ©s
private string $titulaire;
private float $solde;
// âïž Constructeur
public function __construct(string $titulaire, float $soldeInitial = 0) {
$this->titulaire = $titulaire;
// On s'assure que le solde n'est pas négatif
$this->solde = max(0, $soldeInitial);
}
// đ Getter
public function getSolde(): float {
return $this->solde;
}
// đ° DĂ©pĂŽt
public function deposer(float $montant): void {
if ($montant > 0) {
$this->solde += $montant;
}
}
// đž Retrait
public function retirer(float $montant): void {
if ($montant > 0 && $montant <= $this->solde) {
$this->solde -= $montant;
} else {
echo "Retrait impossible !\n";
}
}
// đą Affichage
public function afficher(): void {
echo "{$this->titulaire} - Solde : {$this->solde} âŹ\n";
}
}
// --- đ Tests ---
$c1 = new CompteBancaire("Alice", 200);
$c1->deposer(50); // solde = 250
$c1->retirer(100); // solde = 150
$c1->afficher(); // Alice - Solde : 150 âŹ
đ Commentaires :
- Les attributs sont privĂ©s â on applique lâencapsulation.
- Le constructeur contrĂŽle que le solde initial nâest pas nĂ©gatif.
- Les méthodes métier (deposer, retirer) contiennent des rÚgles de validation.
- afficher() sert de méthode utilitaire simple.
đ§Ź Exercice 2 â HĂ©ritage : CompteEpargne & CompteCourantâïž
đŻ Objectifsâïž
- Mettre en Ćuvre lâhĂ©ritage Ă partir de
CompteBancaire - Utiliser les visibilités (
private,protected) intelligemment - Pratiquer la redéfinition (override) de méthodes
- Approcher un dĂ©but de polymorphisme (mĂȘme interface dâusage)
đ ĂnoncĂ©âïž
à partir de la classe CompteBancaire (Exercice 1), créer deux classes filles :
-
CompteEpargne -
Attribut privé :
tauxInteret: float(ex :0.03pour 3%) - Méthode :
appliquerInteret()â augmente le solde desolde * tauxInteret -
Redéfinir
afficher()pour indiquer le taux -
CompteCourant -
Attributs privés :
-
decouvertAutorise: float(ex : 200.0) fraisDecouvert: float(ex : 8.0)-
Redéfinir
retirer(float $montant): -
autoriser un solde jusquâĂ
-decouvertAutorise - si le solde passe strictement négatif aprÚs le retrait, appliquer
fraisDecouvertimmédiatement - Redéfinir
afficher()pour indiquer découvert et éventuel solde négatif
Contraintes générales
- Conserver lâencapsulation : les nouveaux attributs restent privĂ©s
- Utiliser
parent::__construct(...)dans les constructeurs des classes filles -
Lever une
InvalidArgumentExceptionsi : -
tauxInteret < 0 decouvertAutorise < 0oufraisDecouvert < 0- PHP ℠8.0, propriétés typées,
declare(strict_types=1);
đ» Squelette de code Ă complĂ©ter
<?php
declare(strict_types=1);
class CompteBancaire {
// Reprends la version validĂ©e de lâExercice 1
protected string $titulaire;
protected float $solde;
public function __construct(string $titulaire, float $soldeInitial = 0) {
$this->titulaire = $titulaire;
$this->solde = max(0, $soldeInitial);
}
public function getSolde(): float { return $this->solde; }
public function deposer(float $montant): void {
if ($montant > 0) $this->solde += $montant;
}
public function retirer(float $montant): void {
if ($montant > 0 && $montant <= $this->solde) {
$this->solde -= $montant;
} else {
echo "Retrait impossible !\n";
}
}
public function afficher(): void {
echo "{$this->titulaire} - Solde : {$this->solde} âŹ\n";
}
}
// --- à compléter ---
class CompteEpargne extends CompteBancaire {
// TODO: attribut privé $tauxInteret
public function __construct(string $titulaire, float $soldeInitial, float $tauxInteret) {
// TODO: valider $tauxInteret >= 0
parent::__construct($titulaire, $soldeInitial);
// TODO: affecter $tauxInteret
}
public function appliquerInteret(): void {
// TODO: augmenter $this->solde de $this->solde * $this->tauxInteret
}
public function afficher(): void {
// TODO: afficher avec indication du taux
}
}
class CompteCourant extends CompteBancaire {
// TODO: attributs privés $decouvertAutorise, $fraisDecouvert
public function __construct(
string $titulaire,
float $soldeInitial,
float $decouvertAutorise = 0,
float $fraisDecouvert = 0
) {
// TODO: valider >= 0
parent::__construct($titulaire, $soldeInitial);
// TODO: affecter les attributs
}
public function retirer(float $montant): void {
// TODO:
// - autoriser dépassement jusque -$decouvertAutorise
// - si solde devient < 0 aprĂšs retrait, appliquer fraisDecouvert (et
// vérifier qu'on reste >= -$decouvertAutorise aprÚs frais)
}
public function afficher(): void {
// TODO: afficher avec découvert autorisé et frais
}
}
đ ScĂ©narios de test (scripts rapides)âïž
// 1) Ăpargne
$e = new CompteEpargne("Alice", 1000, 0.05);
$e->appliquerInteret(); // solde attendu: 1050
$e->afficher(); // doit indiquer le taux
// 2) Courant sans découvert
$c1 = new CompteCourant("Bob", 200, 0, 0);
$c1->retirer(250); // devrait refuser (pas de découvert)
$c1->afficher();
// 3) Courant avec découvert autorisé
$c2 = new CompteCourant("Chloe", 100, 200, 8);
$c2->retirer(250); // passe à -150 => appliquer 8⏠de frais => -158
$c2->afficher(); // solde attendu: -158
// 4) Dépassement du découvert autorisé
$c2->retirer(60); // -158 - 60 = -218 -> refuser si dépasse -200 (ou tenir compte de nouveaux frais)
$c2->afficher();
// 5) Cas invalides (doivent lever exception)
try { new CompteEpargne("Dan", 500, -0.01); } catch (InvalidArgumentException $e) { echo "OK\n"; }
try { new CompteCourant("Eve", 500, -100, 5); } catch (InvalidArgumentException $e) { echo "OK\n"; }
try { new CompteCourant("Fay", 500, 100, -2); } catch (InvalidArgumentException $e) { echo "OK\n"; }
đ§ Points dâattention / erreurs frĂ©quentes : Conseilsâïž
- Oublier dâappeler
parent::__construct(...)dans les classes filles - Laisser passer des valeurs négatives pour
tauxInteret,decouvertAutorise,fraisDecouvert - Ne pas gĂ©rer le cas oĂč lâapplication des frais ferait dĂ©passer le dĂ©couvert autorisĂ©
- Redéfinir
retirer()mais ne pas respecter la signature (retourvoidattendu)
â Pour aller plus loinâïž
- Ajouter
getTauxInteret(),setTauxInteret()avec validation - Ajouter dans
CompteCourantune méthodeestADecouvert(): bool - Ajouter une méthode commune
resume()(mĂȘme nom dans les deux classes) â tester un tableau polymorphe de comptes et appelerresume()en boucle
Correction
<?php
declare(strict_types=1);
/**
* Base : CompteBancaire (version adaptée pour l'héritage)
*/
class CompteBancaire {
// Protégés pour un accÚs direct (contrÎlé) dans les classes filles
protected string $titulaire;
protected float $solde;
public function __construct(string $titulaire, float $soldeInitial = 0) {
$this->titulaire = $titulaire;
$this->solde = max(0, $soldeInitial); // pas de solde initial négatif
}
public function getSolde(): float { return $this->solde; }
public function deposer(float $montant): void {
if ($montant > 0) {
$this->solde += $montant;
}
}
public function retirer(float $montant): void {
if ($montant > 0 && $montant <= $this->solde) {
$this->solde -= $montant;
} else {
echo "Retrait impossible !\n";
}
}
public function afficher(): void {
echo "{$this->titulaire} - Solde : {$this->solde} âŹ\n";
}
}
/**
* CompteEpargne : ajoute un taux d'intĂ©rĂȘt et appliqueInteret()
*/
class CompteEpargne extends CompteBancaire {
private float $tauxInteret; // ex: 0.05 pour 5%
public function __construct(string $titulaire, float $soldeInitial, float $tauxInteret) {
if ($tauxInteret < 0) {
throw new InvalidArgumentException("Le taux d'intĂ©rĂȘt doit ĂȘtre >= 0");
}
parent::__construct($titulaire, $soldeInitial);
$this->tauxInteret = $tauxInteret;
}
public function appliquerInteret(): void {
$this->solde += $this->solde * $this->tauxInteret;
}
public function afficher(): void {
$pct = $this->tauxInteret * 100;
echo "{$this->titulaire} - Solde (Ă©pargne) : {$this->solde} ⏠â Taux : {$pct}%\n";
}
}
/**
* CompteCourant : découvert autorisé + frais si solde devient négatif
*/
class CompteCourant extends CompteBancaire {
private float $decouvertAutorise; // ex: 200.0
private float $fraisDecouvert; // ex: 8.0
public function __construct(
string $titulaire,
float $soldeInitial,
float $decouvertAutorise = 0,
float $fraisDecouvert = 0
) {
if ($decouvertAutorise < 0) {
throw new InvalidArgumentException("Le dĂ©couvert autorisĂ© doit ĂȘtre >= 0");
}
if ($fraisDecouvert < 0) {
throw new InvalidArgumentException("Les frais de dĂ©couvert doivent ĂȘtre >= 0");
}
parent::__construct($titulaire, $soldeInitial);
$this->decouvertAutorise = $decouvertAutorise;
$this->fraisDecouvert = $fraisDecouvert;
}
/**
* Retrait :
* - autorisé si le solde aprÚs retrait ET frais éventuels reste >= -decouvertAutorise
* - si le solde devient négatif suite au retrait, on applique immédiatement les frais
* - sinon, on refuse l'opération (aucune modification)
*/
public function retirer(float $montant): void {
if ($montant <= 0) {
echo "Retrait impossible !\n";
return;
}
// Simulation pour vérifier les limites AVANT d'appliquer réellement
$soldeApresRetrait = $this->solde - $montant;
$soldeFinal = $soldeApresRetrait;
if ($soldeApresRetrait < 0) {
$soldeFinal -= $this->fraisDecouvert; // application des frais
}
if ($soldeFinal < -$this->decouvertAutorise) {
echo "Retrait refusé : dépassement du découvert autorisé.\n";
return;
}
// Appliquer réellement
$this->solde = $soldeApresRetrait;
if ($this->solde < 0) {
$this->solde -= $this->fraisDecouvert;
}
}
public function afficher(): void {
$infoDecouvert = "DĂ©couvert autorisĂ© : {$this->decouvertAutorise} âŹ";
$infoFrais = "Frais de dĂ©couvert : {$this->fraisDecouvert} âŹ";
echo "{$this->titulaire} - Solde (courant) : {$this->solde} ⏠â {$infoDecouvert} â {$infoFrais}\n";
}
}
/* ---------------------------
* TESTS
* --------------------------- */
// Ăpargne
$e = new CompteEpargne("Alice", 1000, 0.05);
$e->appliquerInteret(); // 1000 -> 1050
$e->afficher(); // doit indiquer le taux
// Courant sans découvert
$c1 = new CompteCourant("Bob", 200, 0, 0);
$c1->retirer(250); // doit refuser (pas de découvert)
$c1->afficher();
// Courant avec découvert
$c2 = new CompteCourant("Chloe", 100, 200, 8);
$c2->retirer(250); // 100 - 250 = -150 -> frais 8 => -158 (OK, >= -200)
$c2->afficher();
$c2->retirer(60); // tester dépassement: -158 - 60 = -218 -> frais potentiels rendraient < -200 => refus
$c2->afficher();
// Cas invalides
try { new CompteEpargne("Dan", 500, -0.01); } catch (InvalidArgumentException $ex) { echo "OK (taux invalide)\n"; }
try { new CompteCourant("Eve", 500, -100, 5); } catch (InvalidArgumentException $ex) { echo "OK (découvert invalide)\n"; }
try { new CompteCourant("Fay", 500, 100, -2); } catch (InvalidArgumentException $ex) { echo "OK (frais invalides)\n"; }