Lekcja 14: Obsługa Formularzy - Metody GET i POST

Witaj w czternastej lekcji kursu PHP! Po zapoznaniu się z operacjami na plikach i katalogach, przechodzimy do jednego z najbardziej fundamentalnych aspektów tworzenia interaktywnych stron internetowych: obsługi formularzy HTML. Formularze są podstawowym sposobem, w jaki użytkownicy przekazują dane do aplikacji webowej – od prostych pól wyszukiwania, przez formularze logowania i rejestracji, po skomplikowane ankiety i systemy rezerwacji. W tej lekcji nauczymy się, jak PHP odbiera i przetwarza dane wysłane za pomocą dwóch głównych metod HTTP: GET i POST.

Formularze HTML - Krótkie Przypomnienie

Zanim zagłębimy się w PHP, przypomnijmy sobie podstawową strukturę formularza HTML. Formularz definiuje się za pomocą tagu <form>. Najważniejsze atrybuty tego tagu to:

Wewnątrz formularza umieszczamy różne elementy wejściowe (input fields), takie jak pola tekstowe, przyciski opcji, pola wyboru, listy rozwijane, przyciski wysyłania itp. Każdy element wejściowy, którego wartość ma być przesłana na serwer, musi mieć atrybut name. Wartość tego atrybutu będzie używana jako klucz do identyfikacji danych w PHP.

<!DOCTYPE html>
<html lang="pl">
<head>
    <meta charset="UTF-8">
    <title>Przykładowy Formularz</title>
</head>
<body>
    <h2>Prosty Formularz Kontaktowy</h2>
    <form action="przetwarzanie_formularza.php" method="POST">
        <p>
            <label for="imie">Imię:</label>
            <input type="text" id="imie" name="imie_uzytkownika" required>
        </p>
        <p>
            <label for="email">Adres e-mail:</label>
            <input type="email" id="email" name="email_uzytkownika" required>
        </p>
        <p>
            <label for="wiadomosc">Wiadomość:</label><br>
            <textarea id="wiadomosc" name="tresc_wiadomosci" rows="5" cols="30" required></textarea>
        </p>
        <p>
            <input type="submit" value="Wyślij Wiadomość">
        </p>
    </form>
</body>
</html>

W powyższym przykładzie, po kliknięciu przycisku "Wyślij Wiadomość", dane z pól "imie_uzytkownika", "email_uzytkownika" i "tresc_wiadomosci" zostaną wysłane metodą POST do skryptu przetwarzanie_formularza.php.

Metoda GET

Metoda GET służy do żądania danych z określonego zasobu. Kiedy formularz jest wysyłany metodą GET, dane formularza są dołączane do adresu URL jako ciąg znaków zapytania (query string). Ciąg ten zaczyna się od znaku zapytania ?, a poszczególne pary nazwa=wartość są oddzielone ampersandami &. Na przykład:

http://example.com/skrypt.php?nazwa=Jan&wiek=30

Charakterystyka metody GET:

Dostęp do Danych GET w PHP

W PHP dane przesłane metodą GET są dostępne za pośrednictwem superglobalnej tablicy asocjacyjnej $_GET. Kluczami w tej tablicy są nazwy pól formularza (atrybuty name), a wartościami są dane wprowadzone przez użytkownika.

Przykład formularza GET:

<!DOCTYPE html>
<html lang="pl">
<head>
    <meta charset="UTF-8">
    <title>Formularz Wyszukiwania (GET)</title>
</head>
<body>
    <h2>Wyszukaj Produkt</h2>
    <form action="wyniki_wyszukiwania.php" method="GET">
        <p>
            <label for="query">Szukana fraza:</label>
            <input type="text" id="query" name="szukaj">
        </p>
        <p>
            <label for="kategoria">Kategoria:</label>
            <select id="kategoria" name="kat">
                <option value="ksiazki">Książki</option>
                <option value="elektronika">Elektronika</option>
                <option value="odziez">Odzież</option>
            </select>
        </p>
        <p>
            <input type="submit" value="Szukaj">
        </p>
    </form>
</body>
</html>

Skrypt PHP (wyniki_wyszukiwania.php) obsługujący dane GET:

<?php
echo "<h1>Wyniki Wyszukiwania</h1>";

