Chapitre 17 : Projet final
1. Introduction au projet
Félicitations ! Vous avez parcouru tous les chapitres du module PHP. Il est maintenant temps de mettre en pratique tout ce que vous avez appris avec un projet complet.
Nous allons créer un Carnet de contacts qui permettra de :
- ✅ Ajouter un nouveau contact
- ✅ Afficher la liste des contacts
- ✅ Modifier un contact existant
- ✅ Supprimer un contact
Ce projet mettra en œuvre : les variables, les tableaux, les fonctions, les formulaires, la gestion des fichiers, et la gestion des erreurs.
2. Cahier des charges
Fonctionnalités requises
- Créer (Create) : Formulaire pour ajouter un contact (nom, email, téléphone)
- Lire (Read) : Afficher tous les contacts dans un tableau
- Modifier (Update) : Éditer les informations d'un contact
- Supprimer (Delete) : Retirer un contact de la liste
Contraintes techniques
- Stockage des données dans un fichier JSON
- Validation des données (email valide, champs obligatoires)
- Gestion des erreurs avec try/catch
- Interface simple et claire
3. Structure du projet
Créez un dossier carnet-contacts avec les fichiers suivants :
carnet-contacts/
├── index.php (page principale)
├── contacts.json (stockage des données)
└── style.css (optionnel : styles CSS)
4. Code complet : index.php
Voici le code complet du projet avec commentaires détaillés :
<?php
// ============================================
// CARNET DE CONTACTS - Projet PHP
// ============================================
// Fichier de stockage des contacts
$fichierContacts = 'contacts.json';
// Initialiser le fichier s'il n'existe pas
if (!file_exists($fichierContacts)) {
file_put_contents($fichierContacts, json_encode([]));
}
// ============================================
// FONCTIONS
// ============================================
/**
* Lire tous les contacts depuis le fichier JSON
*/
function lireContacts($fichier) {
try {
$contenu = file_get_contents($fichier);
return json_decode($contenu, true) ?? [];
} catch (Exception $e) {
error_log("Erreur lecture : " . $e->getMessage());
return [];
}
}
/**
* Sauvegarder les contacts dans le fichier JSON
*/
function sauvegarderContacts($fichier, $contacts) {
try {
$json = json_encode($contacts, JSON_PRETTY_PRINT);
file_put_contents($fichier, $json);
return true;
} catch (Exception $e) {
error_log("Erreur sauvegarde : " . $e->getMessage());
return false;
}
}
/**
* Valider les données d'un contact
*/
function validerContact($nom, $email, $telephone) {
$erreurs = [];
if (empty($nom)) {
$erreurs[] = "Le nom est obligatoire";
}
if (empty($email) || !filter_var($email, FILTER_VALIDATE_EMAIL)) {
$erreurs[] = "L'email est invalide";
}
if (empty($telephone)) {
$erreurs[] = "Le téléphone est obligatoire";
}
return $erreurs;
}
// ============================================
// TRAITEMENT DES ACTIONS
// ============================================
$message = '';
$contacts = lireContacts($fichierContacts);
// Ajouter un contact
if (isset($_POST['ajouter'])) {
$nom = htmlspecialchars($_POST['nom'] ?? '');
$email = htmlspecialchars($_POST['email'] ?? '');
$telephone = htmlspecialchars($_POST['telephone'] ?? '');
$erreurs = validerContact($nom, $email, $telephone);
if (empty($erreurs)) {
$nouveauContact = [
'id' => uniqid(),
'nom' => $nom,
'email' => $email,
'telephone' => $telephone
];
$contacts[] = $nouveauContact;
if (sauvegarderContacts($fichierContacts, $contacts)) {
$message = "✅ Contact ajouté avec succès !";
} else {
$message = "❌ Erreur lors de la sauvegarde";
}
} else {
$message = "❌ " . implode(", ", $erreurs);
}
}
// Supprimer un contact
if (isset($_GET['supprimer'])) {
$id = $_GET['supprimer'];
$contacts = array_filter($contacts, function($contact) use ($id) {
return $contact['id'] !== $id;
});
// Réindexer le tableau
$contacts = array_values($contacts);
if (sauvegarderContacts($fichierContacts, $contacts)) {
$message = "✅ Contact supprimé";
}
}
// Modifier un contact
if (isset($_POST['modifier'])) {
$id = $_POST['id'];
$nom = htmlspecialchars($_POST['nom'] ?? '');
$email = htmlspecialchars($_POST['email'] ?? '');
$telephone = htmlspecialchars($_POST['telephone'] ?? '');
$erreurs = validerContact($nom, $email, $telephone);
if (empty($erreurs)) {
foreach ($contacts as &$contact) {
if ($contact['id'] === $id) {
$contact['nom'] = $nom;
$contact['email'] = $email;
$contact['telephone'] = $telephone;
break;
}
}
if (sauvegarderContacts($fichierContacts, $contacts)) {
$message = "✅ Contact modifié avec succès !";
}
} else {
$message = "❌ " . implode(", ", $erreurs);
}
}
// Récupérer le contact à modifier
$contactAModifier = null;
if (isset($_GET['modifier'])) {
$id = $_GET['modifier'];
foreach ($contacts as $contact) {
if ($contact['id'] === $id) {
$contactAModifier = $contact;
break;
}
}
}
?>
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Carnet de Contacts</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: Arial, sans-serif;
background: #f4f4f4;
padding: 20px;
}
.container {
max-width: 800px;
margin: 0 auto;
background: white;
padding: 30px;
border-radius: 10px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
h1 { color: #333; margin-bottom: 20px; }
.message {
padding: 10px;
margin-bottom: 20px;
border-radius: 5px;
background: #d4edda;
color: #155724;
}
form {
margin-bottom: 30px;
padding: 20px;
background: #f9f9f9;
border-radius: 5px;
}
input {
width: 100%;
padding: 10px;
margin: 5px 0 15px;
border: 1px solid #ddd;
border-radius: 5px;
}
button {
padding: 10px 20px;
background: #007bff;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
}
button:hover { background: #0056b3; }
table {
width: 100%;
border-collapse: collapse;
}
th, td {
padding: 12px;
text-align: left;
border-bottom: 1px solid #ddd;
}
th { background: #007bff; color: white; }
.actions a {
margin-right: 10px;
color: #007bff;
text-decoration: none;
}
.actions a:hover { text-decoration: underline; }
.supprimer { color: #dc3545; }
</style>
</head>
<body>
<div class="container">
<h1>📒 Carnet de Contacts</h1>
<?php if ($message): ?>
<div class="message"><?= $message ?></div>
<?php endif; ?>
<!-- Formulaire d'ajout/modification -->
<form method="POST">
<h2><?= $contactAModifier ? 'Modifier' : 'Ajouter' ?> un contact</h2>
<?php if ($contactAModifier): ?>
<input type="hidden" name="id" value="<?= $contactAModifier['id'] ?>">
<?php endif; ?>
<label>Nom :</label>
<input type="text" name="nom"
value="<?= $contactAModifier['nom'] ?? '' ?>" required>
<label>Email :</label>
<input type="email" name="email"
value="<?= $contactAModifier['email'] ?? '' ?>" required>
<label>Téléphone :</label>
<input type="tel" name="telephone"
value="<?= $contactAModifier['telephone'] ?? '' ?>" required>
<button type="submit" name="<?= $contactAModifier ? 'modifier' : 'ajouter' ?>">
<?= $contactAModifier ? '✏️ Modifier' : '➕ Ajouter' ?>
</button>
<?php if ($contactAModifier): ?>
<a href="index.php"><button type="button">Annuler</button></a>
<?php endif; ?>
</form>
<!-- Liste des contacts -->
<h2>Liste des contacts (<?= count($contacts) ?>)</h2>
<?php if (empty($contacts)): ?>
<p>Aucun contact pour le moment.</p>
<?php else: ?>
<table>
<thead>
<tr>
<th>Nom</th>
<th>Email</th>
<th>Téléphone</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<?php foreach ($contacts as $contact): ?>
<tr>
<td><?= htmlspecialchars($contact['nom']) ?></td>
<td><?= htmlspecialchars($contact['email']) ?></td>
<td><?= htmlspecialchars($contact['telephone']) ?></td>
<td class="actions">
<a href="?modifier=<?= $contact['id'] ?>">✏️ Modifier</a>
<a href="?supprimer=<?= $contact['id'] ?>"
class="supprimer"
onclick="return confirm('Supprimer ce contact ?')">
🗑️ Supprimer
</a>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>
</div>
</body>
</html>
5. Explications du code
Fonctions principales
lireContacts(): Lit le fichier JSON et retourne un tableau de contactssauvegarderContacts(): Enregistre les contacts dans le fichier JSONvaliderContact(): Vérifie que les données sont valides
Sécurité
- Utilisation de
htmlspecialchars()pour éviter les injections XSS - Validation de l'email avec
filter_var() - Gestion des erreurs avec try/catch
Fonctionnalités CRUD
- Create : Formulaire POST avec action "ajouter"
- Read : Affichage dans un tableau HTML
- Update : Formulaire pré-rempli avec action "modifier"
- Delete : Lien GET avec confirmation JavaScript
6. Améliorations possibles
Pour aller plus loin, vous pouvez :
- 🔍 Ajouter une fonction de recherche
- 📊 Trier les contacts par nom
- 🎨 Améliorer le design avec Bootstrap ou Tailwind
- 💾 Utiliser une base de données MySQL au lieu de JSON
- 🔐 Ajouter un système d'authentification
- 📱 Rendre l'interface responsive
- 📸 Ajouter des photos de profil
7. Exercice : Testez votre application
- Créez le dossier
carnet-contacts - Copiez le code dans
index.php - Lancez votre serveur local (XAMPP, Laragon, etc.)
- Accédez à
http://localhost/carnet-contacts/ - Testez toutes les fonctionnalités : ajout, modification, suppression
Conclusion
🎉 Félicitations ! Vous avez terminé le module PHP et créé votre première application complète.
Ce projet vous a permis de mettre en pratique :
- ✅ Les variables et les tableaux
- ✅ Les fonctions
- ✅ Les formulaires et la validation
- ✅ La manipulation de fichiers
- ✅ La gestion des erreurs
- ✅ Les opérations CRUD
Vous êtes maintenant prêt à explorer des concepts plus avancés comme les bases de données MySQL, la programmation orientée objet, ou les frameworks PHP comme Laravel !
Quiz du chapitre 17
Ateliers pratiques & Projets
Testez vos compétences avec nos exercices guidés et le tuteur IA.
Ateliers pratiques & Corrigés détaillés
Ateliers pratiques & Corrigés
#1 — Calculatrice simple
Atelier #1Crée un script calculette (CLI) qui demande deux nombres et une opération (+, -, *, /) puis affiche le résultat.
Consulter la solution type du formateur
#2 — Carnet de contacts (Tableau)
Atelier #2Crée un tableau de contacts (nom, tel). Permets à l'utilisateur d'ajouter un contact via readline et affiche la liste.
Consulter la solution type du formateur
#3 — Générateur de mot de passe
Atelier #3Crée une fonction qui génère un mot de passe aléatoire de N caractères (lettres+chiffres).