PHP i formularze: GET, POST, tablice superglobalne, walidacja i htmlspecialchars

INF.03 · teoria
autor: redakcja · 08.09.2026
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 for­mu­la­rza w PHP to odczyt ta­bli­cy su­per­glo­bal­nej: $_GET albo $_POST, za­leż­nie od metody, jaką for­mu­larz wysłał dane. Sam for­mu­larz jest tylko kodem HTML i ni­cze­go nie prze­twa­rza, do­pie­ro skrypt na ser­we­rze bierze wpi­sa­ne war­to­ści i coś z nimi robi. W części pi­sem­nej wra­ca­ją py­ta­nia „w której ta­bli­cy będą do­stęp­ne dane z for­mu­la­rza", „co można po­wie­dzieć o me­to­dzie POST", „wskaż pra­wi­dło­wo za­pi­sa­ne po­bra­nie war­to­ści pola", „na czym polega wa­li­da­cja" oraz py­ta­nia z ad­re­sem w pasku prze­glą­dar­ki, po którym po­zna­je się metodę GET. W za­da­niu prak­tycz­nym trzeci skrypt ar­ku­sza po­bie­ra dane z pól for­mu­la­rza wy­sła­ne­go metodą POST do tej samej strony i wsta­wia je do klau­zu­li WHERE za­py­ta­nia.

Metody prze­sy­ła­nia danych: GET i POST

Metoda prze­sy­ła­nia danych to sposób, w jaki prze­glą­dar­ka pakuje za­war­tość for­mu­la­rza do żą­da­nia HTTP. Wy­bie­ra ją atry­but method znacz­ni­ka form, a PHP do­sta­je dane w innej ta­bli­cy dla każdej z metod. Budowę samego for­mu­la­rza, pola, ety­kie­ty i przy­ci­ski opi­su­je lekcja For­mu­la­rze HTML; tutaj liczy się to, co dzieje się po na­ci­śnię­ciu przy­ci­sku wy­sy­ła­ją­ce­go.
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 do­pi­su­je pary nazwa i war­tość do adresu, po znaku za­py­ta­nia, roz­dzie­lo­ne zna­kiem &. Adres zamow.php?imie=Anna&ilosc=2 niesie całą treść for­mu­la­rza, więc widać ją w pasku prze­glą­dar­ki, można ją za­pi­sać w za­kład­ce i wysłać komuś od­no­śni­kiem. Skrypt czyta te war­to­ści z ta­bli­cy $_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 wy­pi­su­je Zamowienie dla: Anna, sztuk: 2. Ten sam skrypt za­dzia­ła bez for­mu­la­rza, bo pa­ra­me­try da się wpisać w pasek adresu ręcz­nie, i to pierw­szy powód, dla któ­re­go danym z for­mu­la­rza się nie ufa.
Metoda POST pakuje te same dane w treść żą­da­nia. Pasek adresu zo­sta­je czysty, dłu­gość danych prak­tycz­nie nie ma limitu i tylko tą metodą wysyła się plik. Skrypt czyta war­to­ści z ta­bli­cy $_POST, a klucz jest zawsze war­to­ścią atry­bu­tu name pola: <input name="imie"> czeka pod $_POST['imie'], nigdy od­wrot­nie. Ukry­cie danych w treści żą­da­nia to nie jest szy­fro­wa­nie: po­uf­ność daje do­pie­ro HTTPS, więc naj­bez­piecz­niej­sza wy­sył­ka danych po­uf­nych to metoda POST przez HTTPS.
Cecha
GET
POST
Gdzie jadą dane
w ad­re­sie, po znaku za­py­ta­nia
w treści żą­da­nia
Widać je w pasku adresu
tak
nie
Limit dłu­go­ści
tak, adres ma ogra­ni­czo­ną dłu­gość
prak­tycz­nie brak
Wy­sył­ka pliku
nie
tak
Do czego
wy­szu­ki­wa­nie, filtry, od­no­śni­ki do wy­ni­ków
hasła, za­mó­wie­nia, zapis do bazy
Odbiór w PHP
$_GET
$_POST
Ist­nie­je jesz­cze $_REQUEST, które łączy za­war­tość $_GET, $_POST i $_COOKIE. Po od­czy­cie nie wia­do­mo, skąd war­tość przy­szła, a skrypt przyj­mie dane do­pi­sa­ne do adresu nawet wtedy, gdy for­mu­larz szedł metodą POST, więc w kodzie pisze się $_GET albo $_POST, zgod­nie z metodą for­mu­la­rza.
Samą metodę bie­żą­ce­go żą­da­nia podaje $_SERVER['REQUEST_METHOD'], czyli napis GET albo POST. To naj­czyst­szy sposób od­róż­nie­nia pierw­sze­go wej­ścia na stronę od wy­sła­nia for­mu­la­rza.
<?php if ($_SERVER['REQUEST_METHOD'] === 'POST') { echo "Formularz zostal wyslany."; } else { echo "Wypelnij formularz."; }

