Leçon 2/6 9 min

Visibilité et encapsulation

public, private, protected en PHP : cacher l'état d'un objet et n'exposer que des méthodes contrôlées. Getters, setters, et pourquoi le solde devient inviolable.

FR EN

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 : comme private, 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.

L'objet Compte garde sa propriété private solde derrière une frontière ; on n'agit dessus que par les méthodes public deposer et getSolde. Un accès direct est refusé. objet Compte private $solde = 120 méthodes public (les portes) deposer() getSolde() $c->solde = 999999 $c ->deposer(50) La donnée private est protégée ; on n'agit dessus que par des portes public.
La propriété private est à l'abri ; les méthodes public sont les seules portes, et elles imposent les règles.
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";
?>
Accepter ou rejeter le code de l'IA

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 »
Rejeter. $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.
Rappel libre

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 ?

PHP déclenche une 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.
Une propriété private est accessible…
Pourquoi passer par un setter plutôt qu'une propriété public ?
Prochaine étape

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.

Leçon 3 : 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: like private, 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.

The Compte object keeps its private property solde behind a boundary; you act on it only through the public methods deposer and getSolde. A direct access is refused. Compte object private $solde = 120 public methods (the doors) deposer() getSolde() $c->solde = 999999 $c ->deposer(50) The private data is protected; you act on it only through public doors.
The private property is safe; the public methods are the only doors, and they enforce the rules.
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";
?>
Next step

Your objects are well-closed. How do you create a CompteEpargne that reuses all of Compte and adds a rate, without copy-paste? Inheritance.

Lesson 3: inheritance →
Besoin d'un développeur pour votre projet ?

Réponse sous 24h · Sans engagement