🎯 PHP

Programmation Orientée Objet

De la procédurale à l'objet

Formation DWWM - 2025-2026

Au programme 📋

  • 🎯 Introduction : POO vs Procédural
  • 📖 Bases POO : Classes, propriétés, méthodes
  • 🔒 Visibilité : public, private, protected
  • 🔄 Getters/Setters : Encapsulation
  • 🏗️ Constructeur : Initialisation automatique
  • ⚡ Static : Propriétés et méthodes de classe
  • 📝 Contraintes : Types et validation
  • 💾 BONUS : Active Record (SQL dans POO)
  • ✅ BONUS : Validation des inputs

🎯 Partie 1

Introduction à la POO

Comprendre le paradigme objet

Qu'est-ce que la POO ? 🤔

POO (Programmation Orientée Objet)
Un paradigme de programmation qui organise le code en objets : des entités qui regroupent données (propriétés) et comportements (méthodes)
🤔 Pourquoi la POO ?
Pour créer du code réutilisable, organisé et maintenable qui reflète mieux la réalité

Analogie : Une voiture 🚗

  • 🏭 Plan de la voiture = Classe (le modèle)
  • 🚗 Voiture réelle = Objet (une voiture construite à partir du plan)
  • ⚙️ Caractéristiques (couleur, marque) = Propriétés (les données)
  • 🔧 Actions (démarrer, freiner) = Méthodes (ce que la voiture peut faire)
Exemple concret :
Une classe "Voiture" permet de créer plusieurs voitures différentes (objets), chacune avec sa propre couleur et marque, mais toutes peuvent démarrer et freiner de la même manière.

Comprendre Classe vs Objet 🏗️

┌─────────────────────────────────────┐ │ CLASSE Voiture (le plan) │ │ - propriétés : marque, couleur │ │ - méthodes : demarrer(), freiner()│ └─────────────────────────────────────┘ │ ├─── new Voiture() ──► Objet 1 : Peugeot rouge │ ├─── new Voiture() ──► Objet 2 : Renault bleue │ └─── new Voiture() ──► Objet 3 : Citroën noire Même plan (classe) → Différentes voitures (objets)

Avec des cookies 🍪 :

  • 🔧 Emporte-pièce = Classe (le moule)
  • 🍪 Cookie = Objet (créé avec le moule)
  • Même moule → plein de cookies différents
  • Chaque cookie peut avoir son glaçage, sa taille...

Avant la POO : Code procédural 📜

Problème : Code désorganisé, données séparées des fonctions

<?php
// Code procédural : données séparées
$voiture1_marque = "Peugeot";
$voiture1_vitesse = 0;

$voiture2_marque = "Renault";
$voiture2_vitesse = 0;

// Fonctions séparées
function demarrer(&$vitesse) {
    $vitesse = 50;
}

demarrer($voiture1_vitesse);
// Quelle voiture ? Pas clair !
?>
❌ Problèmes :
  • Les données et les fonctions sont séparées
  • Il faut créer une variable pour chaque caractéristique de chaque voiture
  • Risque de confusion : quelle vitesse pour quelle voiture ?
  • Code répétitif et difficile à maintenir

Imaginez avec 100 voitures ! Il faudrait 200 variables (marque + vitesse pour chacune). C'est ingérable ! 😵

Le problème du code procédural 😰

Avec 3 voitures en procédural : ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ $voiture1_marque = "Peugeot" $voiture1_couleur = "Rouge" $voiture1_vitesse = 0 $voiture1_carburant = 50 $voiture2_marque = "Renault" $voiture2_couleur = "Bleue" $voiture2_vitesse = 0 $voiture2_carburant = 45 $voiture3_marque = "Citroën" $voiture3_couleur = "Noire" $voiture3_vitesse = 0 $voiture3_carburant = 60 function demarrer(&$vitesse, &$carburant) { ... } function freiner(&$vitesse) { ... } function faireLePlein(&$carburant) { ... } ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ = 12 variables + 3 fonctions ❌ Désorganisé ❌ Difficile à gérer ❌ Erreurs faciles

Avec la POO : Code organisé 🎯

<?php
// Code orienté objet
class Voiture {
    // Propriétés (données)
    public $marque;
    public $vitesse = 0;
    
    // Méthodes (comportements)
    public function demarrer() {
        $this->vitesse = 50;
    }
}

// Créer des objets
$voiture1 = new Voiture();
$voiture1->marque = "Peugeot";
$voiture1->demarrer();  // Clair !