// Sprawdzenie, czy dane zostały przesłane
if (isset($_GET["szukaj"]) && isset($_GET["kat"])) {
    // Odczytanie i oczyszczenie danych (podstawowe zabezpieczenie)
    $fraza = htmlspecialchars(trim($_GET["szukaj"]));
    $kategoria = htmlspecialchars(trim($_GET["kat"]));

    if (!empty($fraza)) {
        echo "<p>Szukasz frazy: <strong>$fraza</strong></p>";
    } else {
        echo "<p>Nie wprowadzono frazy do wyszukania.</p>";
    }
    echo "<p>W kategorii: <strong>$kategoria</strong></p>";

    // Tutaj mogłaby być logika wyszukiwania w bazie danych itp.
    echo "<p><em>(Symulacja wyszukiwania...)</em></p>";

} else {
    echo "<p>Nie przekazano wszystkich wymaganych parametrów wyszukiwania.</p>";
}

echo "<h3>Zawartość tablicy ".htmlspecialchars('$_GET').":</h3>";
echo "<pre>";
print_r($_GET);
echo "</pre>";

echo "<p><a href=\"formularz_get.html\">Powrót do formularza</a></p>";
?>

Jeśli użytkownik wpisze "PHP" w polu szukanej frazy i wybierze kategorię "Książki", adres URL po wysłaniu formularza będzie wyglądał mniej więcej tak: wyniki_wyszukiwania.php?szukaj=PHP&kat=ksiazki. Skrypt PHP odczyta te wartości z tablicy $_GET.

Metoda POST

Metoda POST służy do wysyłania danych na serwer w celu ich przetworzenia, np. utworzenia nowego zasobu, aktualizacji istniejącego, czy wykonania operacji, która zmienia stan aplikacji.

Charakterystyka metody POST:

Dostęp do Danych POST w PHP

W PHP dane przesłane metodą POST są dostępne za pośrednictwem superglobalnej tablicy asocjacyjnej $_POST. Kluczami są nazwy pól formularza, a wartościami – wprowadzone dane.

Przykład formularza POST (plik formularz_post.html):

<!DOCTYPE html>
<html lang="pl">
<head>
    <meta charset="UTF-8">
    <title>Formularz Rejestracji (POST)</title>
</head>
<body>
    <h2>Zarejestruj się</h2>
    <form action="rejestracja.php" method="POST">
        <p>
            <label for="login">Login:</label>
            <input type="text" id="login" name="uzytkownik_login" required>
        </p>
        <p>
            <label for="haslo">Hasło:</label>
            <input type="password" id="haslo" name="uzytkownik_haslo" required>
        </p>
        <p>
            <label for="email_post">E-mail:</label>
            <input type="email" id="email_post" name="uzytkownik_email" required>
        </p>
        <p>
            <input type="checkbox" id="zgoda" name="zgoda_regulamin" value="tak" required>
            <label for="zgoda">Akceptuję regulamin</label>
        </p>
        <p>
            <input type="submit" value="Zarejestruj">
        </p>
    </form>
</body>
</html>

Skrypt PHP (rejestracja.php) obsługujący dane POST:

<?php
echo "<h1>Przetwarzanie Rejestracji</h1>";

// Sprawdzenie, czy formularz został wysłany (czy istnieje np. przycisk submit w $_POST lub inne pole)
// Lepszym sposobem jest sprawdzenie metody żądania:
if ($_SERVER["REQUEST_METHOD"] == "POST") {
    echo "<p>Formularz został wysłany metodą POST.</p>";

    // Walidacja i odczyt danych
    $login = isset($_POST["uzytkownik_login"]) ? htmlspecialchars(trim($_POST["uzytkownik_login"])) : null;
    $haslo = isset($_POST["uzytkownik_haslo"]) ? $_POST["uzytkownik_haslo"] : null; // Hasła nie wyświetlamy, ale powinniśmy je hashować!
    $email = isset($_POST["uzytkownik_email"]) ? htmlspecialchars(trim($_POST["uzytkownik_email"])) : null;
    $zgoda = isset($_POST["zgoda_regulamin"]) ? $_POST["zgoda_regulamin"] : null;

    $bledy = [];

    if (empty($login)) {
        $bledy[] = "Login jest wymagany.";
    }
    if (empty($haslo)) {
        $bledy[] = "Hasło jest wymagane.";
    } elseif (strlen($haslo) < 8) {
        $bledy[] = "Hasło musi mieć co najmniej 8 znaków.";
    }
    if (empty($email) || !filter_var($email, FILTER_VALIDATE_EMAIL)) {
        $bledy[] = "Podaj poprawny adres e-mail.";
    }
    if ($zgoda !== "tak") {
        $bledy[] = "Musisz zaakceptować regulamin.";
    }

    if (empty($bledy)) {
        echo "<p style='color: green;'>Rejestracja przebiegła pomyślnie!</p>";
        echo "<p>Login: <strong>$login</strong></p>";
        echo "<p>E-mail: <strong>$email</strong></p>";
        // Tutaj nastąpiłoby zapisanie danych do bazy, hashowanie hasła itp.
        // Pamiętaj o hashowaniu haseł! Np. password_hash($haslo, PASSWORD_DEFAULT);
        echo "<p><em>(Symulacja zapisu do bazy danych...)</em></p>";
    } else {
        echo "<p style='color: red;'>Wystąpiły błędy w formularzu:</p>";
        echo "<ul style='color: red;'>";
        foreach ($bledy as $blad) {
            echo "<li>$blad</li>";
        }
        echo "</ul>";
    }

} else {
    echo "<p>Formularz nie został wysłany metodą POST lub dostęp bezpośredni do skryptu.</p>";
}

