Jak skrypt PHP odbiera dane z formularza: metody GET i POST, tablice superglobalne $_GET, $_POST i $_SERVER, sprawdzanie isset, walidacja po stronie serwera oraz kodowanie wyjścia funkcją htmlspecialchars. Lekcja 12.6 kursu INF.03.
Odbiór formularza w PHP to odczyt tablicy superglobalnej: $_GET albo $_POST, zależnie od metody, jaką formularz wysłał dane. Sam formularz jest tylko kodem HTML i niczego nie przetwarza, dopiero skrypt na serwerze bierze wpisane wartości i coś z nimi robi. W części pisemnej wracają pytania „w której tablicy będą dostępne dane z formularza", „co można powiedzieć o metodzie POST", „wskaż prawidłowo zapisane pobranie wartości pola", „na czym polega walidacja" oraz pytania z adresem w pasku przeglądarki, po którym poznaje się metodę GET. W zadaniu praktycznym trzeci skrypt arkusza pobiera dane z pól formularza wysłanego metodą POST do tej samej strony i wstawia je do klauzuli WHERE zapytania.
Metody przesyłania danych: GET i POST
Metoda przesyłania danych to sposób, w jaki przeglądarka pakuje zawartość formularza do żądania HTTP. Wybiera ją atrybut method znacznika form, a PHP dostaje dane w innej tablicy dla każdej z metod. Budowę samego formularza, pola, etykiety i przyciski opisuje lekcja Formularze HTML; tutaj liczy się to, co dzieje się po naciśnięciu przycisku wysyłającego. Formularz z metodą get wysyła dane w adresie zamow.php?imie=Anna&ilosc=2 i PHP czyta je z $_GET, a metoda post wysyła te same dane w treści żądania do $_POST
Metoda GET dopisuje pary nazwa i wartość do adresu, po znaku zapytania, rozdzielone znakiem &. Adres zamow.php?imie=Anna&ilosc=2 niesie całą treść formularza, więc widać ją w pasku przeglądarki, można ją zapisać w zakładce i wysłać komuś odnośnikiem. Skrypt czyta te wartości z tablicy $_GET.
<?php
$imie = $_GET['imie'];
$ilosc = $_GET['ilosc'];
echo "Zamowienie dla: " . $imie . ", sztuk: " . $ilosc;
Po wejściu pod adres zamow.php?imie=Anna&ilosc=2 skrypt wypisuje Zamowienie dla: Anna, sztuk: 2. Ten sam skrypt zadziała bez formularza, bo parametry da się wpisać w pasek adresu ręcznie, i to pierwszy powód, dla którego danym z formularza się nie ufa.
Metoda POST pakuje te same dane w treść żądania. Pasek adresu zostaje czysty, długość danych praktycznie nie ma limitu i tylko tą metodą wysyła się plik. Skrypt czyta wartości z tablicy $_POST, a klucz jest zawsze wartością atrybutu name pola: <input name="imie"> czeka pod $_POST['imie'], nigdy odwrotnie. Ukrycie danych w treści żądania to nie jest szyfrowanie: poufność daje dopiero HTTPS, więc najbezpieczniejsza wysyłka danych poufnych to metoda POST przez HTTPS.
Gdzie jadą dane
w adresie, po znaku zapytania
w treści żądania
Widać je w pasku adresu
tak
nie
Limit długości
tak, adres ma ograniczoną długość
praktycznie brak
Do czego
wyszukiwanie, filtry, odnośniki do wyników
hasła, zamówienia, zapis do bazy
Istnieje jeszcze $_REQUEST, które łączy zawartość $_GET, $_POST i $_COOKIE. Po odczycie nie wiadomo, skąd wartość przyszła, a skrypt przyjmie dane dopisane do adresu nawet wtedy, gdy formularz szedł metodą POST, więc w kodzie pisze się $_GET albo $_POST, zgodnie z metodą formularza.
Samą metodę bieżącego żądania podaje $_SERVER['REQUEST_METHOD'], czyli napis GET albo POST. To najczystszy sposób odróżnienia pierwszego wejścia na stronę od wysłania formularza.
<?php
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
echo "Formularz zostal wyslany.";
} else {
echo "Wypelnij formularz.";
}
Odbiór danych krok po kroku
Odbiór formularza to sprawdzenie, czy dane w ogóle przyszły, odczyt wartości po kluczach i przygotowanie ich do użycia. Arkusz CKE prosi zwykle o formularz wysyłany metodą POST do tej samej strony, czyli action="", więc jeden plik zawiera i formularz, i skrypt, który go obsługuje. Przy pierwszym wejściu tablica $_POST jest pusta, dlatego cały odbiór stoi w warunku.
Warunek buduje się na przycisku wysyłającym: isset($_POST['wyslij']) (CZY USTAWIONE) jest prawdą dopiero wtedy, gdy formularz z polem o nazwie wyslij został wysłany. Sama funkcja isset sprawdza, czy klucz w tablicy istnieje, i nie zgłasza przy tym ostrzeżenia, w odróżnieniu od zwykłego odczytu. Ten sam skutek daje operator ?? (wartość zapasowa), który oddaje wartość spod klucza albo podaną wartość domyślną, gdy klucza nie ma.
Wartość z pola tekstowego jest zawsze napisem, także wtedy, gdy pole miało type="number". Przed użyciem obcina się z niej białe znaki funkcją trim (PRZYTNIJ), a liczbę uzyskuje się rzutowaniem (int) albo (float).
<?php
if (isset($_POST['wyslij'])) {
$imie = trim($_POST['imie']);
$produkt = $_POST['produkt'];
$ilosc = (int) $_POST['ilosc'];
$rabat = isset($_POST['rabat']) ? 'tak' : 'nie';
$uwagi = $_POST['uwagi'] ?? '';
echo "Zamowienie dla: " . $imie . "<br>";
echo "Produkt: " . $produkt . ", sztuk: " . $ilosc . "<br>";
echo "Rabat: " . $rabat;
}
Dla imienia Anna, produktu mysz, ilości 2 i zaznaczonego pola rabatu skrypt wypisuje:
Zamowienie dla: Anna
Produkt: mysz, sztuk: 2
Rabat: tak
Poszczególne rodzaje kontrolek zachowują się przy odbiorze różnie. Lista wyboru i przyciski opcji przysyłają wartość atrybutu value zaznaczonej pozycji, a nie jej widoczny napis, więc $_POST['produkt'] to mysz, a nie Mysz bezprzewodowa. Grupa przycisków opcji ma wspólny name, więc daje jeden klucz z jedną wartością. Pole wyboru zachowuje się inaczej niż wszystkie pozostałe: niezaznaczone nie wysyła NICZEGO, więc klucza rabat nie ma w tablicy i tylko isset odróżnia zaznaczone od pustego. Kilka pól o nazwie zakończonej nawiasami kwadratowymi, na przykład name="kolory[]", przysyła tablicę: $_POST['kolory'] jest wtedy tablicą indeksowaną zaznaczonych wartości, po której przechodzi się pętlą foreach (DLA KAŻDEGO) albo składa w napis funkcją implode.
Walidacja po stronie serwera
Walidacja to sprawdzenie, czy wprowadzone dane spełniają określone reguły. Przeglądarka robi część tej pracy sama, mechanizmami walidacji HTML5: required blokuje puste pole, type="email" wymaga adresu ze znakiem @, min i max pilnują zakresu liczby, a pattern porównuje wartość z wyrażeniem regularnym. To wygoda dla użytkownika, nie zabezpieczenie: atrybuty można usunąć w narzędziach przeglądarki, formularz da się wysłać z innej strony, a przy metodzie GET wystarczy wpisać parametry w pasek adresu. Reguły muszą więc powtórzyć się w skrypcie na serwerze, i ten zestaw jest wiążący.
Sprawdzone wyniki zbiera się w tablicy błędów zamiast przerywać skrypt przy pierwszym potknięciu: użytkownik dostaje wtedy komplet uwag naraz.
<?php
$bledy = [];
$imie = trim($_POST['imie'] ?? '');
$ilosc = (int) ($_POST['ilosc'] ?? 0);
$mail = trim($_POST['mail'] ?? '');
if ($imie === '' || strlen($imie) > 40) {
$bledy[] = 'Imie: od 1 do 40 znakow.';
}
if ($ilosc < 1 || $ilosc > 10) {
$bledy[] = 'Ilosc: liczba calkowita od 1 do 10.';
}
if (!filter_var($mail, FILTER_VALIDATE_EMAIL)) {
$bledy[] = 'Adres poczty jest niepoprawny.';
}
Każdy warunek pilnuje innej reguły. empty (PUSTE) mówi, czy wartość jest pusta, ale uznaje za puste także zero i napis "0", więc do ilości nadaje się porównanie zakresu, a nie empty. is_numeric (CZY LICZBA) sprawdza, czy napis w ogóle jest liczbą, zanim porówna się go z zakresem. strlen (DŁUGOŚĆ NAPISU) liczy znaki, czyli pilnuje krótkiego imienia i zbyt długiego tekstu. filter_var (FILTRUJ ZMIENNĄ) z regułą FILTER_VALIDATE_EMAIL oddaje poprawny adres albo false, więc wynik testuje się przeczeniem. Format o stałym kształcie sprawdza wyrażenie regularne funkcją preg_match (DOPASUJ WZORZEC).
<?php
$kod = trim($_POST['kod'] ?? '');
if (!preg_match('/^[0-9]{2}-[0-9]{3}$/', $kod)) {
$bledy[] = 'Kod pocztowy w formacie 00-000.';
}
Wzorzec czyta się kawałek po kawałku: ^ to początek wartości, [0-9] jedna cyfra, {2} dokładnie dwa takie znaki, potem dywiz, trzy cyfry i $, czyli koniec wartości. Bez ^ i $ wzorzec pasowałby do fragmentu, więc abc 12-345 xyz przeszłoby sprawdzenie.
Jeżeli tablica błędów została pusta, dane idą dalej: do bazy, do pliku albo na ekran. Jeżeli błędy są, wypisuje się je nad formularzem, a pola wypełnia z powrotem tym, co użytkownik już wpisał, żeby nie zaczynał od nowa. Wstawiana wartość musi przejść przez htmlspecialchars.
<?php
echo '<input type="text" name="imie" value="' . htmlspecialchars($imie) . '">';
foreach ($bledy as $blad) {
echo '<p>' . $blad . '</p>';
}
htmlspecialchars i kodowanie wyjścia
htmlspecialchars (ZNAKI SPECJALNE HTML) zamienia znaki o znaczeniu w HTML na encje: < na <, > na >, & na &, a cudzysłów na ". Przeglądarka pokazuje wtedy te znaki jako zwykły tekst, zamiast czytać je jako kod. Bez tego wartość wpisana w pole i wypisana z powrotem na stronie może wnieść na nią cudzy skrypt, co nazywa się atakiem XSS (cross-site scripting, wstrzyknięcie skryptu na stronę): napastnik wysyła formularzem znacznik script, a przeglądarka każdego odwiedzającego ten skrypt wykonuje. Zasada dzieli się na dwa kroki: wejście waliduj, sprawdzając format i zakres przy odbiorze, a wyjście koduj, przepuszczając przez htmlspecialchars wszystko, co wraca na stronę. Trzecim, osobnym krokiem jest zabezpieczenie zapytania SQL, opisane w lekcji o bazie danych. Tablice superglobalne
Tablica superglobalna to wbudowana tablica asocjacyjna, dostępna w każdym miejscu skryptu, także wewnątrz funkcji, bez deklaracji global. Nazwa każdej zaczyna się od $_, a klucze są napisami. To o nie pyta bank pytań w kształcie „w której tablicy będą dostępne dane".
$_GET
dane z formularza wysłanego metodą GET oraz parametry z adresu
$_POST
dane z formularza wysłanego metodą POST, w treści żądania
$_REQUEST
zawartość $_GET, $_POST i $_COOKIE razem, bez informacji o źródle
$_SERVER
dane o serwerze i żądaniu: adres IP, protokół, metoda, nazwa skryptu
$_FILES
pliki wysłane formularzem: nazwa, typ, rozmiar, ścieżka tymczasowa
$_COOKIE
ciasteczka przysłane przez przeglądarkę
$_SESSION
dane bieżącej sesji, przechowywane po stronie serwera
$GLOBALS
wszystkie zmienne globalne skryptu, po nazwach
Wysyłka pliku wymaga trzech rzeczy naraz: metody POST, atrybutu enctype="multipart/form-data" w znaczniku form i pola type="file". Odebrany plik leży w katalogu tymczasowym, a $_FILES opisuje go kluczami name (nazwa z dysku użytkownika), tmp_name (ścieżka tymczasowa), size (rozmiar w bajtach), type i error. Plik trzeba przenieść na miejsce docelowe funkcją move_uploaded_file (PRZENIEŚ WYSŁANY PLIK), bo po zakończeniu skryptu wersja tymczasowa znika. Ciasteczka i sesje omawia lekcja o plikach, ciasteczkach i sesjach. Na egzaminie
Pytania pisemne o obsługę formularzy mają kilka stałych kształtów. Pierwszy to adres w pasku przeglądarki: skoro po nazwie pliku widać ?imie=Anna, metodą była GET, a dane czekają w $_GET. Drugi to pole <input name="im"> wypełnione wartością Janek: w tablicy $_POST znajdzie się wartość Janek pod indeksem im, a nie nazwa pola pod indeksem Janek. Trzeci to poprawny zapis odczytu, w którym liczy się każdy znak: $name = $_POST['imie']; ma znak dolara przed nazwą zmiennej, podkreślenie w nazwie tablicy, klucz w apostrofach i klucz pisany dokładnie tak jak name pola, z małej litery i bez polskich znaków. Czwarty to definicja: walidacja pól formularza polega na sprawdzeniu, czy wprowadzone dane spełniają określone reguły, a nie na sprawdzeniu, kto je wprowadził ani czy plik odbierający istnieje. Piąty rozdziela dwa języki: sprawdzanie w trakcie wypełniania, znak po znaku, bez przeładowania strony należy do JavaScriptu, a sprawdzanie po wysłaniu, po stronie serwera, do PHP. Szósty pyta o dane poufne: odpowiedź to metoda POST przez protokół HTTPS. Siódmy to zawartość pozostałych tablic: $_SERVER niesie adres IP i nazwę protokołu, $_SESSION dane logowania, $_COOKIE ciasteczka. Ósmy pokazuje kod z ostrzeżeniem Warning: Undefined array key i pyta o przyczynę: skrypt czyta klucz z $_POST, zanim formularz cokolwiek wysłał.
Pułapka: niezaznaczone pole wyboru nie wysyła żadnej wartości, więc jego klucza w ogóle nie ma w $_POST. Odczyt $_POST['rabat'] daje wtedy ostrzeżenie, a stan pola sprawdza się przez isset.
W zadaniu praktycznym schemat jest zawsze ten sam. Formularz idzie metodą POST do tej samej strony, więc method="post" i action="", a cały odbiór stoi w warunku sprawdzającym przycisk wysyłający. Nazwy kluczy w $_POST muszą zgadzać się co do znaku z atrybutami name z formularza opisanego w treści zadania. Odebrana wartość trafia potem do klauzuli WHERE zapytania, a przed wstawieniem do zapytania liczbę rzutuje się na (int), natomiast wartość tekstową bezpiecznie przekazuje się wyłącznie zapytaniem przygotowanym, opisanym w lekcji o PHP i bazie danych. Wyświetlane wyniki i wartości wracające do pól przepuszcza się przez htmlspecialchars. Strona musi być otwarta przez adres http://localhost/..., bo plik wczytany z dysku pokaże kod PHP zamiast go wykonać. Ściąga
$_GET['imie']
wartość pola z formularza wysłanego metodą GET
$_POST['imie']
wartość pola z formularza wysłanego metodą POST
$_SERVER['REQUEST_METHOD']
metoda bieżącego żądania: GET albo POST
isset($_POST['wyslij'])
czy formularz został wysłany, czyli czy klucz istnieje
$_POST['uwagi'] ?? ''
wartość spod klucza albo wartość domyślna, gdy klucza brak
trim($_POST['imie'])
wartość bez białych znaków z początku i końca
(int) $_POST['ilosc']
wartość pola jako liczba całkowita
empty($x)
czy wartość jest pusta, przy czym zero też uznaje za puste
is_numeric($x)
czy napis jest liczbą
strlen($x)
liczba znaków napisu
filter_var($m, FILTER_VALIDATE_EMAIL)
poprawny adres poczty albo false
preg_match('/^[0-9]{2}-[0-9]{3}$/', $k)
czy wartość pasuje do wzorca, tu kod pocztowy
htmlspecialchars($x)
zamiana znaków HTML na encje, ochrona przed XSS
$_FILES['zdjecie']['tmp_name']
ścieżka tymczasowa wysłanego pliku
Pobierz ściągę PDF