$voiture2 = new Voiture();
$voiture2->marque = "Renault";
$voiture2->demarrer();
?>
✅ Avantages :
  • Tout est regroupé dans un objet cohérent
  • Chaque voiture "sait" comment démarrer
  • Code organisé et facile à comprendre
  • Facile d'ajouter 100 voitures : juste new Voiture()

Comparaison : Avant vs Après POO 🆚

PROCÉDURAL (avant) POO (après) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 12 variables séparées 1 classe 3 fonctions séparées 3 méthodes dans la classe Créer 3 voitures : Créer 3 voitures : $v1_marque = "..." $v1 = new Voiture() $v1_vitesse = 0 $v2 = new Voiture() $v2_marque = "..." $v3 = new Voiture() $v2_vitesse = 0 $v3_marque = "..." $v3_vitesse = 0 Démarrer : Démarrer : demarrer($v1_vitesse) $v1->demarrer() (quelle voiture ?) (évident : la voiture 1) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ❌ Compliqué ✅ Simple ❌ Erreurs faciles ✅ Clair et sûr

En POO : Chaque objet "se gère lui-même". La voiture sait comment démarrer, vous lui demandez juste de le faire !

📖 Partie 2

Syntaxe de base POO

Classes, propriétés, méthodes

Déclarer une classe 📦

Classe
Un modèle (blueprint) qui définit les propriétés et méthodes d'un type d'objet
<?php
// Syntaxe de base
class NomDeLaClasse {
    // Propriétés
    // Méthodes
}

// Exemple
class Utilisateur {
    // On va remplir ici
}
?>
💡 Conventions : Nom en PascalCase, un fichier = une classe

Propriétés : Les données 📊

Propriété
Une variable qui appartient à un objet. Chaque objet a ses propres valeurs.
<?php
class Utilisateur {
    // Propriétés (caractéristiques d'un utilisateur)
    public $nom;
    public $prenom;
    public $email;
    public $age;
    
    // Propriété avec valeur par défaut
    public $actif = true;
}
?>

Analogie : Une fiche d'identité 🪪

  • Chaque propriété = un champ de la fiche
  • $nom = "Dupont"
  • $prenom = "Marie"
  • $age = 25
💡 Important : Les propriétés sont comme des tiroirs. Chaque objet a ses propres tiroirs avec ses propres valeurs !

Comprendre : Chaque objet = ses propres propriétés 🎯

Classe Utilisateur ┌─────────────────┐ │ $nom │ ← Modèle (vide au départ) │ $prenom │ │ $age │ └─────────────────┘ Objet $user1 Objet $user2 ┌─────────────────┐ ┌─────────────────┐ │ $nom = "Dupont" │ │ $nom = "Martin" │ │ $prenom = "Marie"│ │ $prenom = "Jean"│ │ $age = 25 │ │ $age = 30 │ └─────────────────┘ └─────────────────┘ Même classe → Valeurs différentes !
<?php
$user1 = new Utilisateur();
$user1->nom = "Dupont";  // Seulement pour user1

$user2 = new Utilisateur();
$user2->nom = "Martin";  // Seulement pour user2

echo $user1->nom;  // "Dupont"
echo $user2->nom;  // "Martin"
// Chacun a son propre nom !
?>

Pensez à des jumeaux : Même structure (2 bras, 2 jambes), mais noms et âges différents !

Méthodes : Les comportements 🎬

Méthode
Une fonction qui appartient à un objet. Elle peut utiliser et modifier les propriétés de l'objet.
<?php
class Utilisateur {
    public $nom;
    public $age;
    
    // Méthode : afficher les infos
    public function afficher() {
        echo "Nom : " . $this->nom . "\n";
        echo "Âge : " . $this->age . " ans\n";
    }
    
    // Méthode : modifier l'âge
    public function vieillir() {
        $this->age++;
    }
}
?>
$this = "moi-même", "cet objet"
$this->nom signifie "MON nom" (le nom de cet utilisateur)

Comprendre $this 🤔

Imaginez 2 personnes : Marie (objet $user1) Jean (objet $user2) ┌─────────────────┐ ┌─────────────────┐ │ nom = "Marie" │ │ nom = "Jean" │ │ age = 25 │ │ age = 30 │ └─────────────────┘ └─────────────────┘ Quand Marie appelle afficher() : $this->nom → "Marie" (son nom à elle) Quand Jean appelle afficher() : $this->nom → "Jean" (son nom à lui) $this = "soi-même" pour chaque objet
<?php
class Utilisateur {
    public $nom;
    
    public function sePresenter() {
        // $this->nom = MON nom (celui de cet objet)
        echo "Bonjour, je suis " . $this->nom;
    }
}

$user1 = new Utilisateur();
$user1->nom = "Marie";
$user1->sePresenter();  // "Bonjour, je suis Marie"

