Projekt: Księga gości
🎯 Po tej lekcji zbudujesz od zera działającą księgę gości, która waliduje wpisy, przechowuje je w sesji i bezpiecznie wyświetla przez htmlspecialchars.
Czas połączyć wszystko, czego nauczyłeś się w tym module: formularze, odczyt $_POST, walidację, sanityzację, sesje i bezpieczne wyświetlanie danych. Zbudujesz księgę gości — klasyczny projekt webowy, który działa jak miniaturowy serwis społecznościowy.
$_SESSION — żyją tylko przez czas trwania sesji i nie są widoczne dla innych użytkowników. Trwałe przechowywanie z dostępem dla wszystkich (baza danych) pojawi się w Module 8. Na razie skupiamy się na technikach webowych.Co będziemy budować
Aplikacja składa się z dwóch części na tej samej stronie:
- Formularz — imię (wymagane), e-mail (opcjonalny, ale jeśli podany — musi być poprawny), treść wpisu (wymagana, minimum 10 znaków)
- Lista wpisów — wszystkie wpisy z sesji, wyświetlone od najnowszego, bezpiecznie przez
htmlspecialchars
Krok 1 — inicjalizacja i odczyt sesji
Każdy plik PHP z sesjami zaczynamy tak samo — session_start() na samym początku:
<?php
declare(strict_types=1);
session_start();
// Upewnij się, że tablica wpisów istnieje w sesji
if (!isset($_SESSION['wpisy'])) {
$_SESSION['wpisy'] = [];
}
Krok 2 — obsługa wysłanego formularza
Przed wyświetleniem czegokolwiek przetwarzamy dane, jeśli formularz był wysłany:
$imie = '';
$email = '';
$tresc = '';
$bledy = [];
$sukces = false;
if (isset($_POST['wyslij'])) {
// Sanityzacja
$imie = trim($_POST['imie'] ?? '');
$email = trim($_POST['email'] ?? '');
$tresc = trim($_POST['tresc'] ?? '');
// Walidacja
if ($imie === '') {
$bledy[] = 'Imię jest wymagane.';
}
if ($email !== '' && filter_var($email, FILTER_VALIDATE_EMAIL) === false) {
$bledy[] = 'Podany adres e-mail jest nieprawidłowy.';
}
if ($tresc === '') {
$bledy[] = 'Treść wpisu jest wymagana.';
} elseif (strlen($tresc) < 10) {
$bledy[] = 'Wpis musi mieć co najmniej 10 znaków.';
}
if ($bledy === []) {
// Dodaj nowy wpis na POCZĄTKU tablicy (od najnowszego)
array_unshift($_SESSION['wpisy'], [
'imie' => $imie,
'email' => $email,
'tresc' => $tresc,
'data' => date('d.m.Y H:i'),
]);
$sukces = true;
// Wyczyść pola po sukcesie
$imie = $email = $tresc = '';
}
}
Krok 3 — szablon HTML
Teraz wyświetlamy wszystko — błędy, formularz i listę wpisów:
?>
<!DOCTYPE html>
<html lang="pl">
<head>
<meta charset="UTF-8">
<title>Księga gości</title>
<style>
body { font-family: sans-serif; max-width: 700px; margin: 2rem auto; padding: 0 1rem; }
.blad { color: #c00; }
.sukces { color: #080; }
.wpis { border: 1px solid #ddd; border-radius: 6px; padding: 1rem; margin: 1rem 0; }
.wpis-meta { color: #666; font-size: .85em; }
</style>
</head>
<body>
<h1>Księga gości</h1>
<?php if ($sukces): ?>
<p class="sukces">Dziękujemy za wpis!</p>
<?php endif; ?>
<?php if ($bledy !== []): ?>
<ul class="blad">
<?php foreach ($bledy as $blad): ?>
<li><?= htmlspecialchars($blad, ENT_QUOTES, 'UTF-8') ?></li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
<h2>Zostaw wpis</h2>
<form method="post" action="">
<p>
<label>Imię <em>(wymagane)</em>:<br>
<input type="text" name="imie" value="<?= htmlspecialchars($imie, ENT_QUOTES, 'UTF-8') ?>" size="40"></label>
</p>
<p>
<label>E-mail <em>(opcjonalny)</em>:<br>
<input type="email" name="email" value="<?= htmlspecialchars($email, ENT_QUOTES, 'UTF-8') ?>" size="40"></label>
</p>
<p>
<label>Wpis <em>(min. 10 znaków)</em>:<br>
<textarea name="tresc" rows="4" cols="50"><?= htmlspecialchars($tresc, ENT_QUOTES, 'UTF-8') ?></textarea></label>
</p>
<button type="submit" name="wyslij">Dodaj wpis</button>
</form>
<h2>Wpisy (<?= count($_SESSION['wpisy']) ?>)</h2>
<?php if ($_SESSION['wpisy'] === []): ?>
<p>Brak wpisów. Bądź pierwszy!</p>
<?php else: ?>
<?php foreach ($_SESSION['wpisy'] as $wpis): ?>
<div class="wpis">
<p><?= htmlspecialchars($wpis['tresc'], ENT_QUOTES, 'UTF-8') ?></p>
<p class="wpis-meta">
— <?= htmlspecialchars($wpis['imie'], ENT_QUOTES, 'UTF-8') ?>
<?php if ($wpis['email'] !== ''): ?>
(<a href="mailto:<?= htmlspecialchars($wpis['email'], ENT_QUOTES, 'UTF-8') ?>"><?= htmlspecialchars($wpis['email'], ENT_QUOTES, 'UTF-8') ?></a>)
<?php endif; ?>
| <?= htmlspecialchars($wpis['data'], ENT_QUOTES, 'UTF-8') ?>
</p>
</div>
<?php endforeach; ?>
<?php endif; ?>
</body>
</html>
Kompletny plik — wszystko razem
Oto gotowy plik ksiega-gosci.php łączący wszystkie kroki:
<?php
declare(strict_types=1);
session_start();
if (!isset($_SESSION['wpisy'])) {
$_SESSION['wpisy'] = [];
}
$imie = '';
$email = '';
$tresc = '';
$bledy = [];
$sukces = false;
if (isset($_POST['wyslij'])) {
$imie = trim($_POST['imie'] ?? '');
$email = trim($_POST['email'] ?? '');
$tresc = trim($_POST['tresc'] ?? '');
if ($imie === '') {
$bledy[] = 'Imię jest wymagane.';
}
if ($email !== '' && filter_var($email, FILTER_VALIDATE_EMAIL) === false) {
$bledy[] = 'Podany adres e-mail jest nieprawidłowy.';
}
if ($tresc === '') {
$bledy[] = 'Treść wpisu jest wymagana.';
} elseif (strlen($tresc) < 10) {
$bledy[] = 'Wpis musi mieć co najmniej 10 znaków.';
}
if ($bledy === []) {
array_unshift($_SESSION['wpisy'], [
'imie' => $imie,
'email' => $email,
'tresc' => $tresc,
'data' => date('d.m.Y H:i'),
]);
$sukces = true;
$imie = $email = $tresc = '';
}
}
?>
<!DOCTYPE html>
<html lang="pl">
<head>
<meta charset="UTF-8">
<title>Księga gości</title>
<style>
body { font-family: sans-serif; max-width: 700px; margin: 2rem auto; padding: 0 1rem; }
.blad { color: #c00; }
.sukces { color: #080; }
.wpis { border: 1px solid #ddd; border-radius: 6px; padding: 1rem; margin: 1rem 0; }
.wpis-meta { color: #666; font-size: .85em; }
</style>
</head>
<body>
<h1>Księga gości</h1>
<?php if ($sukces): ?>
<p class="sukces">Dziękujemy za wpis!</p>
<?php endif; ?>
<?php if ($bledy !== []): ?>
<ul class="blad">
<?php foreach ($bledy as $blad): ?>
<li><?= htmlspecialchars($blad, ENT_QUOTES, 'UTF-8') ?></li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
<h2>Zostaw wpis</h2>
<form method="post" action="">
<p>
<label>Imię <em>(wymagane)</em>:<br>
<input type="text" name="imie"
value="<?= htmlspecialchars($imie, ENT_QUOTES, 'UTF-8') ?>" size="40"></label>
</p>
<p>
<label>E-mail <em>(opcjonalny)</em>:<br>
<input type="email" name="email"
value="<?= htmlspecialchars($email, ENT_QUOTES, 'UTF-8') ?>" size="40"></label>
</p>
<p>
<label>Wpis <em>(min. 10 znaków)</em>:<br>
<textarea name="tresc" rows="4" cols="50"><?= htmlspecialchars($tresc, ENT_QUOTES, 'UTF-8') ?></textarea></label>
</p>
<button type="submit" name="wyslij">Dodaj wpis</button>
</form>
<h2>Wpisy (<?= count($_SESSION['wpisy']) ?>)</h2>
<?php if ($_SESSION['wpisy'] === []): ?>
<p>Brak wpisów. Bądź pierwszy!</p>
<?php else: ?>
<?php foreach ($_SESSION['wpisy'] as $wpis): ?>
<div class="wpis">
<p><?= htmlspecialchars($wpis['tresc'], ENT_QUOTES, 'UTF-8') ?></p>
<p class="wpis-meta">
— <?= htmlspecialchars($wpis['imie'], ENT_QUOTES, 'UTF-8') ?>
<?php if ($wpis['email'] !== ''): ?>
(<a href="mailto:<?= htmlspecialchars($wpis['email'], ENT_QUOTES, 'UTF-8') ?>"><?= htmlspecialchars($wpis['email'], ENT_QUOTES, 'UTF-8') ?></a>)
<?php endif; ?>
| <?= htmlspecialchars($wpis['data'], ENT_QUOTES, 'UTF-8') ?>
</p>
</div>
<?php endforeach; ?>
<?php endif; ?>
</body>
</html>
Co zastosowaliśmy?
Przejrzyj kod i znajdź te elementy — wszystkie poznałeś w tym module:
session_start()— inicjalizacja sesji na samym początkuisset($_POST['wyslij'])— sprawdzenie czy formularz wysłanytrim(...)— sanityzacja wejścia przed walidacjąfilter_var(..., FILTER_VALIDATE_EMAIL)— walidacja adresu e-mailhtmlspecialchars(..., ENT_QUOTES, 'UTF-8')— przy każdym wyświetleniu danych z sesji- Składnia alternatywna
foreach (...): ... endforeach;w szablonie - Wzorzec „formularz wysyła do samego siebie" (action="")
Rozbuduj sam
Masz działający fundament — oto kilka pomysłów na rozbudowę:
- Limit wpisów — zachowaj tylko ostatnie 20 wpisów:
array_slice($_SESSION['wpisy'], 0, 20) - Walidacja długości — ogranicz treść wpisu do 500 znaków przez
strlen($tresc) > 500 - Usuwanie wpisów — dodaj formularz z przyciskiem usuń i indeksem wpisu
- Podgląd przed wysłaniem — pokaż podgląd wpisu i poproś o potwierdzenie
- Trwałe przechowywanie — w Module 8 nauczysz się zapisywać wpisy do bazy danych MySQL, żeby przetrwały sesję i były widoczne dla wszystkich
Funkcje w tej lekcji
Sprawdź się
Dlaczego w księdze gości używamy htmlspecialchars przy wyświetlaniu wpisów?
Ktoś mógł wpisać <script>alert('hack')</script> jako treść wpisu. Bez htmlspecialchars przeglądarka wykonałaby skrypt. htmlspecialchars zamienia < na < itp. — skrypt wyświetla się jako tekst, nie uruchamia.
Gdzie ta aplikacja przechowuje wpisy do księgi gości?
Wpisy są dodawane do $_SESSION['wpisy']. Sesja jest tymczasowa — dane znikają gdy przeglądarka zostanie zamknięta lub sesja wygaśnie. Trwałe przechowywanie (baza danych) pojawi się w Module 8.
Dlaczego trim() jest ważny przed zapisaniem wpisu do księgi gości?
trim() usuwa białe znaki z początku i końca łańcucha. Dzięki temu pole wypełnione samymi spacjami po trim() stanie się '' i zostanie odrzucone przez walidację wymaganego pola.