PHP est un langage serveur puissant pour créer des applications web dynamiques. Gestion de formulaires, sessions, cookies, bases de données, upload de fichiers et création d'APIs REST. PHP 8 apporte de nombreuses améliorations modernes.

Bases PHP

PHP
<?php
// Variables
$nom = "Nicolas";
$age = 25;
$prix = 19.99;
$actif = true;

// Afficher
echo "Bonjour $nom";
echo 'Bonjour ' . $nom;  // Concaténation
print_r($array);          // Afficher array
var_dump($variable);      // Debug

// Constantes
define('API_KEY', 'abc123');
const DB_HOST = 'localhost';

// Tableaux
$fruits = ["pomme", "banane", "orange"];
$fruits[] = "kiwi";  // Ajouter

// Tableau associatif
$personne = [
    'nom' => 'Lema',
    'prenom' => 'Nicolas',
    'age' => 25
];

echo $personne['nom'];

// Conditions
if ($age >= 18) {
    echo "Majeur";
} elseif ($age >= 16) {
    echo "Adolescent";
} else {
    echo "Mineur";
}

// Switch
switch ($jour) {
    case 'lundi':
        echo "Début semaine";
        break;
    case 'samedi':
    case 'dimanche':
        echo "Weekend";
        break;
    default:
        echo "Jour normal";
}

// Boucles
for ($i = 0; $i < 10; $i++) {
    echo $i;
}

foreach ($fruits as $fruit) {
    echo $fruit;
}

foreach ($personne as $cle => $valeur) {
    echo "$cle : $valeur";
}

// Fonctions
function saluer($nom, $titre = "M.") {
    return "Bonjour $titre $nom";
}

echo saluer("Dupont");
echo saluer("Martin", "Mme");
?>

Traiter des Formulaires

Formulaire HTML

HTML
<form action="traitement.php" method="POST">
    <input type="text" name="nom" required>
    <input type="email" name="email" required>
    <input type="password" name="password" required>
    <textarea name="message"></textarea>
    <button type="submit">Envoyer</button>
</form>

Traitement PHP

PHP
<?php
// Vérifier si formulaire soumis
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    
    // Récupérer données (dangereux - voir sécurité)
    $nom = $_POST['nom'];
    $email = $_POST['email'];
    
    // Sécurisé - filtrer et valider
    $nom = filter_input(INPUT_POST, 'nom', FILTER_SANITIZE_STRING);
    $email = filter_input(INPUT_POST, 'email', FILTER_VALIDATE_EMAIL);
    
    // Vérifier email valide
    if ($email === false) {
        die("Email invalide");
    }
    
    // Échapper pour HTML (XSS protection)
    $nom_safe = htmlspecialchars($nom, ENT_QUOTES, 'UTF-8');
    
    // Traiter...
    echo "Merci $nom_safe";
}

// GET
$id = $_GET['id'] ?? null;  // Null coalescing operator
$page = $_GET['page'] ?? 1;

// Redirection
header('Location: merci.php');
exit;
?>
Sécurité : Ne jamais faire confiance aux données utilisateur ! Toujours filtrer, valider et échapper. Utiliser des requêtes préparées pour SQL.

Base de Données avec PDO

Connexion

PHP
<?php
// Connexion PDO (recommandé)
try {
    $pdo = new PDO(
        'mysql:host=localhost;dbname=ma_base;charset=utf8mb4',
        'username',
        'password',
        [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
            PDO::ATTR_EMULATE_PREPARES => false
        ]
    );
} catch (PDOException $e) {
    die("Erreur connexion : " . $e->getMessage());
}
?>

Requêtes préparées (sécurisées)

PHP
<?php
// SELECT avec paramètres
$stmt = $pdo->prepare('SELECT * FROM users WHERE email = :email');
$stmt->execute(['email' => $email]);
$user = $stmt->fetch();

if ($user) {
    echo "Bonjour " . $user['nom'];
}

// SELECT multiple
$stmt = $pdo->prepare('SELECT * FROM products WHERE category_id = ?');
$stmt->execute([$category_id]);
$products = $stmt->fetchAll();

foreach ($products as $product) {
    echo $product['name'];
}