$user2 = new Utilisateur();
$user2->nom = "Jean";
$user2->sePresenter();  // "Bonjour, je suis Jean"

// Même méthode, résultats différents !
?>

Créer un objet (instancier) 🏗️

<?php
// 1. Créer l'objet avec new
$user = new Utilisateur();

// 2. Définir les propriétés
$user->nom = "Dupont";
$user->age = 25;

// 3. Appeler les méthodes
$user->afficher();
// Affiche :
// Nom : Dupont
// Âge : 25 ans

$user->vieillir();
echo $user->age;  // 26
?>
💡 Important : Chaque objet est indépendant

Exemple : Classe Produit 🛍️

<?php
class Produit {
    public $nom;
    public $prix;
    public $stock;
    
    public function afficher() {
        echo "Produit : " . $this->nom . "\n";
        echo "Prix : " . $this->prix . " €\n";
    }
    
    public function prixTTC($tva = 20) {
        return $this->prix * (1 + $tva / 100);
    }
    
    public function estDisponible() {
        return $this->stock > 0;
    }
}

$produit = new Produit();
$produit->nom = "Clavier";
$produit->prix = 89.99;
$produit->stock = 15;
echo $produit->prixTTC();  // 107.988
?>

🔒 Partie 3

Visibilité

public, private, protected

Les 3 niveaux de visibilité 🚦

Niveau Symbole Accessible depuis
public 🟢 Partout
private 🔴 Seulement dans la classe
protected 🟡 Classe + enfants

Analogie : Une maison 🏠

  • 🟢 public = Jardin (tout le monde)
  • 🟡 protected = Salon (famille)
  • 🔴 private = Chambre (propriétaire uniquement)

public : Accès libre 🟢

<?php
class Voiture {
    public $marque;
    public $couleur;
    
    public function demarrer() {
        echo "Vroum !";
    }
}

$voiture = new Voiture();
$voiture->marque = "Peugeot";  // ✅ OK
$voiture->demarrer();           // ✅ OK
?>

private : Accès restreint 🔴

<?php
class CompteBancaire {
    private $solde = 0;
    
    public function deposer($montant) {
        $this->solde += $montant;  // ✅ OK dans la classe
    }
    
    public function getSolde() {
        return $this->solde;  // ✅ OK dans la classe
    }
}

$compte = new CompteBancaire();
$compte->deposer(100);
echo $compte->getSolde();  // 100 ✅

// echo $compte->solde;  // ❌ ERREUR ! Private
?>
🤔 Pourquoi private ?
Protéger les données sensibles et contrôler les modifications

Pourquoi private est important ? 🛡️

Scénario : Un compte bancaire

<?php
// ❌ AVEC public : DANGEREUX !
class CompteBancaire {
    public $solde = 0;
}

$compte = new CompteBancaire();
$compte->solde = 1000000;  // 😈 Je me donne 1 million !
// Pas de contrôle → Problème !
?>
<?php
// ✅ AVEC private : SÉCURISÉ !
class CompteBancaire {
    private $solde = 0;
    
    public function deposer($montant) {
        // Vérifications avant modification
        if ($montant > 0) {
            $this->solde += $montant;
        }
    }
}

$compte = new CompteBancaire();
// $compte->solde = 1000000;  // ❌ IMPOSSIBLE !
$compte->deposer(100);  // ✅ Seule façon autorisée
?>
Principe : private = porte verrouillée. On passe par les méthodes publiques (la porte officielle) pour accéder aux données.

Analogie : La maison 🏠

Votre maison : 🟢 PUBLIC = Jardin → Tout le monde peut voir → Ex: $couleur, $adresse 🟡 PROTECTED = Salon → Seulement famille (héritage) → Ex: $revenus (parents + enfants) 🔴 PRIVATE = Chambre → Seulement vous → Ex: $motDePasse, $solde

Dans la vraie vie :

  • Vous ne laissez pas n'importe qui entrer dans votre chambre
  • Vous ne montrez pas votre mot de passe bancaire à tout le monde
  • En code, c'est pareil : les données sensibles doivent être private

Bonne pratique 📋

💡 Règle d'or : Par défaut, tout en private
<?php
class Utilisateur {
    // ❌ À ÉVITER
    public $motDePasse;
    
    // ✅ MIEUX
    private $motDePasse;
    
    public function setMotDePasse($mdp) {
        // Validation avant modification
        if (strlen($mdp) >= 8) {
            $this->motDePasse = $mdp;
        }
    }
}
?>

🔄 Partie 4

Getters et Setters

L'encapsulation en action

Qu'est-ce qu'un Getter / Setter ? 🤔

