Na tej lekcji formularz zapisu na koło zacznie działać naprawdę: program w PHP odbierze dane, sprawdzi je i odpowie użytkownikowi. Uczniowie napiszą dwie strony — powitanie odbierające imię metodą GET oraz zapis na koło metodą POST z walidacją, listą błędów i zachowaniem wpisanych wartości.
Materiał jest przeznaczony na 90 minut (dwie lekcje) dla uczniów, którzy uruchamiali już lokalny serwer PHP i znają zmienne, echo oraz instrukcję if — na przykład po lekcji PHP od podstaw. Budowę samego formularza w HTML omawia lekcja o formularzach HTML. Tablicę błędów i pętlę foreach pokazujemy w gotowym fragmencie; ich szczegółowe omówienie to osobny temat.
Przygotowanie środowiska
Potrzebne są PHP 8.x, edytor i przeglądarka. Rozpakuj pakiet z sekcji materiałów. Otwórz terminal w folderze start i uruchom wbudowany serwer:
php -S 127.0.0.1:8000
Strony otworzysz pod adresami http://127.0.0.1:8000/powitanie.php i http://127.0.0.1:8000/zapis.php. Pliku .php nie otwiera się dwuklikiem — przeglądarka pokazałaby wtedy kod, a nie wynik. Serwer zatrzymasz przez Ctrl+C. Służy on wyłącznie do ćwiczeń na własnym komputerze.
Instalację i sprawdzenie php -v wykonaj przed zajęciami; plan 90 minut ich nie obejmuje. Na lekcji wpisujemy tylko zmyślone dane.
Cele i plan lekcji
Po zajęciach uczeń potrafi:
- odczytać dane z
$_GETi$_POST, bezpiecznie obsługując brak pola operatorem??; - wyjaśnić, kiedy użyć metody GET, a kiedy POST;
- sprawdzić dane po stronie serwera i wyświetlić czytelne komunikaty o błędach;
- wypisać dane użytkownika przez
htmlspecialchars, także w atrybucievalue.
| Czas | Praca na lekcji |
|---|---|
| 0–10 min | Uruchomienie serwera; formularz wysyła dane, ale kto je odbiera? |
| 10–25 min | GET: $_GET, operator ??, htmlspecialchars — wspólny przykład |
| 25–40 min | Zadanie 1 i test ze znacznikami w imieniu |
| 40–55 min | POST: $_SERVER["REQUEST_METHOD"], walidacja, tablica błędów |
| 55–75 min | Zadania 2 i 3 |
| 75–85 min | Testy z tabeli: błędne dane, ręcznie zmieniona wartość klasy |
| 85–90 min | Pytania kontrolne |
GET: dane w adresie strony
Formularz z metodą get dopisuje dane do adresu, np. powitanie.php?imie=Ola. PHP udostępnia je w tablicy $_GET. Wspólny przykład:
<?php
$imie = trim($_GET["imie"] ?? "");
echo "Cześć, " . htmlspecialchars($imie) . "!";
$_GET["imie"]odczytuje wartość pola oname="imie".- Operator
??podaje wartość zastępczą, gdy pola nie ma — np. przy pierwszym wejściu na stronę bez parametrów. Bez niego PHP zgłosi ostrzeżenie o niezdefiniowanym kluczu. trimusuwa spacje z początku i końca.htmlspecialcharszamienia znaki<,>,&i cudzysłowy na encje HTML. Dzięki temu wpisane<b>Ola</b>pojawi się na stronie dosłownie, a nie jako pogrubiony tekst. Bez tej funkcji użytkownik mógłby wstrzyknąć do strony dowolny kod HTML lub JavaScript (atak XSS).
GET nadaje się do danych, które mogą być widoczne w adresie i zapisane w historii: wyszukiwania, filtrów, numeru strony. Adres z wynikiem można skopiować i wysłać innej osobie.
POST: dane wysyłane w treści żądania
Formularz zapisu zmienia stan aplikacji (w prawdziwym projekcie zapisałby uczestnika w bazie), dlatego używa metody post. Dane nie trafiają do adresu, a PHP odczytuje je z $_POST. Ta sama strona najpierw wyświetla pusty formularz, a po wysłaniu go — przetwarza dane. Rozpoznajemy to po metodzie żądania:
if ($_SERVER["REQUEST_METHOD"] === "POST") {
$imie = trim($_POST["imie"] ?? "");
// tu sprawdzamy dane
}
POST nie szyfruje danych — robi to dopiero HTTPS. POST oznacza tylko tyle, że dane nie są częścią adresu. Po wysłaniu formularza i odświeżeniu strony przeglądarka zapyta, czy wysłać dane ponownie.
Każdą wartość z formularza trzeba sprawdzić na serwerze. Atrybuty required czy lista select w HTML to wygoda dla użytkownika, ale żądanie można wysłać z pominięciem przeglądarki albo zmienić wartość opcji w narzędziach deweloperskich. W tym ćwiczeniu formularz celowo nie ma required, żeby było widać walidację po stronie PHP.
Trzy zadania
Zadanie 1. Powitanie przez GET. W powitanie.php formularz z metodą get wysyła pole imie. Jeśli imię (po trim) jest puste lub go brak, wyświetl Podaj imię w formularzu.; w przeciwnym razie Cześć, Ola!. Sprawdź adres po wysłaniu oraz wpis <b>Ola</b>.
Zadanie 2. Zapis przez POST z walidacją. W zapis.php formularz z metodą post ma pola imie i klasa (lista 1–4 i pusta opcja „wybierz”). Po wysłaniu sprawdź: imię ma co najmniej 2 znaki (mb_strlen), klasa jest liczbą całkowitą od 1 do 4 (filter_var z FILTER_VALIDATE_INT). Błędy zbierz w tablicy i wyświetl jako listę. Gdy błędów nie ma, pokaż Dziękujemy, Ola! Zapisano do koła (klasa 2).
Zadanie 3. Zachowanie wpisanych danych. Gdy walidacja się nie powiedzie, formularz ma pokazać wcześniej wpisane imię (w atrybucie value, przez htmlspecialchars) i zaznaczoną wybraną klasę. Po udanym zapisie pola mają być puste.
Dla chętnych: po udanym zapisie przekieruj przeglądarkę funkcją header("Location: zapis.php?ok=1") i exit, a komunikat sukcesu pokaż na podstawie $_GET["ok"]. Sprawdź, że odświeżenie strony nie pyta już o ponowne wysłanie danych (wzorzec Post/Redirect/Get).
Rozwiązanie
powitanie.php:
<?php
$imie = trim($_GET["imie"] ?? "");
?>
<!doctype html>
<html lang="pl">
<head>
<meta charset="utf-8">
<title>Powitanie</title>
</head>
<body>
<h1>Powitanie</h1>
<form method="get">
<label for="imie">Imię</label>
<input id="imie" name="imie">
<button type="submit">Przywitaj</button>
</form>
<?php if ($imie === ""): ?>
<p>Podaj imię w formularzu.</p>
<?php else: ?>
<p>Cześć, <?= htmlspecialchars($imie) ?>!</p>
<?php endif; ?>
</body>
</html>
zapis.php:
<?php
$imie = "";
$klasa = "";
$bledy = [];
$sukces = "";
if ($_SERVER["REQUEST_METHOD"] === "POST") {
$imie = trim($_POST["imie"] ?? "");
$klasa = $_POST["klasa"] ?? "";
if (mb_strlen($imie) < 2) {
$bledy[] = "Imię musi mieć co najmniej 2 znaki.";
}
$numer = filter_var($klasa, FILTER_VALIDATE_INT, ["options" => ["min_range" => 1, "max_range" => 4]]);
if ($numer === false) {
$bledy[] = "Wybierz klasę od 1 do 4.";
}
if (count($bledy) === 0) {
$sukces = "Dziękujemy, " . $imie . "! Zapisano do koła (klasa " . $numer . ").";
$imie = "";
$klasa = "";
}
}
?>
<!doctype html>
<html lang="pl">
<head>
<meta charset="utf-8">
<title>Zapis na koło</title>
</head>
<body>
<h1>Zapis na koło</h1>
<?php if ($sukces !== ""): ?>
<p><?= htmlspecialchars($sukces) ?></p>
<?php endif; ?>
<?php if (count($bledy) > 0): ?>
<ul>
<?php foreach ($bledy as $blad): ?>
<li><?= htmlspecialchars($blad) ?></li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
<form method="post">
<p>
<label for="imie">Imię</label>
<input id="imie" name="imie" value="<?= htmlspecialchars($imie) ?>">
</p>
<p>
<label for="klasa">Klasa</label>
<select id="klasa" name="klasa">
<option value="">wybierz</option>
<?php for ($i = 1; $i <= 4; $i++): ?>
<option value="<?= $i ?>"<?= $klasa === (string) $i ? " selected" : "" ?>><?= $i ?></option>
<?php endfor; ?>
</select>
</p>
<p><button type="submit">Zapisz</button></p>
</form>
</body>
</html>
| Dane wysłane formularzem | Oczekiwany wynik |
|---|---|
| GET bez parametru | Podaj imię w formularzu. |
| GET imie=Ola | Cześć, Ola! |
GET imie=<b>Ola</b> | znaczniki widoczne dosłownie, w kodzie strony jako <b> |
| POST imie=Ola, klasa=2 | Dziękujemy, Ola! Zapisano do koła (klasa 2). Pola puste. |
| POST imie=„ O ” (spacje i jedna litera), klasa=2 | błąd imienia; klasa 2 nadal zaznaczona |
| POST imie=Ola, klasa pusta | błąd klasy; w polu nadal Ola |
| POST imie=Ola, klasa=7 (zmieniona w F12) | błąd klasy — walidacja serwera działa mimo listy w HTML |
| POST imie=Łucja, klasa=1 | zapis poprawny; mb_strlen liczy polskie litery jako znaki |
Komentarz do rozwiązania:
mb_strlenliczy znaki, astrlenbajty. ImięŁma jeden znak, ale w UTF-8 dwa bajty —strlenuznałby je za wystarczająco długie.filter_varzwraca liczbę albofalse. Porównujemy przez===, bo przy innym zakresie wartość0byłaby poprawna, a luźne porównanie pomyliłoby ją zfalse. Wartości2.5,abcczy7dająfalse.htmlspecialcharsstosujemy w momencie wypisywania, także dla komunikatu sukcesu, bo zawiera imię wpisane przez użytkownika. W atrybucievaluezamienia cudzysłowy, więc wpisOla" autofocus="nie doda do pola nowego atrybutu.$klasa === (string) $iporównuje tekst z formularza z tekstem — w$_POSTwszystkie wartości są napisami.
Typowe błędy
| Objaw | Co sprawdzić |
|---|---|
| W przeglądarce widać kod PHP | Plik otwarto dwuklikiem albo serwer nie działa; wejdź przez http://127.0.0.1:8000/. |
| Warning: Undefined array key "imie" | Odczyt bez ?? przy pierwszym wejściu na stronę. |
| Dane z formularza nie docierają | Pole nie ma name albo method formularza nie zgadza się z tablicą $_GET lub $_POST. |
| Formularz POST pokazuje stare błędy po odświeżeniu | Przeglądarka ponownie wysyła dane; to normalne zachowanie POST — patrz zadanie dla chętnych. |
| Imię „Ł” przechodzi walidację | Użyto strlen zamiast mb_strlen. |
| Call to undefined function mb_strlen() | Wyłączone rozszerzenie mbstring — nauczyciel włącza je w php.ini. |
| Wpisany tekst rozbija pole lub stronę | Brak htmlspecialchars przy wypisywaniu. |
Pytania kontrolne i punktacja
- Czym różni się przesyłanie danych metodą GET od POST? Podaj przykład zastosowania każdej z nich.
- Dlaczego walidacja w PHP jest konieczna, skoro HTML ma atrybut
requiredi listę wyboru? - Co robi
htmlspecialcharsi dlaczego używamy jej przy wypisywaniu danych użytkownika?
Odpowiedzi: GET dopisuje dane do adresu (np. wyszukiwanie, filtr), POST wysyła je w treści żądania (np. zapis, logowanie) — żadna z metod sama nie szyfruje danych. Ograniczenia HTML działają tylko w przeglądarce i można je pominąć, więc serwer musi sam sprawdzić każdą wartość. htmlspecialchars zamienia znaki specjalne HTML na encje, dzięki czemu tekst użytkownika jest wyświetlany jako tekst, a nie interpretowany jako kod (ochrona przed XSS).
Proponowana punktacja (razem 10 punktów): zadanie 1 — odczyt z ?? i trim 1 pkt, oba komunikaty 1 pkt, htmlspecialchars 1 pkt; zadanie 2 — rozpoznanie POST 1 pkt, walidacja imienia 1 pkt, walidacja klasy 1 pkt, lista błędów i komunikat sukcesu 1 pkt; zadanie 3 — zachowanie imienia i klasy 2 pkt; test z klasą zmienioną w F12 — 1 pkt. Zadanie dla chętnych nie jest wymagane do kompletu punktów.
Materiały do pobrania
- Pakiet PHP: pliki startowe, rozwiązanie, instrukcja i karta pracy
- Karta pracy ucznia — otwórz i wydrukuj
W ZIP-ie pliki mają rozszerzenie .php. Na stronie bloga kopie poza archiwum mają końcówkę .php.txt i są tylko do podglądu tekstu; po pobraniu pojedynczego pliku usuń .txt. Materiały są dostępne bez logowania. Klucz jest publiczny i służy nauce oraz samodzielnemu sprawdzeniu pracy, nie poufnemu sprawdzianowi.
Co dalej po formularzach PHP?
Kolejny krok to zapisanie poprawnych danych w bazie MySQL przez PDO i zapytania przygotowane — wtedy formularz stanie się prawdziwą aplikacją. Do przygotowania tabel przyda się lekcja SQL JOIN. Pełną ścieżkę od pierwszego skryptu po aplikację z bazą danych prowadzi kurs PHP w TablicaPro, z prezentacjami, ćwiczeniami i testami.
Źródła techniczne: obsługa formularzy w dokumentacji PHP, funkcja htmlspecialchars, funkcja filter_var oraz metody HTTP w MDN.
Poprowadź kolejne lekcje PHP
Pełny kurs: prezentacje, ćwiczenia i testy do pracy z klasą.
Kup dostęp do kursu PHP →