Le problème : tout est ouvert
À la leçon 1, $solde était public. Donc n'importe où dans le code : $compte->solde = 999999; — on contourne deposer() et ses règles. C'est précisément l'encapsulation (vue dans le cours POO) qu'il faut appliquer ici, avec les mots-clés de visibilité de PHP.
public, private, protected
public: accessible de partout (l'interface de l'objet).private: accessible uniquement à l'intérieur de la classe. C'est le coffre-fort.protected: commeprivate, mais aussi accessible dans les classes filles (utile avec l'héritage, prochaine leçon).
On met les données en private par défaut, et on n'expose en public que les méthodes nécessaires.
class Compte {
private float $solde = 0; // verrouillé
public function deposer(float $m): void {
if ($m > 0) $this->solde += $m; // la règle vit ici
}
public function getSolde(): float { // accesseur en lecture seule
return $this->solde;
}
}
$c = new Compte();
$c->deposer(50);
echo $c->getSolde(); // 50
// $c->solde = 999999; // ❌ Fatal error: Cannot access private property
Tenter $c->solde depuis l'extérieur déclenche une erreur fatale : PHP refuse. La seule voie, c'est deposer(), qui impose ses règles. Un solde incohérent devient impossible.
Getters et setters
Pour exposer une donnée privée de façon contrôlée : un getter (lire) et, si besoin, un setter (écrire avec validation). Le setter est l'occasion de refuser une valeur invalide.
class Produit {
private float $prix = 0;
public function getPrix(): float { return $this->prix; }
public function setPrix(float $prix): void {
if ($prix < 0) {
throw new InvalidArgumentException("Prix négatif interdit");
}
$this->prix = $prix;
}
}
La capsule en vrai (exécute)
Un compte qui protège son solde. La voie normale marche ; la règle (pas de retrait à découvert) est imposée par la méthode.
<?php
class Compte {
private float $solde = 0;
public function deposer(float $m): void {
if ($m > 0) $this->solde += $m;
}
public function retirer(float $m): bool {
if ($m > 0 && $m <= $this->solde) {
$this->solde -= $m;
return true;
}
return false; // règle : pas de découvert
}
public function getSolde(): float { return $this->solde; }
}
$c = new Compte();
$c->deposer(100);
var_dump($c->retirer(30)); // bool(true) → solde 70
var_dump($c->retirer(99999)); // bool(false) → refusé
echo "Solde final : " . $c->getSolde() . " €\n";
?>
L'IA propose cette classe Panier pour un site marchand. Ton rôle de relecteur : l'accepter telle quelle ou la rejeter, et dire pourquoi.
class Panier {
public float $total = 0;
public function ajouter(float $prix): void {
$this->total += $prix;
}
}
$p = new Panier();
$p->ajouter(29.90);
$p->total = -500; // remise « surprise »
$total est public : n'importe qui peut écrire $p->total = -500 et contourner la seule règle qui compte (un total se construit par ajouter()). C'est exactement le problème de la leçon. Le réflexe pro : private float $total, un getTotal() en lecture seule, et la validation (prix > 0) dans ajouter(). La donnée redevient inviolable.Sans remonter dans la leçon : que se passe-t-il si on écrit $c->solde = 999999 alors que $solde est private, et par quoi doit-on passer à la place ?
Fatal error: Cannot access private property : depuis l'extérieur de la classe, on ne peut ni lire ni écrire une propriété private. La seule voie est une méthode public de la classe (par exemple deposer() pour écrire, getSolde() pour lire), qui peut imposer ses règles avant de toucher à la donnée.Tes objets sont bien fermés. Comment créer un CompteEpargne qui réutilise tout Compte et ajoute un taux, sans copier-coller ? L'héritage.
The problem: everything is open
In lesson 1, $solde was public. So anywhere in the code: $compte->solde = 999999; — bypassing deposer() and its rules. This is exactly the encapsulation (seen in the OOP course) we must apply here, with PHP's visibility keywords.
public, private, protected
public: accessible from everywhere (the object's interface).private: accessible only inside the class. The safe.protected: likeprivate, but also accessible in child classes (useful with inheritance, next lesson).
Keep data private by default, and expose as public only the methods you need.
class Compte {
private float $solde = 0; // locked
public function deposer(float $m): void {
if ($m > 0) $this->solde += $m; // the rule lives here
}
public function getSolde(): float { // read-only accessor
return $this->solde;
}
}
$c = new Compte();
$c->deposer(50);
echo $c->getSolde(); // 50
// $c->solde = 999999; // ❌ Fatal error: Cannot access private property
Trying $c->solde from outside triggers a fatal error: PHP refuses. The only way is deposer(), which enforces its rules. An inconsistent balance becomes impossible.
Getters and setters
To expose a private value in a controlled way: a getter (read) and, if needed, a setter (write with validation). The setter is your chance to reject an invalid value.
class Produit {
private float $prix = 0;
public function getPrix(): float { return $this->prix; }
public function setPrix(float $prix): void {
if ($prix < 0) {
throw new InvalidArgumentException("Negative price not allowed");
}
$this->prix = $prix;
}
}
The capsule for real (run it)
An account protecting its balance. The normal path works; the rule (no overdraft) is enforced by the method.
<?php
class Compte {
private float $solde = 0;
public function deposer(float $m): void {
if ($m > 0) $this->solde += $m;
}
public function retirer(float $m): bool {
if ($m > 0 && $m <= $this->solde) {
$this->solde -= $m;
return true;
}
return false; // rule: no overdraft
}
public function getSolde(): float { return $this->solde; }
}
$c = new Compte();
$c->deposer(100);
var_dump($c->retirer(30)); // bool(true) → balance 70
var_dump($c->retirer(99999)); // bool(false) → refused
echo "Final balance: " . $c->getSolde() . " €\n";
?>
Your objects are well-closed. How do you create a CompteEpargne that reuses all of Compte and adds a rate, without copy-paste? Inheritance.