Getter = Méthode qui retourne (lit) une propriété private
Setter = Méthode qui modifie (écrit) une propriété private

Analogie : Un coffre-fort 🔐

  • 💰 Propriété private = argent dans le coffre (caché)
  • 👁️ Getter = fenêtre pour voir combien il y a
  • 🔑 Setter = clé contrôlée pour déposer/retirer
  • ❌ On ne peut PAS ouvrir directement le coffre !
Sans getters/setters : C'est comme laisser votre argent sur la table. N'importe qui peut le prendre ou le modifier.

Avec getters/setters : Votre argent est dans un coffre. Vous contrôlez qui peut voir combien il y a et qui peut déposer/retirer.

Le problème sans getters/setters 😰

<?php
// ❌ SANS getters/setters : DANGEREUX
class Utilisateur {
    public $age;  // N'importe qui peut modifier !
}

$user = new Utilisateur();
$user->age = 25;    // OK
$user->age = -50;   // 😱 Âge négatif accepté !
$user->age = 500;   // 😱 Âge impossible accepté !
$user->age = "abc"; // 😱 Texte au lieu de nombre !

// Aucun contrôle → Données invalides
?>
<?php
// ✅ AVEC getters/setters : SÉCURISÉ
class Utilisateur {
    private $age;  // Protégé
    
    public function setAge($age) {
        // VALIDATION avant modification
        if ($age >= 0 && $age <= 150) {
            $this->age = $age;
        } else {
            throw new Exception("Âge invalide !");
        }
    }
    
    public function getAge() {
        return $this->age;
    }
}

$user = new Utilisateur();
$user->setAge(25);    // ✅ OK
$user->setAge(-50);   // ❌ Exception !
$user->setAge(500);   // ❌ Exception !
// Contrôle total → Données valides
?>

Syntaxe des Getters 👁️

<?php
class Utilisateur {
    private $nom;
    private $email;
    
    // Getter
    public function getNom() {
        return $this->nom;
    }
    
    public function getEmail() {
        return $this->email;
    }
}

$user = new Utilisateur();
echo $user->getNom();  // ✅ OK
?>
💡 Convention : get + NomPropriété

Syntaxe des Setters 🔑

<?php
class Utilisateur {
    private $nom;
    private $age;
    
    // Setter
    public function setNom($nom) {
        $this->nom = $nom;
    }
    
    public function setAge($age) {
        $this->age = $age;
    }
}

$user = new Utilisateur();
$user->setNom("Dupont");  // ✅ OK
?>
💡 Convention : set + NomPropriété

Pourquoi Getters/Setters ? 🤔

<?php
class Utilisateur {
    private $age;
    
    // Setter AVEC VALIDATION
    public function setAge($age) {
        if ($age > 0 && $age < 150) {
            $this->age = $age;
        } else {
            throw new Exception("Âge invalide !");
        }
    }
    
    public function getAge() {
        return $this->age;
    }
}

$user = new Utilisateur();
$user->setAge(25);   // ✅ OK
$user->setAge(-5);   // ❌ Exception
$user->setAge(200);  // ❌ Exception
?>

Validation d'email 📧

<?php
class Utilisateur {
    private $email;
    
    public function setEmail($email) {
        // Validation du format
        if (filter_var($email, FILTER_VALIDATE_EMAIL)) {
            $this->email = $email;
        } else {
            throw new Exception("Email invalide !");
        }
    }
    
    public function getEmail() {
        return $this->email;
    }
}

$user = new Utilisateur();
$user->setEmail("marie@example.com");  // ✅ OK
$user->setEmail("pas-un-email");       // ❌ Exception
?>

Propriété en lecture seule 🔒

<?php
class Utilisateur {
    private $id;
    
    // Getter UNIQUEMENT
    public function getId() {
        return $this->id;
    }
    
    // PAS de setter !
    // L'ID ne peut pas être modifié
}

$user = new Utilisateur();
echo $user->getId();  // ✅ OK
// $user->setId(42);  // ❌ N'existe pas
?>

🏗️ Partie 5

Le Constructeur

Initialisation automatique

Le constructeur __construct() 🏗️

Constructeur
Méthode magique appelée automatiquement quand on crée un objet avec new
<?php
class Utilisateur {
    private $nom;
    private $email;
    
    // Constructeur : s'exécute automatiquement
    public function __construct($nom, $email) {
        $this->nom = $nom;
        $this->email = $email;
        echo "Nouvel utilisateur créé !\n";
    }
}

// Dès qu'on fait "new", le constructeur s'exécute !
$user = new Utilisateur("Dupont", "dupont@example.com");
// Affiche automatiquement : "Nouvel utilisateur créé !"
?>

