(hlavička webu)

Předání dat na server

V rámci základního kurzu jsme si už ukázali základy práce s proměnnými, ale ještě nám chybí způsob, jakým do těch proměnných dostat nějaká data od návštěvníka stránky. O tom bude tato kapitola.

Získání dat od návštěvníka stránky

Z HTML víme, že návštěvník stránky může data zadávat pomocí [odkaz]formulářů[/odkaz]. Formulář může obsahovat různé druhy políček, má atribut action pro určení adresy, kam se mají data odeslat (tam vyplníme adresu PHP skriptu, který s nimi má pracovat) a atribut method, který určuje metodu odeslání dat (GET nebo POST).

Stačí tedy v atributu action uvést adresu PHP skriptu a po odeslání formuláře se data předají tomuto skriptu. Na straně serveru je pak možné data formuláře číst pomocí tří speciálních proměnných, které definuje samo PHP. Jmenují se $_REQUEST, $_GET a $_POST a jde o pole (viz kapitola [odkaz]o polích[/odkaz]). Už názvy proměnných napovídají, že přijatá data jsou do nich organizovaná podle toho, jakou metodou byla odeslána. Data přijatá metodou GET se uloží do pole $_GET, data přijatá metodou POST do pole $_POST. A kromě toho jsou data přijatá libovolnou metodou ještě uložena v poli $_REQUEST.

Data jsou organizována tak, že klíčem je název formulářového políčka (hodnota atributu name) a hodnotou pole je hodnota formulářového políčka. Políčko bez atributu name se neodešle. Hodnotou přepínacího pole (radio) a výklopného seznamu (select) je hodnota zvolené možnosti. Zaškrtávací pole (checkbox) se v případě, že není zaškrtnuté, neodešle vůbec. Pokud zaškrtnuté je, odešle se hodnota atributu value a není-li nastavena, řetězec "on".

Příklad:

<?php
  // vložení hlavičky stránky, kterou jsme vytvořili ve [odkaz]4. kapitole[/odkaz]
  include "spolecna_hlavicka.php";
  // jestliže je nastaveno $_GET["cislo"], tedy bylo něco odesláno
  if (isset($_GET["cislo"])) {
    echo "Naposledy odeslaná hodnota: " . $_GET["cislo"];
    echo "<br> data GET:";
    var_dump($_GET);
  }
  // níže HTML formulář pro odeslání
?>
<form action="" method="get">
<label for="cislo">Zadejte číslo:</label> 
<input type="text" name="cislo" id="cislo">
<input type="submit" name="odeslat" value="Odeslat">
</form>
Uložte třeba jako k12p1.php do kořene webu a vyzkoušejte. Všimněte si, jak je položka v $_GET svázána s atributem name u políčka. Také si všimněte, že v datech GET se odešle i hodnota odesílacího tlačítka, pomocí kterého byl formulář odeslán.

Rozdíl mezi GET a POST

Metoda GET odešle data v adrese stránky (zkontrolujte blíže adresu po odeslání nějaké hodnoty u výše uvedeného příkladu). Metoda POST data odesílá v hlavičce HTTP požadavku a adresu stránky neovlivní. Z toho vyplývá, že u metody GET lze serveru data předat kromě formuláře i vytvořením příslušného odkazu. Vyzkoušejte k příkladu výše v prohlížeči otevřít adresu: http://localhost/k12p1.php?cislo=1 Metoda GET je proto vhodná tam, kde je toto chování přínosné, například vyhledávání. Metoda POST je pak vhodná v situacích, kdy je toto naopak nežádoucí, jako například přihlašovací formulář (jednak je nevhodné, aby přihlašovací jméno a heslo bylo viditelné v adrese a jednak by návštěvník mohl někomu poslat odkaz se svými přihlašovacími údaji.

Kontrola a ošetření vstupních dat

Možná jste si u příkladu výše všimli, že popisek sice říká "Zadejte číslo", ale ve skutečnosti jde do políčka zadat prakticky cokoliv. Toto se ovšem týká veškerých dat zaslaných od klienta. Ať už jde o důsledek nějaké chyby, omyl návštěvníka nebo záměr, zadaná data mohou vypadat jinak, než programátor očekává. Proto platí jedna důležitá poučka: Data přijatá od uživatele jsou vždy nedůvěryhodná. Cokoli přišlo od návštěvníka nemusí obsahovat to, co očekáváme. A to včetně polí označených jen pro čtení (artibuty readonly anebo disabled) a skrytých polí. Spoléhat se nelze ani na Javascriptové kontroly, protože návštěvník může mít Javascript vypnutý, případně kontrolu nějakým způsobem obejít. Javascriptové kontroly slouží pro komfort návštěvníka (na chybnou hodnotu je upozorněn hned, ne až po odeslání formuláře), ale správnost dat si musíme ohlídat přímo v PHP na straně serveru.