echo "<h3>Zawartość tablicy ".htmlspecialchars('$_POST').":</h3>";
echo "<pre>";
print_r($_POST);
echo "</pre>";

echo "<p><a href=\"formularz_post.html\">Powrót do formularza rejestracji</a></p>";
?>

Superglobalna Tablica $_REQUEST

PHP udostępnia również superglobalną tablicę $_REQUEST, która domyślnie zawiera połączoną zawartość tablic $_GET, $_POST i $_COOKIE. Kolejność, w jakiej te tablice są łączone, zależy od dyrektywy request_order (lub starszej variables_order) w php.ini.

Chociaż $_REQUEST może wydawać się wygodna, generalnie odradza się jej używanie. Powody:

Bezpieczeństwo Danych z Formularzy

Obsługa danych z formularzy jest jednym z głównych punktów wejścia dla ataków na aplikacje webowe. Nigdy nie ufaj danym pochodzącym od użytkownika! Zawsze należy je walidować i odpowiednio oczyszczać (sanitize) przed użyciem.

Podstawowe zasady bezpieczeństwa:

  1. Walidacja po stronie serwera: Walidacja po stronie klienta (JavaScript) jest przydatna dla UX, ale może być łatwo ominięta. Zawsze przeprowadzaj pełną walidację danych po stronie serwera (w PHP). Sprawdzaj typy danych, formaty (np. e-mail, data), długość, zakresy wartości, wymagane pola itp.
  2. Oczyszczanie danych wyjściowych (Output Sanitization): Kiedy wyświetlasz dane pochodzące od użytkownika z powrotem na stronie HTML, zawsze używaj funkcji takich jak htmlspecialchars(), aby zapobiec atakom XSS (Cross-Site Scripting). htmlspecialchars() konwertuje specjalne znaki HTML (np. <, >, &, ") na ich encje HTML.
  3. Ochrona przed SQL Injection: Jeśli dane z formularza mają być użyte w zapytaniach do bazy danych, używaj zapytań przygotowanych (prepared statements) z PDO lub MySQLi. Nigdy nie wstawiaj surowych danych użytkownika bezpośrednio do zapytań SQL. Ten temat zostanie omówiony w lekcjach o bazach danych.
  4. Ochrona przed CSRF (Cross-Site Request Forgery): Używaj tokenów CSRF, aby upewnić się, że żądania POST pochodzą z Twojej własnej aplikacji, a nie zostały sfabrykowane przez złośliwą stronę.
  5. Hashowanie haseł: Nigdy nie przechowuj haseł w postaci jawnego tekstu. Używaj silnych algorytmów hashowania, takich jak te dostarczane przez funkcje password_hash() i password_verify().
  6. Używaj HTTPS: Zawsze używaj protokołu HTTPS do szyfrowania komunikacji między przeglądarką a serwerem, zwłaszcza przy przesyłaniu wrażliwych danych.

Przykład podstawowego oczyszczania i walidacji:

<?php
if ($_SERVER["REQUEST_METHOD"] == "POST") {
    $imie = "";
    if (isset($_POST["imie_uzytkownika"])) {
        $imie = trim($_POST["imie_uzytkownika"]); // Usuń białe znaki z początku i końca
        if (empty($imie)) {
            echo "Imię nie może być puste.<br>";
        } elseif (strlen($imie) > 50) {
            echo "Imię jest za długie (maksymalnie 50 znaków).<br>";
        } else {
            // Imię wygląda OK, można je dalej przetwarzać
            // Pamiętaj o htmlspecialchars() przy wyświetlaniu!
            echo "Witaj, " . htmlspecialchars($imie) . "!<br>";
        }
    }

    $email = "";
    if (isset($_POST["email_uzytkownika"])) {
        $email = trim($_POST["email_uzytkownika"]);
        if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
            echo "Podany adres e-mail jest nieprawidłowy.<br>";
        } else {
            echo "Twój e-mail to: " . htmlspecialchars($email) . "<br>";
        }
    }
}
?>