Analogie : Naissance d'un bébé 👶

  • Le constructeur = première respiration (automatique)
  • Définit le nom, la date de naissance...
  • Se produit UNE SEULE FOIS à la naissance
  • Vous ne pouvez pas "naître" deux fois !

Pourquoi utiliser un constructeur ? 🤔

❌ SANS constructeur : ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ $user = new Utilisateur(); $user->setNom("Dupont"); $user->setPrenom("Marie"); $user->setEmail("marie@example.com"); $user->setAge(25); 5 LIGNES pour créer 1 utilisateur ! Facile d'oublier une ligne... ✅ AVEC constructeur : ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ $user = new Utilisateur("Dupont", "Marie", "marie@example.com", 25); 1 LIGNE seulement ! Impossible d'oublier une info !
Avantage principal : Le constructeur garantit que l'objet est créé avec TOUTES les infos nécessaires dès le départ. Pas de "demi-objet" incomplet !

Constructeur : Comment ça marche ? ⚙️

<?php
class Utilisateur {
    private $nom;
    private $email;
    
    public function __construct($nom, $email) {
        echo "1. Constructeur appelé !\n";
        $this->nom = $nom;
        $this->email = $email;
        echo "2. Propriétés définies !\n";
    }
}

echo "Avant new\n";
$user = new Utilisateur("Dupont", "dupont@example.com");
echo "Après new\n";

// Affichage :
// Avant new
// 1. Constructeur appelé !
// 2. Propriétés définies !
// Après new
?>

Déroulement :

  1. PHP voit new Utilisateur(...)
  2. PHP crée un nouvel objet vide en mémoire
  3. PHP appelle automatiquement __construct()
  4. Le constructeur initialise les propriétés
  5. L'objet complet est retourné dans $user

Avant/Après constructeur ⚖️

❌ SANS constructeur : $user = new Utilisateur(); $user->setNom("Dupont"); $user->setEmail("dupont@example.com"); $user->setAge(25); // 4 lignes ! ✅ AVEC constructeur : $user = new Utilisateur("Dupont", "dupont@example.com", 25); // 1 ligne !
<?php
class Utilisateur {
    private $nom;
    private $email;
    private $age;
    
    public function __construct($nom, $email, $age) {
        $this->nom = $nom;
        $this->email = $email;
        $this->age = $age;
    }
}
?>

Constructeur avec validation 🛡️

<?php
class Utilisateur {
    private $nom;
    private $email;
    
    public function __construct($nom, $email) {
        if (empty($nom)) {
            throw new Exception("Nom vide");
        }
        
        if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
            throw new Exception("Email invalide");
        }
        
        $this->nom = $nom;
        $this->email = $email;
    }
}

$user = new Utilisateur("Dupont", "dupont@example.com");  // ✅
$user = new Utilisateur("", "invalide");  // ❌ Exception
?>

Valeurs par défaut 🎯

<?php
class Utilisateur {
    private $nom;
    private $actif;
    private $role;
    
    public function __construct(
        $nom, 
        $actif = true,           // Défaut
        $role = "utilisateur"     // Défaut
    ) {
        $this->nom = $nom;
        $this->actif = $actif;
        $this->role = $role;
    }
}

$user1 = new Utilisateur("Dupont");
// actif = true, role = "utilisateur"

$user2 = new Utilisateur("Martin", false, "admin");
?>

⚡ Partie 6

Static

Propriétés et méthodes de classe

Qu'est-ce que static ? ⚡

Static
Propriété ou méthode qui appartient à la classe (pas à un objet particulier). C'est partagé par TOUS les objets de cette classe.

Analogie : Compteur de personnes dans un magasin 🔢

  • Chaque personne = objet différent (nom, âge différents)
  • Compteur à l'entrée = static (UN SEUL compteur pour tout le monde)
  • Le compteur augmente à chaque nouvelle personne
  • Tout le monde partage le même compteur
PROPRIÉTÉ NORMALE PROPRIÉTÉ STATIC ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ $user1->nom = "Marie" Utilisateur::$compteur $user2->nom = "Jean" = 2 $user3->nom = "Paul" (partagé par tous) Chaque objet a UN SEUL compteur son propre nom pour toute la classe

Static : Exemple concret de compteur 📊

<?php
class Utilisateur {
    private $nom;
    // STATIC : partagé par TOUS les utilisateurs
    private static $compteur = 0;
    
    public function __construct($nom) {
        $this->nom = $nom;
        // Chaque nouvel utilisateur incrémente LE MÊME compteur
        self::$compteur++;
        echo "Utilisateur #{self::$compteur} créé : {$nom}\n";
    }
    
