Aller au contenu

💻 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.

  1. Créer une classe CompteBancaire avec les attributs suivants (tous privés) :

  2. titulaire : string → nom du titulaire du compte

  3. solde : float → solde du compte

  4. Le constructeur initialise le titulaire et le solde initial (qui ne peut pas être négatif).

  5. Ajouter les méthodes suivantes :

  6. getSolde() : retourne le solde actuel

  7. deposer(float $montant) : ajoute de l’argent au solde (uniquement si le montant est positif)
  8. retirer(float $montant) : retire de l’argent (uniquement si le montant est positif et ≤ solde actuel)
  9. 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
<?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
<?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 :

  1. CompteEpargne

  2. Attribut privé : tauxInteret: float (ex : 0.03 pour 3%)

  3. Méthode : appliquerInteret() → augmente le solde de solde * tauxInteret
  4. Redéfinir afficher() pour indiquer le taux

  5. CompteCourant

  6. Attributs privés :

  7. decouvertAutorise: float (ex : 200.0)

  8. fraisDecouvert: float (ex : 8.0)
  9. Redéfinir retirer(float $montant) :

  10. autoriser un solde jusqu’à -decouvertAutorise

  11. si le solde passe strictement négatif après le retrait, appliquer fraisDecouvert immédiatement
  12. 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 InvalidArgumentException si :

  • tauxInteret < 0

  • decouvertAutorise < 0 ou fraisDecouvert < 0
  • PHP ≥ 8.0, propriétés typées, declare(strict_types=1);
💻 Squelette de code à compléter
🐘 PHP
<?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)⚓︎

🐘 PHP
// 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 (retour void attendu)

⭐ Pour aller plus loin⚓︎

  • Ajouter getTauxInteret(), setTauxInteret() avec validation
  • Ajouter dans CompteCourant une méthode estADecouvert(): bool
  • Ajouter une méthode commune resume() (même nom dans les deux classes) → tester un tableau polymorphe de comptes et appeler resume() en boucle
Correction
🐘 PHP
<?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"; }