Kontrola správnosti se liší podle typu odesílaných dat. V první řadě ale libovolný prvek může být prázdný, takže se nám budou hodit funkce isset a empty (připomeňme, že jednak mají navzájem obrácenou logiku, tj. pokud v proměnné je neprázdná hodnota, isset vrátí true a empty vrátí false, jednak isset testuje pouze zda proměnná nějakou hodnotu má, empty testuje i zda hodnota není „prázdná“, tedy prázdný řetězec, nula, false apod). V případě zaškrtávacího pole nám (vzhledem k výše popsanému mechanismu jeho fungování) tedy často stačí jen funkce empty nebo isset:

// V $zaskrtnuto bude true/false podle toho, zda checkbox "zaskrtavaci" byl/nebyl označen
$zaskrtnuto = isset($_POST["zaskrtavaci"]);
// Totéž pomocí funkce empty. Vzhledem k obrácené logice použijeme negaci
$zaskrtnuto = !empty($_POST["zaskrtavaci"]);
Stejný postup můžeme použít u libovolného pole pro získání informace, zda je či není vyplněné.

Očekáváme-li celé číslo, po zkontrolování, zda je dané pole vůbec vyplněné, můžeme použít přetypování na číslo, nebo funkci intval, která udělá prakticky totéž.

<?php
if (isset($_GET["cislo"])) {
  $cislo = (int) $_GET["cislo"];
  // Nebo by šlo použít: $cislo = intval($_GET["cislo"]);
} else {
  // hodnota pokud položka není nastavena
  $cislo = 0;
}
?>
Tento zdlouhavý zápis můžeme zkrátit použitím podmíněného operátoru:
<?php
$cislo = isset($_GET["cislo"]) ? intval($_GET["cislo"]) : 0;
// Druhá varianta téhož: 
$cislo = isset($_GET["cislo"]) ? (int) $_GET["cislo"] : 0;
?>
Pro ověření, zda hodnota je v určitém rozsahu, můžeme použít běžné podmínky a aritmetické operátory. Připomeňme, že ne všechny hodnoty tvořené číslicemi jsou z programátorského pohledu čísla. Na čísla je vhodné převádět jen ty hodnoty, které chceme jako čísla používat ve výpočtech. Hodnoty jako telefonní číslo, číslo popisné, PSČ atp. nejsou z programátorského pohledu čísla, ale řetězce. Může to ale záviset i na kontextu, například rodné číslo bude obvykle řetězec, ale pokud bychom chtěli kontrolovat jeho správnost (rodné číslo musí být dělitelné 11), v tomto kontextu s ním pracujeme jako s číslem.

U desetinných čísel lze použít analogický postup, přetypování anebo funkci floatval, ale komplikuje se to tím, že se v češtině používá desetinná čárka, zatímco v PHP desetinná tečka. Můžeme použít funkci pro nahrazení řetězce str_replace, nahradit čárku za tečku a pak zkusit převod. Použití funkce str_replace je: str_replace(nahradit co, čím, v čem) Příklad:

<?php
$cislo = empty($_GET["cislo"]) ? 0 : $_GET["cislo"];
$cislo = (float) str_replace(",", ".", $cislo);
?>
Nejrozmanitější je ověřování u řetězců, kde záleží na tom, co má být obsahem řetězce. Uveďme některé užitečné funkce, které lze použít:

Poznámka: Dejte pozor na kombinování „bajtových“ a „znakových“ variant, můžete pak získat neočekávané výsledky. Zároveň zejména při použití „znakových“ (mb_*) funkcí a kódování UTF-8 nezapomeňte nastavit interní kódování PHP:

mb_internal_encoding("UTF-8");
(viz také [odkaz]kapitola 4[/odkaz])