    public static function getCompteur() {
        return self::$compteur;
    }
}

$user1 = new Utilisateur("Marie");
// Affiche : Utilisateur #1 créé : Marie

$user2 = new Utilisateur("Jean");
// Affiche : Utilisateur #2 créé : Jean

$user3 = new Utilisateur("Paul");
// Affiche : Utilisateur #3 créé : Paul

echo Utilisateur::getCompteur();  // 3
// Pas besoin d'objet pour lire le compteur !
?>

Comprendre : Normal vs Static 🆚

┌─ Classe Utilisateur ──────────────┐ │ │ │ Propriété NORMALE : $nom │ │ → Chaque objet a le sien │ │ │ │ Propriété STATIC : $compteur │ │ → UN SEUL pour toute la classe │ │ │ └────────────────────────────────────┘ │ │ │ ▼ ▼ ▼ ┌────────┐ ┌────────┐ ┌────────┐ │ Marie │ │ Jean │ │ Paul │ │ nom │ │ nom │ │ nom │ └────────┘ └────────┘ └────────┘ ▲ ▲ ▲ └──────────┴──────────┘ │ ▼ ┌──────────┐ │compteur=3│ ← PARTAGÉ ! └──────────┘

Règle simple :

  • Propriété NORMALE → Chaque objet a SA valeur
  • Propriété STATIC → TOUS les objets partagent LA MÊME valeur

Méthode static : Utilité 🔧

<?php
class MathUtils {
    // Méthodes static = fonctions utilitaires
    public static function carre($n) {
        return $n * $n;
    }
    
    public static function cube($n) {
        return $n * $n * $n;
    }
}

// On utilise DIRECTEMENT sans créer d'objet
echo MathUtils::carre(5);  // 25
echo MathUtils::cube(3);   // 27

// Pas besoin de faire : $math = new MathUtils();
?>
Quand utiliser static ?
  • Fonctions utilitaires (Math, Date, String...)
  • Compteurs globaux
  • Configuration partagée
  • Quand on n'a PAS BESOIN d'objet spécifique

Analogie : C'est comme une calculatrice. Pas besoin d'acheter une calculatrice personnelle pour faire 2+2. Vous utilisez juste la fonction de calcul !

Propriété statique 📊

<?php
class Utilisateur {
    private $nom;
    private static $compteur = 0;
    
    public function __construct($nom) {
        $this->nom = $nom;
        self::$compteur++;
    }
    
    public static function getCompteur() {
        return self::$compteur;
    }
}

$user1 = new Utilisateur("Dupont");
echo Utilisateur::getCompteur();  // 1

$user2 = new Utilisateur("Martin");
echo Utilisateur::getCompteur();  // 2

// Pas besoin de créer un objet !
?>

Méthode statique 🔧

<?php
class MathUtils {
    public static function carre($nombre) {
        return $nombre * $nombre;
    }
    
    public static function cube($nombre) {
        return $nombre * $nombre * $nombre;
    }
}

// Utilisation SANS créer d'objet
echo MathUtils::carre(5);  // 25
echo MathUtils::cube(3);   // 27
?>
🤔 Quand utiliser static ?
Fonctions utilitaires, compteurs globaux, factory methods

self vs $this 🆚

$this ----- - Objet actuel - Propriétés d'instance - Exemple : $this->nom self ---- - Classe - Propriétés/méthodes static - Exemple : self::$compteur
<?php
class Utilisateur {
    private $nom;                // Instance
    private static $compteur;    // Static
    
    public function getNom() {
        return $this->nom;       // $this
    }
    
    public static function getCompteur() {
        return self::$compteur;  // self
    }
}
?>

📝 Partie 7

Contraintes de type

Type hints

Type hints 🎯

Type Hint
Spécifier le type attendu pour paramètres et retours
<?php
class Utilisateur {
    private string $nom;
    private int $age;
    
    public function __construct(string $nom, int $age) {
        $this->nom = $nom;
        $this->age = $age;
    }
    
    public function getNom(): string {
        return $this->nom;
    }
    
    public function getAge(): int {
        return $this->age;
    }
}

$user = new Utilisateur("Dupont", 25);  // ✅
$user = new Utilisateur("Dupont", "25");  // ❌ Erreur
?>

Types disponibles 📋

Type Description Exemple
string Chaîne "Dupont"
int Entier 25
float Décimal 19.99
bool Booléen true/false
array Tableau [1, 2, 3]

Nullable (accepter null) ❓

<?php
class Utilisateur {
    private string $nom;
    private ?string $telephone;  // Peut être null
    