// INSERT
$stmt = $pdo->prepare('
    INSERT INTO users (nom, email, password) 
    VALUES (:nom, :email, :password)
');

$stmt->execute([
    'nom' => $nom,
    'email' => $email,
    'password' => password_hash($password, PASSWORD_DEFAULT)
]);

$user_id = $pdo->lastInsertId();

// UPDATE
$stmt = $pdo->prepare('
    UPDATE users 
    SET email = :email 
    WHERE id = :id
');

$stmt->execute([
    'email' => $new_email,
    'id' => $user_id
]);

$rows_affected = $stmt->rowCount();

// DELETE
$stmt = $pdo->prepare('DELETE FROM users WHERE id = ?');
$stmt->execute([$user_id]);

// Transaction
try {
    $pdo->beginTransaction();
    
    // Plusieurs requêtes...
    $stmt1->execute([...]);
    $stmt2->execute([...]);
    
    $pdo->commit();
} catch (Exception $e) {
    $pdo->rollBack();
    throw $e;
}
?>

Sessions et Cookies

Sessions

PHP
<?php
// Démarrer session (au début du fichier)
session_start();

// Enregistrer données
$_SESSION['user_id'] = 123;
$_SESSION['username'] = 'nicolas';
$_SESSION['role'] = 'admin';

// Lire données
if (isset($_SESSION['user_id'])) {
    $user_id = $_SESSION['user_id'];
    echo "Utilisateur connecté : " . $_SESSION['username'];
}

// Vérifier connexion
function isLoggedIn() {
    return isset($_SESSION['user_id']);
}

// Protéger page
if (!isLoggedIn()) {
    header('Location: login.php');
    exit;
}

// Détruire session (déconnexion)
session_unset();
session_destroy();
setcookie(session_name(), '', time() - 3600, '/');
?>

Cookies

PHP
<?php
// Créer cookie (avant tout output HTML)
setcookie(
    'nom_cookie',           // Nom
    'valeur',              // Valeur
    time() + (86400 * 30), // Expire dans 30 jours
    '/',                   // Path
    '',                    // Domain
    true,                  // Secure (HTTPS uniquement)
    true                   // HttpOnly (pas accessible JS)
);

// Lire cookie
if (isset($_COOKIE['nom_cookie'])) {
    $valeur = $_COOKIE['nom_cookie'];
}

// Supprimer cookie
setcookie('nom_cookie', '', time() - 3600, '/');

// Cookie "Se souvenir de moi"
if (isset($_POST['remember'])) {
    $token = bin2hex(random_bytes(32));
    setcookie('remember_token', $token, time() + (86400 * 30), '/', '', true, true);
    // Stocker token en base avec user_id
}
?>

Upload de Fichiers

Formulaire

HTML
<form action="upload.php" method="POST" enctype="multipart/form-data">
    <input type="file" name="fichier" accept="image/*" required>
    <button type="submit">Envoyer</button>
</form>

Traitement sécurisé

PHP
<?php
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['fichier'])) {
    $file = $_FILES['fichier'];
    
    // Vérifier erreurs
    if ($file['error'] !== UPLOAD_ERR_OK) {
        die("Erreur upload : " . $file['error']);
    }
    
    // Vérifier taille (5MB max)
    $max_size = 5 * 1024 * 1024;
    if ($file['size'] > $max_size) {
        die("Fichier trop volumineux");
    }
    
    // Vérifier type MIME
    $allowed_types = ['image/jpeg', 'image/png', 'image/gif'];
    $finfo = finfo_open(FILEINFO_MIME_TYPE);
    $mime_type = finfo_file($finfo, $file['tmp_name']);
    finfo_close($finfo);
    
    if (!in_array($mime_type, $allowed_types)) {
        die("Type de fichier non autorisé");
    }
    
    // Générer nom unique
    $extension = pathinfo($file['name'], PATHINFO_EXTENSION);
    $new_name = uniqid('img_', true) . '.' . $extension;
    
    // Dossier destination
    $upload_dir = __DIR__ . '/uploads/';
    if (!is_dir($upload_dir)) {
        mkdir($upload_dir, 0755, true);
    }
    
    $destination = $upload_dir . $new_name;
    
    // Déplacer fichier
    if (move_uploaded_file($file['tmp_name'], $destination)) {
        echo "Fichier uploadé : $new_name";
        
        // Sauvegarder en base
        $stmt = $pdo->prepare('
            INSERT INTO uploads (filename, original_name, mime_type, size) 
            VALUES (?, ?, ?, ?)
        ');
        $stmt->execute([
            $new_name,
            $file['name'],
            $mime_type,
            $file['size']
        ]);
    } else {
        die("Erreur déplacement fichier");
    }
}
?>

Créer une API REST

PHP
<?php
// api/users.php
header('Content-Type: application/json');
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE');

// Connexion DB
require_once 'db.php';

// Déterminer méthode HTTP
$method = $_SERVER['REQUEST_METHOD'];
$id = $_GET['id'] ?? null;

switch ($method) {
    case 'GET':
        if ($id) {
            // GET un utilisateur
            $stmt = $pdo->prepare('SELECT id, nom, email FROM users WHERE id = ?');
            $stmt->execute([$id]);
            $user = $stmt->fetch();
            
            if ($user) {
                echo json_encode($user);
            } else {
                http_response_code(404);
                echo json_encode(['error' => 'User not found']);
            }
        } else {
            // GET tous les utilisateurs
            $stmt = $pdo->query('SELECT id, nom, email FROM users');
            $users = $stmt->fetchAll();
            echo json_encode($users);
        }
        break;
        
    case 'POST':
        // Créer utilisateur
        $data = json_decode(file_get_contents('php://input'), true);
        
        if (!isset($data['nom'], $data['email'])) {
            http_response_code(400);
            echo json_encode(['error' => 'Missing required fields']);
            break;
        }
        
        $stmt = $pdo->prepare('
            INSERT INTO users (nom, email, password) 
            VALUES (?, ?, ?)
        ');
        
        $stmt->execute([
            $data['nom'],
            $data['email'],
            password_hash($data['password'], PASSWORD_DEFAULT)
        ]);
        
        http_response_code(201);
        echo json_encode([
            'id' => $pdo->lastInsertId(),
            'message' => 'User created'
        ]);
        break;
        
    case 'PUT':
        // Modifier utilisateur
        if (!$id) {
            http_response_code(400);
            echo json_encode(['error' => 'ID required']);
            break;
        }
        
        $data = json_decode(file_get_contents('php://input'), true);
        
        $stmt = $pdo->prepare('
            UPDATE users 
            SET nom = ?, email = ? 
            WHERE id = ?
        ');
        
        $stmt->execute([$data['nom'], $data['email'], $id]);
        
        echo json_encode(['message' => 'User updated']);
        break;
        
    case 'DELETE':
        // Supprimer utilisateur
        if (!$id) {
            http_response_code(400);
            echo json_encode(['error' => 'ID required']);
            break;
        }
        
        $stmt = $pdo->prepare('DELETE FROM users WHERE id = ?');
        $stmt->execute([$id]);
        
        http_response_code(204);
        break;
        
    default:
        http_response_code(405);
        echo json_encode(['error' => 'Method not allowed']);
}
?>

Sécurité PHP

Protection XSS

PHP
<?php
// Échapper pour HTML
$safe = htmlspecialchars($user_input, ENT_QUOTES, 'UTF-8');
echo "<div>$safe</div>";

// Pour attributs HTML
echo "<input value='" . htmlspecialchars($value, ENT_QUOTES) . "'>";
?>

Protection CSRF

PHP
<?php
// Générer token CSRF
function generateCSRFToken() {
    if (!isset($_SESSION['csrf_token'])) {
        $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
    }
    return $_SESSION['csrf_token'];
}

// Dans le formulaire
$token = generateCSRFToken();
echo "<input type='hidden' name='csrf_token' value='$token'>";

// Vérifier token
function verifyCSRFToken($token) {
    return isset($_SESSION['csrf_token']) && 
           hash_equals($_SESSION['csrf_token'], $token);
}

if (!verifyCSRFToken($_POST['csrf_token'])) {
    die("Invalid CSRF token");
}
?>

Hachage mot de passe

PHP
<?php
// Hacher
$hash = password_hash($password, PASSWORD_DEFAULT);

// Vérifier
if (password_verify($password_input, $hash_from_db)) {
    echo "Mot de passe correct";
}

// Re-hacher si nécessaire (algorithme mis à jour)
if (password_needs_rehash($hash, PASSWORD_DEFAULT)) {
    $new_hash = password_hash($password, PASSWORD_DEFAULT);
    // Mettre à jour en base
}
?>

Bonnes Pratiques

Sécurité

Requêtes préparées, échapper HTML, CSRF tokens, passwords hashés

Error handling

Afficher erreurs en dev, les logger en prod (ne jamais exposer)

PSR Standards

Suivre PSR-1, PSR-2, PSR-4 pour code cohérent

Composer

Utiliser Composer pour dépendances, autoloading

MVC

Séparer logique métier, présentation, données

.env

Config sensible dans .env, pas dans le code