PHP oferuje funkcje filtrowania danych (Filter Functions), takie jak filter_var() i filter_input(), które są bardzo pomocne przy walidacji i sanityzacji danych wejściowych. Zostaną one omówione bardziej szczegółowo w późniejszej lekcji.

Podsumowanie Lekcji

W tej lekcji nauczyliśmy się, jak obsługiwać dane przesyłane z formularzy HTML za pomocą metod GET i POST w PHP. Zrozumieliśmy różnice między tymi metodami, ich typowe zastosowania oraz jak uzyskiwać dostęp do przesłanych danych za pomocą superglobalnych tablic $_GET i $_POST. Podkreśliliśmy również fundamentalne znaczenie walidacji i oczyszczania danych pochodzących od użytkownika w celu zapewnienia bezpieczeństwa aplikacji. Dowiedzieliśmy się także o tablicy $_REQUEST i powodach, dla których generalnie należy jej unikać.

Obsługa formularzy jest kluczowym elementem interakcji z użytkownikiem w aplikacjach webowych. W następnej lekcji zajmiemy się bardziej zaawansowanym aspektem formularzy: przesyłaniem plików na serwer.


Zadanie praktyczne

Stwórz dwa pliki: kalkulator.html i oblicz.php.

  1. kalkulator.html: Stwórz formularz HTML, który pozwala użytkownikowi wprowadzić dwie liczby (liczba1, liczba2) oraz wybrać operację matematyczną (operacja) z listy rozwijanej (dodawanie, odejmowanie, mnożenie, dzielenie). Formularz powinien wysyłać dane metodą POST do skryptu oblicz.php.
  2. oblicz.php: Napisz skrypt PHP, który odbiera dane z formularza. Sprawdź, czy wszystkie wymagane dane zostały przesłane. Przeprowadź walidację, upewniając się, że wprowadzone wartości są numeryczne. Wykonaj wybraną operację matematyczną. Zabezpiecz się przed dzieleniem przez zero. Wyświetl wynik operacji lub odpowiedni komunikat błędu, jeśli walidacja się nie powiodła lub wystąpił błąd (np. dzielenie przez zero). Pamiętaj o użyciu htmlspecialchars() przy wyświetlaniu danych wejściowych.

Pokaż przykładowe rozwiązanie

kalkulator.html

<!DOCTYPE html>
<html lang="pl">
<head>
    <meta charset="UTF-8">
    <title>Prosty Kalkulator</title>
</head>
<body>
    <h2>Kalkulator</h2>
    <form action="oblicz.php" method="POST">
        <p>
            <label for="l1">Liczba 1:</label>
            <input type="text" id="l1" name="liczba1" required>
        </p>
        <p>
            <label for="l2">Liczba 2:</label>
            <input type="text" id="l2" name="liczba2" required>
        </p>
        <p>
            <label for="op">Operacja:</label>
            <select id="op" name="operacja">
                <option value="dodaj">Dodawanie (+)</option>
                <option value="odejmij">Odejmowanie (-)</option>
                <option value="pomnoz">Mnożenie (*)</option>
                <option value="podziel">Dzielenie (/)</option>
            </select>
        </p>
        <p>
            <input type="submit" value="Oblicz">
        </p>
    </form>
</body>
</html>
            

oblicz.php

<?php
echo "<h1>Wynik Obliczeń</h1>";
$wynik = null;
$blad = "";

if ($_SERVER["REQUEST_METHOD"] == "POST") {
    if (isset($_POST["liczba1"], $_POST["liczba2"], $_POST["operacja"])) {
        $liczba1_str = trim($_POST["liczba1"]);
        $liczba2_str = trim($_POST["liczba2"]);
        $operacja = $_POST["operacja"];

        // Walidacja czy są numeryczne
        if (is_numeric($liczba1_str) && is_numeric($liczba2_str)) {
            $liczba1 = floatval($liczba1_str);
            $liczba2 = floatval($liczba2_str);

            switch ($operacja) {
                case "dodaj":
                    $wynik = $liczba1 + $liczba2;
                    break;
                case "odejmij":
                    $wynik = $liczba1 - $liczba2;
                    break;
                case "pomnoz":
                    $wynik = $liczba1 * $liczba2;
                    break;
                case "podziel":
                    if ($liczba2 == 0) {
                        $blad = "Błąd: Nie można dzielić przez zero!";
                    } else {
                        $wynik = $liczba1 / $liczba2;
                    }
                    break;
                default:
                    $blad = "Błąd: Nieznana operacja.";
            }
        } else {
            $blad = "Błąd: Wprowadzone wartości muszą być liczbami.";
        }
    } else {
        $blad = "Błąd: Nie wszystkie pola formularza zostały przesłane.";
    }
} else {
    $blad = "Błąd: Formularz nie został wysłany metodą POST.";
}