    public function __construct(
        string $nom, 
        ?string $telephone = null
    ) {
        $this->nom = $nom;
        $this->telephone = $telephone;
    }
    
    public function getTelephone(): ?string {
        return $this->telephone;
    }
}

$user1 = new Utilisateur("Dupont");  // tel = null ✅
$user2 = new Utilisateur("Martin", "0601020304");  // ✅
?>
💡 Syntaxe : ?Type = Type OU null

💾 BONUS 1

Active Record Pattern

SQL dans la POO

Qu'est-ce que l'Active Record ? 🗄️

Active Record
Pattern où chaque objet = une ligne de la BDD
Base de données Code PHP ------------------ -------------- Table : articles Classe : Article Ligne : id=1 Objet : $article SELECT * ... -> Article::findAll() SELECT WHERE... -> Article::findById(5) INSERT... -> $article->save() UPDATE... -> $article->save() DELETE... -> $article->delete()

Connexion BDD 🔌

<?php
class Database {
    private static ?PDO $pdo = null;
    
    public static function getConnection(): PDO {
        if (self::$pdo === null) {
            self::$pdo = new PDO(
                'mysql:host=localhost;dbname=ma_base',
                'user',
                'password'
            );
            self::$pdo->setAttribute(
                PDO::ATTR_ERRMODE, 
                PDO::ERRMODE_EXCEPTION
            );
        }
        return self::$pdo;
    }
}
?>

Classe Article 📝

<?php
class Article {
    private ?int $id = null;
    private string $titre;
    private string $contenu;
    
    public function __construct(string $titre, string $contenu) {
        $this->titre = $titre;
        $this->contenu = $contenu;
    }
    
    public function getId(): ?int {
        return $this->id;
    }
    
    public function getTitre(): string {
        return $this->titre;
    }
    
    public function getContenu(): string {
        return $this->contenu;
    }
}
?>

Méthode save() 💾

<?php
class Article {
    // ... propriétés ...
    
    public function save(): bool {
        $pdo = Database::getConnection();
        
        if ($this->id === null) {
            // INSERT
            $sql = "INSERT INTO articles (titre, contenu) 
                    VALUES (:titre, :contenu)";
            $stmt = $pdo->prepare($sql);
            $result = $stmt->execute([
                ':titre' => $this->titre,
                ':contenu' => $this->contenu
            ]);
            
            if ($result) {
                $this->id = (int) $pdo->lastInsertId();
            }
            return $result;
        } else {
            // UPDATE
            $sql = "UPDATE articles 
                    SET titre = :titre, contenu = :contenu 
                    WHERE id = :id";
            $stmt = $pdo->prepare($sql);
            return $stmt->execute([
                ':titre' => $this->titre,
                ':contenu' => $this->contenu,
                ':id' => $this->id
            ]);
        }
    }
}
?>

Méthode findById() 🔍

<?php
class Article {
    // ... autres méthodes ...
    
    public static function findById(int $id): ?Article {
        $pdo = Database::getConnection();
        $sql = "SELECT * FROM articles WHERE id = :id";
        $stmt = $pdo->prepare($sql);
        $stmt->execute([':id' => $id]);
        
        $data = $stmt->fetch(PDO::FETCH_ASSOC);
        if (!$data) {
            return null;
        }
        
        $article = new Article($data['titre'], $data['contenu']);
        $article->id = (int) $data['id'];
        return $article;
    }
}

// Utilisation
$article = Article::findById(5);
if ($article) {
    echo $article->getTitre();
}
?>

Méthode findAll() 📚

<?php
class Article {
    public static function findAll(): array {
        $pdo = Database::getConnection();
        $sql = "SELECT * FROM articles ORDER BY id DESC";
        $stmt = $pdo->query($sql);
        
        $articles = [];
        while ($data = $stmt->fetch(PDO::FETCH_ASSOC)) {
            $article = new Article($data['titre'], $data['contenu']);
            $article->id = (int) $data['id'];
            $articles[] = $article;
        }
        
        return $articles;
    }
}

// Utilisation
$articles = Article::findAll();
foreach ($articles as $article) {
    echo $article->getTitre() . "\n";
}
?>

Utilisation complète 🎯

<?php
// CRÉER
$article = new Article("Mon titre", "Mon contenu");
$article->save();  // INSERT

// MODIFIER
$article->setTitre("Nouveau titre");
$article->save();  // UPDATE

// RÉCUPÉRER
$article = Article::findById(5);
echo $article->getTitre();

// LISTE
$articles = Article::findAll();

// SUPPRIMER
$article->delete();
?>

✅ BONUS 2

Validation des inputs

Sécurité et validation

Pourquoi valider ? 🛡️