Ukažme si jako příklad jednoduchou validaci e-mailové adresy. (Poznámka: Přesná kontrola formátu e-mailové adresy je daleko složitější než by se zdálo a téměř nikde se nedělá. My si ukážeme základní jednoduchou kontrolu, důslednější kontroly se dělají pomocí regulárních výrazů, o kterých si povíme v pokročilejší části této učebnice).

<?php
// vložení hlavičky stránky, kterou jsme vytvořili ve [odkaz]4. kapitole[/odkaz]
include "spolecna_hlavicka.php";

if (isset($_POST["email"])) { $adresa = $_POST["email"]; $kontrola = true;

// strpos vrátí false pokud tam není vůbec a 0 pokud je hned na začátku. // Ani jedno není platný e-mail, takže v tomto případě nemusíme rozlišovat. $zavinac = mb_strpos($adresa, "@");

// část adresy od zavináče do konce $zaZavinacem = ($zavinac > 0) ? mb_substr($adresa, $zavinac + 1) : "";

// Adresa není prázdná, obsahuje "@" (ne jako první znak), // část za "@" obsahuje tečku a neobsahuje další "@" if (!empty($adresa) && ($zavinac > 0) && (mb_strpos($zaZavinacem, ".") > 0) && (mb_strpos($zaZavinacem, "@") === false)) { echo "Zadaná adresa vypadá v pořádku"; } else { echo "Nebyla zadána správná e-mailová adresa"; } } ?>

<form action="" method="post"> <label for="email">Zadejte e-mailovou adresu:</label> <input type="text" name="email" id="email"> <input type="submit" name="odeslat" value="Odeslat"> </form>

Vraťme se ještě k prvnímu příkladu, kde je vidět další důležitá věc: Všimněte si, že když do políčka zadáte HTML kód, tento kód se zpracuje. Tzn. když zadáte třeba „<b>Ahoj!</b>“, při výpisu se zobrazí „Ahoj!“ tučným písmem. Aby stránka zpracovala libovolný HTML kód zadaný návštěvníkem je ve většině případů nežádoucí (pamatujte, že tak lze zadat třeba i Javascript). Zároveň většina návštěvníků HTML neovládá a mohla by nevědomky způsobit problémy se zobrazením stránky. Po běžném návštěvníkovi také nemůžete chtít, aby třeba znak „<“ do příspěvku zapisoval entitou „&lt;“ a podobně. Proto je většinou nutné možnosti vkládání HTML kódu návštěvníkem buď omezit, nebo úplně zakázat.

K tomu slouží dvě PHP funkce:

Výsledkem příkazu echo strip_tags("<b>Ahoj!</b>"); tedy bude řetězec "Ahoj!", čili HTML tagy se odstraní. Výsledkem příkazu echo htmlspecialchars("<b>Ahoj!</b>"); bude řetězec "&lt;b&gt;Ahoj!&lt;/b&gt;" a při jeho vypsání do stránky se v prohlížeči objeví text „<b>Ahoj!</b>“,

Příklad:

<?php
// vložení hlavičky stránky, kterou jsme vytvořili ve [odkaz]4. kapitole[/odkaz]
include "spolecna_hlavicka.php";
if (isset($_POST["text"])) {
  echo "Naposledy odeslaná hodnota: " . htmlspecialchars($_POST["text"]);
}
// níže HTML formulář pro odeslání
?>
<form action="" method="post">
<label for="text">Toto pole bezpečně zpracuje i HTML kód:</label> 
<textarea type="text" name="text" id="text"></textarea>
<input type="submit" name="odeslat" value="Odeslat">
</form>
S textem od uživatele se ovšem dá dělat spousta jiných věcí než zobrazit do stránky (většinou se někam ukládá) a většina z nich má svoje metody ošetření dat. Proto použití htmlspecialchars nebo strip_tags není univerzální řešení pro všechny situace a rozhodně to neřeší problém špatných vstupů ve všech kontextech. Proto ani nemá smysl používat htmlspecialchars bezhlavě na všechny řetězcové vstupy. Naopak se aplikuje pouze na řetězce, které se mají vypsat do stránky (a nemá v nich být HTML kód). Pokud se řetězec nejdříve uloží například do souboru nebo databáze a někdy později se vypisuje, je lepší HTML značky odstraňovat až před výpisem, než hned před uložením. Naopak před uložením může být nutné řetězec upravit pro zvolený způsob uložení (například pro SQL dotaz při uložení do databáze). To si ukážeme později v této učebnici.

(patička webu)