if ($blad) {
    echo "<p style='color: red;'>" . htmlspecialchars($blad) . "</p>";
} elseif ($wynik !== null) {
    echo "<p>Liczba 1: " . htmlspecialchars($_POST["liczba1"]) . "</p>";
    echo "<p>Liczba 2: " . htmlspecialchars($_POST["liczba2"]) . "</p>";
    echo "<p>Operacja: " . htmlspecialchars($_POST["operacja"]) . "</p>";
    echo "<p style='font-weight: bold; color: green;'>Wynik: " . htmlspecialchars((string)$wynik) . "</p>";
}

echo "<p><a href=\"kalkulator.html\">Powrót do kalkulatora</a></p>";
?>
            

Zadanie do samodzielnego wykonania

Rozbuduj powyższy kalkulator. Dodaj walidację po stronie serwera, aby upewnić się, że pola liczbowe nie są puste przed próbą konwersji na liczbę. Zmodyfikuj formularz i skrypt PHP tak, aby po wyświetleniu wyniku lub błędu, wartości wprowadzone przez użytkownika w pola liczbowe oraz wybrana operacja były nadal widoczne w formularzu (tzw. "sticky form"). Oznacza to, że jeśli użytkownik popełni błąd, nie musi wypełniać całego formularza od nowa.


FAQ - Obsługa Formularzy GET i POST

Kiedy dokładnie używać GET, a kiedy POST?

Używaj GET dla operacji, które pobierają dane i nie mają efektów ubocznych (np. wyszukiwanie, filtrowanie, nawigacja). Używaj POST dla operacji, które modyfikują dane na serwerze (np. tworzenie, aktualizacja, usuwanie), przesyłają wrażliwe informacje lub duże ilości danych.

Czy mogę używać obu metod (GET i POST) jednocześnie?

Technicznie, żądanie HTTP jest albo GET, albo POST (lub inną metodą). Formularz HTML ma atrybut method ustawiony na jedną z nich. Można jednak mieć parametry w URL (jak w GET) i jednocześnie wysyłać dane w ciele żądania (jak w POST), ale to rzadsze i wymaga specyficznej obsługi.

Jak sprawdzić, czy formularz został wysłany?

Najlepiej sprawdzić metodę żądania HTTP: if ($_SERVER["REQUEST_METHOD"] == "POST") { ... }. Można też sprawdzać istnienie konkretnego pola z formularza, np. przycisku submit: if (isset($_POST["nazwa_przycisku_submit"])) { ... }.

Co to jest htmlspecialchars() i dlaczego jest ważne?

htmlspecialchars() konwertuje specjalne znaki HTML (&, ", ', <, >) na ich odpowiedniki w postaci encji HTML. Jest to kluczowe zabezpieczenie przed atakami XSS (Cross-Site Scripting), gdy wyświetlasz dane pochodzące od użytkownika na stronie.

Jak obsłużyć pola wyboru (checkboxes) i przyciski opcji (radio buttons)?

Jeśli checkbox nie jest zaznaczony, jego nazwa w ogóle nie pojawi się w danych $_POST lub $_GET. Dlatego zawsze sprawdzaj jego istnienie za pomocą isset(). Przyciski opcji o tej samej nazwie (name) pozwalają wybrać tylko jedną opcję; jej wartość (value) zostanie przesłana.

Jak przesłać tablicę danych z formularza?

Można użyć notacji z nawiasami kwadratowymi w atrybucie name elementu formularza, np. <input type="text" name="produkty[]">. W PHP $_POST["produkty"] (lub $_GET["produkty"]) będzie wtedy tablicą.

Co się stanie, jeśli nazwy pól w GET i POST będą takie same?

Jeśli używasz $_REQUEST, to która wartość zostanie użyta, zależy od konfiguracji request_order. Jeśli używasz jawnie $_GET i $_POST, będziesz miał dostęp do obu wartości oddzielnie w odpowiednich tablicach.