Objectifs :
  • Sécurité (injection SQL, XSS)
  • Intégrité des données
  • Messages d'erreur clairs
Sans validation : Age négatif, email invalide, injection SQL...

Validation dans les setters 🔧

<?php
class Utilisateur {
    private string $nom;
    
    public function setNom(string $nom): void {
        // Nettoyer
        $nom = trim($nom);
        
        // Vérifier non vide
        if (empty($nom)) {
            throw new Exception("Nom vide");
        }
        
        // Vérifier longueur
        if (strlen($nom) < 2 || strlen($nom) > 50) {
            throw new Exception("Nom : 2-50 caractères");
        }
        
        $this->nom = $nom;
    }
}
?>

Validation email 📧

<?php
class Utilisateur {
    private string $email;
    
    public function setEmail(string $email): void {
        $email = trim(strtolower($email));
        
        if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
            throw new Exception("Email invalide");
        }
        
        $this->email = $email;
    }
}

$user = new Utilisateur();
$user->setEmail("marie@example.com");  // ✅
$user->setEmail("pas-email");          // ❌
?>

Validation mot de passe 🔐

<?php
class Utilisateur {
    private string $motDePasse;
    
    public function setMotDePasse(string $mdp): void {
        if (strlen($mdp) < 8) {
            throw new Exception("Min 8 caractères");
        }
        
        if (!preg_match('/[A-Z]/', $mdp)) {
            throw new Exception("Une majuscule requise");
        }
        
        if (!preg_match('/[0-9]/', $mdp)) {
            throw new Exception("Un chiffre requis");
        }
        
        // Hasher
        $this->motDePasse = password_hash($mdp, PASSWORD_DEFAULT);
    }
}
?>

Classe Validator 🔧

<?php
class Validator {
    public static function email(string $email): bool {
        return filter_var($email, FILTER_VALIDATE_EMAIL) !== false;
    }
    
    public static function range($value, $min, $max): bool {
        return $value >= $min && $value <= $max;
    }
    
    public static function length(string $str, int $min, int $max): bool {
        $len = strlen($str);
        return $len >= $min && $len <= $max;
    }
}

if (!Validator::email($email)) {
    throw new Exception("Email invalide");
}
?>

Protection SQL injection 💉

<?php
class Article {
    // ❌ DANGEREUX
    public static function findDangereux($titre) {
        $sql = "SELECT * FROM articles WHERE titre = '$titre'";
        // Injection possible !
    }
    
    // ✅ SÛR : requête préparée
    public static function findByTitre(string $titre): array {
        $pdo = Database::getConnection();
        $sql = "SELECT * FROM articles WHERE titre = :titre";
        $stmt = $pdo->prepare($sql);
        $stmt->execute([':titre' => $titre]);
        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    }
}
?>
TOUJOURS utiliser des requêtes préparées

Protection XSS 🛡️

<?php
class Article {
    private string $titre;
    
    public function setTitre(string $titre): void {
        // Échapper les balises HTML
        $this->titre = htmlspecialchars($titre, ENT_QUOTES, 'UTF-8');
    }
}

$article = new Article();
$article->setTitre("Titre avec balise script");
// Les balises sont échappées, pas exécutées
?>
💡 Règle : Toujours échapper avant affichage HTML

Résumé sécurité ✅

Contexte Solution
Email filter_var FILTER_VALIDATE_EMAIL
SQL Requêtes préparées PDO
HTML htmlspecialchars
Mot de passe password_hash
Principe : Ne jamais faire confiance aux données utilisateur

🎓 Récapitulatif

Ce que nous avons vu

Concepts clés 🔑

Concept Description
Classe Modèle pour créer des objets
Objet Instance d'une classe
Propriété Variable de classe
Méthode Fonction de classe
Visibilité public/private/protected
Getter/Setter Accès contrôlé aux propriétés
Constructeur Initialisation automatique
Static Appartient à la classe

Points clés 💡

  • 🏗️ Classes = plans, Objets = instances
  • 🔒 Toujours private par défaut
  • 🔄 Getters/Setters pour encapsulation
  • ✅ Valider dans setters et constructeur
  • ⚡ Static pour utilitaires et compteurs
  • 💾 Active Record pour simplifier BDD
  • 🛡️ Sécurité : requêtes préparées + htmlspecialchars

Pour aller plus loin 🚀

  • 🔗 Héritage (extends)
  • 📜 Interfaces
  • 🎭 Classes abstraites
  • 🎯 Traits
  • 📦 Namespaces
  • 🔄 Autoloading
  • 🏛️ Design Patterns
  • 🎨 Principes SOLID

Fin du cours 🎓

Merci !

Questions ?