Odbiór danych krok po kroku

Odbiór for­mu­la­rza to spraw­dze­nie, czy dane w ogóle przy­szły, odczyt war­to­ści po klu­czach i przy­go­to­wa­nie ich do użycia. Arkusz CKE prosi zwykle o for­mu­larz wy­sy­ła­ny metodą POST do tej samej strony, czyli action="", więc jeden plik za­wie­ra i for­mu­larz, i skrypt, który go ob­słu­gu­je. Przy pierw­szym wej­ściu ta­bli­ca $_POST jest pusta, dla­te­go cały odbiór stoi w wa­run­ku.
Wa­ru­nek buduje się na przy­ci­sku wy­sy­ła­ją­cym: isset($_POST['wyslij']) (CZY USTAWIONE) jest prawdą do­pie­ro wtedy, gdy for­mu­larz z polem o nazwie wyslij został wy­sła­ny. Sama funk­cja isset spraw­dza, czy klucz w ta­bli­cy ist­nie­je, i nie zgła­sza przy tym ostrze­że­nia, w od­róż­nie­niu od zwy­kłe­go od­czy­tu. Ten sam skutek daje ope­ra­tor ?? (war­tość za­pa­so­wa), który oddaje war­tość spod klucza albo podaną war­tość do­myśl­ną, gdy klucza nie ma.
War­tość z pola tek­sto­we­go jest zawsze na­pi­sem, także wtedy, gdy pole miało type="number". Przed uży­ciem obcina się z niej białe znaki funk­cją trim (PRZYTNIJ), a liczbę uzy­sku­je się rzu­to­wa­niem (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 imie­nia Anna, pro­duk­tu mysz, ilości 2 i za­zna­czo­ne­go pola rabatu skrypt wy­pi­su­je:
Zamowienie dla: Anna Produkt: mysz, sztuk: 2 Rabat: tak
Po­szcze­gól­ne ro­dza­je kon­tro­lek za­cho­wu­ją się przy od­bio­rze różnie. Lista wyboru i przy­ci­ski opcji przy­sy­ła­ją war­tość atry­bu­tu value za­zna­czo­nej po­zy­cji, a nie jej wi­docz­ny napis, więc $_POST['produkt'] to mysz, a nie Mysz bezprzewodowa. Grupa przy­ci­sków opcji ma wspól­ny name, więc daje jeden klucz z jedną war­to­ścią. Pole wyboru za­cho­wu­je się ina­czej niż wszyst­kie po­zo­sta­łe: nie­za­zna­czo­ne nie wysyła NICZEGO, więc klucza rabat nie ma w ta­bli­cy i tylko isset od­róż­nia za­zna­czo­ne od pu­ste­go. Kilka pól o nazwie za­koń­czo­nej na­wia­sa­mi kwa­dra­to­wy­mi, na przy­kład name="kolory[]", przy­sy­ła ta­bli­cę: $_POST['kolory'] jest wtedy ta­bli­cą in­dek­so­wa­ną za­zna­czo­nych war­to­ści, po której prze­cho­dzi się pętlą foreach (DLA KAŻDEGO) albo składa w napis funk­cją implode.

Wa­li­da­cja po stro­nie ser­we­ra

Wa­li­da­cja to spraw­dze­nie, czy wpro­wa­dzo­ne dane speł­nia­ją okre­ślo­ne reguły. Prze­glą­dar­ka robi część tej pracy sama, me­cha­ni­zma­mi wa­li­da­cji HTML5: required blo­ku­je puste pole, type="email" wymaga adresu ze zna­kiem @, min i max pil­nu­ją za­kre­su liczby, a pattern po­rów­nu­je war­tość z wy­ra­że­niem re­gu­lar­nym. To wygoda dla użyt­kow­ni­ka, nie za­bez­pie­cze­nie: atry­bu­ty można usunąć w na­rzę­dziach prze­glą­dar­ki, for­mu­larz da się wysłać z innej strony, a przy me­to­dzie GET wy­star­czy wpisać pa­ra­me­try w pasek adresu. Reguły muszą więc po­wtó­rzyć się w skryp­cie na ser­we­rze, i ten zestaw jest wią­żą­cy.
Spraw­dzo­ne wyniki zbiera się w ta­bli­cy błędów za­miast prze­ry­wać skrypt przy pierw­szym po­tknię­ciu: użyt­kow­nik do­sta­je wtedy kom­plet 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 wa­ru­nek pil­nu­je innej reguły. empty (PUSTE) mówi, czy war­tość jest pusta, ale uznaje za puste także zero i napis "0", więc do ilości nadaje się po­rów­na­nie za­kre­su, a nie empty. is_numeric (CZY LICZBA) spraw­dza, czy napis w ogóle jest liczbą, zanim po­rów­na się go z za­kre­sem. strlen (DŁUGOŚĆ NAPISU) liczy znaki, czyli pil­nu­je krót­kie­go imie­nia i zbyt dłu­gie­go tekstu. filter_var (FILTRUJ ZMIENNĄ) z regułą FILTER_VALIDATE_EMAIL oddaje po­praw­ny adres albo false, więc wynik te­stu­je się prze­cze­niem. Format o stałym kształ­cie spraw­dza wy­ra­że­nie re­gu­lar­ne funk­cją 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.'; }
Wzo­rzec czyta się ka­wa­łek po ka­wał­ku: ^ to po­czą­tek war­to­ści, [0-9] jedna cyfra, {2} do­kład­nie dwa takie znaki, potem dywiz, trzy cyfry i $, czyli koniec war­to­ści. Bez ^ i $ wzo­rzec pa­so­wał­by do frag­men­tu, więc abc 12-345 xyz prze­szło­by spraw­dze­nie.
Jeżeli ta­bli­ca błędów zo­sta­ła pusta, dane idą dalej: do bazy, do pliku albo na ekran. Jeżeli błędy są, wy­pi­su­je się je nad for­mu­la­rzem, a pola wy­peł­nia z po­wro­tem tym, co użyt­kow­nik już wpisał, żeby nie za­czy­nał od nowa. Wsta­wia­na war­tość musi przejść przez htmlspecialchars.
<?php echo '<input type="text" name="imie" value="' . htmlspecialchars($imie) . '">'; foreach ($bledy as $blad) { echo '<p>' . $blad . '</p>'; }

html­spe­cial­chars i ko­do­wa­nie wyj­ścia

htmlspecialchars (ZNAKI SPECJALNE HTML) za­mie­nia znaki o zna­cze­niu w HTML na encje: < na &lt;, > na &gt;, & na &amp;, a cu­dzy­słów na &quot;. Prze­glą­dar­ka po­ka­zu­je wtedy te znaki jako zwykły tekst, za­miast czytać je jako kod. Bez tego war­tość wpi­sa­na w pole i wy­pi­sa­na z po­wro­tem na stro­nie może wnieść na nią cudzy skrypt, co nazywa się ata­kiem XSS (cross-site scrip­ting, wstrzyk­nię­cie skryp­tu na stronę): na­past­nik wysyła for­mu­la­rzem znacz­nik script, a prze­glą­dar­ka każ­de­go od­wie­dza­ją­ce­go ten skrypt wy­ko­nu­je. Zasada dzieli się na dwa kroki: wej­ście wa­li­duj, spraw­dza­jąc format i zakres przy od­bio­rze, a wyj­ście koduj, prze­pusz­cza­jąc przez htmlspecialchars wszyst­ko, co wraca na stronę. Trze­cim, osob­nym kro­kiem jest za­bez­pie­cze­nie za­py­ta­nia SQL, opi­sa­ne w lekcji o bazie danych.

Ta­bli­ce su­per­glo­bal­ne

Ta­bli­ca su­per­glo­bal­na to wbu­do­wa­na ta­bli­ca aso­cja­cyj­na, do­stęp­na w każdym miej­scu skryp­tu, także we­wnątrz funk­cji, bez de­kla­ra­cji global. Nazwa każdej za­czy­na się od $_, a klucze są na­pi­sa­mi. To o nie pyta bank pytań w kształ­cie „w której ta­bli­cy będą do­stęp­ne dane".
Ta­bli­ca
Co niesie
$_GET
dane z for­mu­la­rza wy­sła­ne­go metodą GET oraz pa­ra­me­try z adresu
$_POST
dane z for­mu­la­rza wy­sła­ne­go metodą POST, w treści żą­da­nia
$_REQUEST
za­war­tość $_GET, $_POST i $_COOKIE razem, bez in­for­ma­cji o źródle
$_SERVER
dane o ser­we­rze i żą­da­niu: adres IP, pro­to­kół, metoda, nazwa skryp­tu
$_FILES
pliki wy­sła­ne for­mu­la­rzem: nazwa, typ, roz­miar, ścież­ka tym­cza­so­wa
$_COOKIE
cia­stecz­ka przy­sła­ne przez prze­glą­dar­kę
$_SESSION
dane bie­żą­cej sesji, prze­cho­wy­wa­ne po stro­nie ser­we­ra
$GLOBALS
wszyst­kie zmien­ne glo­bal­ne skryp­tu, po na­zwach
Wy­sył­ka pliku wymaga trzech rzeczy naraz: metody POST, atry­bu­tu enctype="multipart/form-data" w znacz­ni­ku form i pola type="file". Ode­bra­ny plik leży w ka­ta­lo­gu tym­cza­so­wym, a $_FILES opi­su­je go klu­cza­mi name (nazwa z dysku użyt­kow­ni­ka), tmp_name (ścież­ka tym­cza­so­wa), size (roz­miar w baj­tach), type i error. Plik trzeba prze­nieść na miej­sce do­ce­lo­we funk­cją move_uploaded_file (PRZENIEŚ WYSŁANY PLIK), bo po za­koń­cze­niu skryp­tu wersja tym­cza­so­wa znika. Cia­stecz­ka i sesje omawia lekcja o pli­kach, cia­stecz­kach i se­sjach.

Na eg­za­mi­nie

Py­ta­nia pi­sem­ne o ob­słu­gę for­mu­la­rzy mają kilka sta­łych kształ­tów. Pierw­szy to adres w pasku prze­glą­dar­ki: skoro po nazwie pliku widać ?imie=Anna, metodą była GET, a dane cze­ka­ją w $_GET. Drugi to pole <input name="im"> wy­peł­nio­ne war­to­ścią Janek: w ta­bli­cy $_POST znaj­dzie się war­tość Janek pod in­dek­sem im, a nie nazwa pola pod in­dek­sem Janek. Trzeci to po­praw­ny zapis od­czy­tu, w którym liczy się każdy znak: $name = $_POST['imie']; ma znak dolara przed nazwą zmien­nej, pod­kre­śle­nie w nazwie ta­bli­cy, klucz w apo­stro­fach i klucz pisany do­kład­nie tak jak name pola, z małej litery i bez pol­skich znaków. Czwar­ty to de­fi­ni­cja: wa­li­da­cja pól for­mu­la­rza polega na spraw­dze­niu, czy wpro­wa­dzo­ne dane speł­nia­ją okre­ślo­ne reguły, a nie na spraw­dze­niu, kto je wpro­wa­dził ani czy plik od­bie­ra­ją­cy ist­nie­je. Piąty roz­dzie­la dwa języki: spraw­dza­nie w trak­cie wy­peł­nia­nia, znak po znaku, bez prze­ła­do­wa­nia strony należy do JavaScriptu, a spraw­dza­nie po wy­sła­niu, po stro­nie ser­we­ra, do PHP. Szósty pyta o dane poufne: od­po­wiedź to metoda POST przez pro­to­kół HTTPS. Siódmy to za­war­tość po­zo­sta­łych tablic: $_SERVER niesie adres IP i nazwę pro­to­ko­łu, $_SESSION dane lo­go­wa­nia, $_COOKIE cia­stecz­ka. Ósmy po­ka­zu­je kod z ostrze­że­niem Warning: Undefined array key i pyta o przy­czy­nę: skrypt czyta klucz z $_POST, zanim for­mu­larz co­kol­wiek wysłał.
Pu­łap­ka: nie­za­zna­czo­ne pole wyboru nie wysyła żadnej war­to­ści, więc jego klucza w ogóle nie ma w $_POST. Odczyt $_POST['rabat'] daje wtedy ostrze­że­nie, a stan pola spraw­dza się przez isset.
W za­da­niu prak­tycz­nym sche­mat jest zawsze ten sam. For­mu­larz idzie metodą POST do tej samej strony, więc method="post" i action="", a cały odbiór stoi w wa­run­ku spraw­dza­ją­cym przy­cisk wy­sy­ła­ją­cy. Nazwy kluczy w $_POST muszą zga­dzać się co do znaku z atry­bu­ta­mi name z for­mu­la­rza opi­sa­ne­go w treści za­da­nia. Ode­bra­na war­tość trafia potem do klau­zu­li WHERE za­py­ta­nia, a przed wsta­wie­niem do za­py­ta­nia liczbę rzu­tu­je się na (int), na­to­miast war­tość tek­sto­wą bez­piecz­nie prze­ka­zu­je się wy­łącz­nie za­py­ta­niem przy­go­to­wa­nym, opi­sa­nym w lekcji o PHP i bazie danych. Wy­świe­tla­ne wyniki i war­to­ści wra­ca­ją­ce do pól prze­pusz­cza się przez htmlspecialchars. Strona musi być otwar­ta przez adres http://localhost/..., bo plik wczy­ta­ny z dysku pokaże kod PHP za­miast go wy­ko­nać.

Ściąga

Zapis
Znaczy
$_GET['imie']
war­tość pola z for­mu­la­rza wy­sła­ne­go metodą GET
$_POST['imie']
war­tość pola z for­mu­la­rza wy­sła­ne­go metodą POST
$_SERVER['REQUEST_METHOD']
metoda bie­żą­ce­go żą­da­nia: GET albo POST
isset($_POST['wyslij'])
czy for­mu­larz został wy­sła­ny, czyli czy klucz ist­nie­je
$_POST['uwagi'] ?? ''
war­tość spod klucza albo war­tość do­myśl­na, gdy klucza brak
trim($_POST['imie'])
war­tość bez bia­łych znaków z po­cząt­ku i końca
(int) $_POST['ilosc']
war­tość pola jako liczba cał­ko­wi­ta
empty($x)
czy war­tość 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)
po­praw­ny adres poczty albo false
preg_match('/^[0-9]{2}-[0-9]{3}$/', $k)
czy war­tość pasuje do wzorca, tu kod pocz­to­wy
htmlspecialchars($x)
za­mia­na znaków HTML na encje, ochro­na przed XSS
$_FILES['zdjecie']['tmp_name']
ścież­ka tym­cza­so­wa wy­sła­ne­go pliku
Pobierz ściągę PDF
ZAWODNIK
egzamin zawodowy INF.02 · INF.03 · INF.04
Serwer Discord
Egzaminy zawodowe IT/Matury/Studia
@yastor
© 2026 Zawodnik · by Yastor
Arkusze i zasady oceniania pochodzą z CKE. Serwis nie jest powiązany z Centralną Komisją Egzaminacyjną.