PHP · formularze

Formularze PHP — zadania z metodami GET i POST

GET i POST, walidacja po stronie serwera i bezpieczne wypisywanie danych. Dwie strony PHP, trzy zadania i tabela testów.

90 minutPoziom podstawowyPliki i rozwiązania

Materiał redakcyjny TablicaPro · liceum i technikum

$imie = trim($_POST["imie"] ?? "");
if (mb_strlen($imie) < 2) {
    $bledy[] = "Za krótkie imię.";
}
Dziękujemy, Ola! Zapisano do koła.
Dane sprawdzone na serwerze.

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 $_GET i $_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 atrybucie value.
CzasPraca na lekcji
0–10 minUruchomienie serwera; formularz wysyła dane, ale kto je odbiera?
10–25 minGET: $_GET, operator ??, htmlspecialchars — wspólny przykład
25–40 minZadanie 1 i test ze znacznikami w imieniu
40–55 minPOST: $_SERVER["REQUEST_METHOD"], walidacja, tablica błędów
55–75 minZadania 2 i 3
75–85 minTesty z tabeli: błędne dane, ręcznie zmieniona wartość klasy
85–90 minPytania 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 o name="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.
  • trim usuwa spacje z początku i końca.
  • htmlspecialchars zamienia 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 formularzemOczekiwany wynik
GET bez parametruPodaj imię w formularzu.
GET imie=OlaCześć, Ola!
GET imie=<b>Ola</b>znaczniki widoczne dosłownie, w kodzie strony jako &lt;b&gt;
POST imie=Ola, klasa=2Dziękujemy, Ola! Zapisano do koła (klasa 2). Pola puste.
POST imie=„ O ” (spacje i jedna litera), klasa=2błąd imienia; klasa 2 nadal zaznaczona
POST imie=Ola, klasa pustabłą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=1zapis poprawny; mb_strlen liczy polskie litery jako znaki

Komentarz do rozwiązania:

  • mb_strlen liczy znaki, a strlen bajty. Imię Ł ma jeden znak, ale w UTF-8 dwa bajty — strlen uznałby je za wystarczająco długie.
  • filter_var zwraca liczbę albo false. Porównujemy przez ===, bo przy innym zakresie wartość 0 byłaby poprawna, a luźne porównanie pomyliłoby ją z false. Wartości 2.5, abc czy 7 dają false.
  • htmlspecialchars stosujemy w momencie wypisywania, także dla komunikatu sukcesu, bo zawiera imię wpisane przez użytkownika. W atrybucie value zamienia cudzysłowy, więc wpis Ola" autofocus=" nie doda do pola nowego atrybutu.
  • $klasa === (string) $i porównuje tekst z formularza z tekstem — w $_POST wszystkie wartości są napisami.

Typowe błędy

ObjawCo sprawdzić
W przeglądarce widać kod PHPPlik 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żeniuPrzeglą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

  1. Czym różni się przesyłanie danych metodą GET od POST? Podaj przykład zastosowania każdej z nich.
  2. Dlaczego walidacja w PHP jest konieczna, skoro HTML ma atrybut required i listę wyboru?
  3. Co robi htmlspecialchars i 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

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 →

Zobacz program kursu