Akce! Dobij si body, napiš nám do zpráv "Přes léto se to naučím!" a dobijeme ti ještě navíc 50% z této částky! Sleva na výuku platí do 22.6.2018.

Lekce 9 - Vylepšení kontaktního formuláře v PHP

PHP Základní konstrukce Vylepšení kontaktního formuláře v PHP American English version English version

ONEbit hosting Unicorn College Tento obsah je dostupný zdarma v rámci projektu IT lidem. Vydávání, hosting a aktualizace umožňují jeho sponzoři.

V minulé lekci, Kontaktní emailový formulář v PHP, jsme naprogramovali jednoduchý emailový formulář. V dnešním PHP tutoriálu ho vylepšíme.

Předvyplnění polí

Když něco špatně vyplníme, skript nám to oznámí, ale formulář je již prázdný. Je to samozřejmě proto, že zobrazujeme již jinou stránku, než do které jsme data zadávali.

Protože zpracováváme data tím samým skriptem, ve kterém je formulář, můžeme do formuláře jednoduše vložit hodnoty z POSTu v případě, že byly nějaké odeslány. Budete to vypadat, jako by se formulář ani nevymazal.

V PHP bloku těsně před formulářem si naplníme proměnné $jmeno, $email a $zprava hodnotami z $_POST. To můžeme udělat samozřejmě jen v případě, když v POSTu tyto hodnoty jsou. Pokud ne, dáme do proměnných prázdné řetězce.

V této chvíli bychom kód napsali asi takto:

if (isset($_POST['jmeno']))
        $jmeno = $_POST['jmeno'];
else
        $jmeno = '';

Kód je poměrně dlouhý a psát ho pro všechny 3 proměnné by bylo nepohodlné. Zvláště, když by formulář obsahoval ještě další pole. Proto si uvedeme tzv. ternární výraz. Jedná se o zkrácenou podobu if ... else. Ternání výraz vždy vrací nějakou hodnotu, nedá se tedy použít jen místo podmínky. Skládá se ze tří části, což by nás podle jeho názvu nemělo překvapit :) V první části uvedeme podmínku, dále znak ? a poté hodnotu, kterou má výraz vrátit, pokud podmínka platí. Za ní uvedeme : a hodnotu, která se má vrátit, když podmínka neplatí.

Proměnnou bychom pomocí ternárního výrazu naplnili takto:

$jmeno = (isset($_POST['jmeno'])) ? $_POST['jmeno'] : '';

Ještě malá rekapitulace. Pokud v POSTu existuje daný klíč, vložíme tuto hodnotu do proměnné $jmeno. Pokud ne, vložíme tam prázdný textový řetězec, což jsou jednoduše uvozovky, mezi kterými nic není.

Toto uděláme ještě s emailem a zprávou.

Formulář dále upravíme tak, aby se do jeho polí vkládaly hodnoty z těchto proměnných. Pokud nebyl formulář odeslán, vloží se tam prázdné řetězce, jinak se tam vloží to, co uživatel vyplnil.

Do formulářového pole bychom mohli hodnotu vložit takto:

<input name="jmeno" type="text" value="<?php echo $jmeno ?>" />

Pokud chceme v PHP sekvenci pouze vypsat obsah proměnné, použijeme k tomu zkrácenou direktivu <?=. Stejného výstupu tedy můžeme dosáhnout i kratším zápisem:

<input name="jmeno" type="text" value="<?= $jmeno ?>" />

Stále to však není ono. Jelikož text v proměnné $jmeno pochází od uživatele, nemůžeme si být jistí, že je bezpečný. Co kdyby nám do textu uživatel vložil nějaký HTML kód? Vložil by se poté normálně do naší stránky.

V našem případě by to tolik nevadilo, ale jsou případy, kde ano. Představte si, že vypisujete na stránce zprávy od uživatelů, třeba komentáře k nějakému článku. A jeden uživatel místo textu zprávy vložil HTML kód s formulářem, který je nasměrovaný na jeho obslužný skript na jiném webu a ve kterém požaduje od vašich uživatelů heslo. Při vypisování zprávy se tento kód vypíše do stránky a zobrazí se jeho formulář. Někteří vaši uživatelé tam opravdu zadají své heslo, které se odešle někam útočníkovi. A vy máte problém.

Tomuto typu útoku se říká XSS (jako Cross Site Scripting). Obrana je velmi jednoduchá, před vypsáním obsahu kterékoli proměnné do HTML kódu stránky použijte funkci htmlspecialchar­s(). Ta převede špičaté HTML závorky a pár dalších znaků na tzv. HTML entity. Případný škodlivý kód je potom braný jen jako obyčejný text a prohlížeč ho nezpracuje jako HTML kód.

Je potřeba při výpisu ošetřovat opravdu každou proměnnou, i ty, které uživatel přímo nezadá. Nikdy totiž nevíte, jestli se obsah proměnné neskládá z něčeho, co uživatel zadal a jednoduše se to nedá ohlídat. Proto se ošetří vše a to přímo na tom místě, kde proměnnou vypisujeme. Až budete pokročilejší a budete znát objektovou architekturu, dokážete tento proces zautomatizovat, abyste funkci nemuseli ručně psát.

Ukažme si konečně upravený HTML kód formuláře i s direktivou nad ním:

<?php
        if ($hlaska)
                echo('<p>' . htmlspecialchars($hlaska) . '</p>');

        $jmeno = (isset($_POST['jmeno'])) ? $_POST['jmeno'] : '';
        $email = (isset($_POST['email'])) ? $_POST['email'] : '';
        $zprava = (isset($_POST['zprava'])) ? $_POST['zprava'] : '';
?>

<form method="POST">
        <table>
                <tr>
                        <td>Vaše jméno</td>
                        <td><input name="jmeno" type="text" value="<?= htmlspecialchars($jmeno) ?>"/></td>
                </tr>
                <tr>
                        <td>Váš email</td>
                        <td><input name="email" type="email" value="<?= htmlspecialchars($email) ?>"/></td>
                </tr>
                <tr>
                        <td>Aktuální rok</td>
                        <td><input name="rok" type="number" /></td>
                </tr>
        </table>
        <textarea name="zprava"><?= htmlspecialchars($zprava) ?></textarea>
        <br />

        <input type="submit" value="Odeslat" />
</form>

Formulář si vyzkoušejme. Ponechme nějaké pole prázdné a odešleme, ukáže se chybová hláška a zároveň zůstane i to, co uživatel vyplnil:

Kontaktní formulář
localhost/mail­form.php

Přesměrování

Formulář má nyní dvě podstatné vady. Pokud zprávu úspěšně odešleme, stejně zůstane předvyplněný. A hlavně pokud po odeslání stiskneme F5, formulář se odešle znovu. Tímto neduhem trpí všechny formuláře, pokud jej zpracovává ten samý soubor, ve kterém je formulář vložený (což je většinou nutné).

Pokud je zpracování formuláře dokončeno, měli bychom provést přesměrování. Přesměrujeme na tu samou adresu, na které je formulář. Díky přesměrování se ovšem ztratí data v $_POST. Následné obnovení stránky tak již nic neodešle.

Přesměrování provedeme pomocí funkce header. Ta odešle tzv. hlavičku prohlížeči. Právě hlavička může obsahovat informaci o přesměrování a to pomocí slova Location.

Po uložení úspěšné hlášky tedy formulář přesměrujeme:

if ($uspech)
{
        $hlaska = 'Email byl úspěšně odeslán, brzy vám odpovíme.';
        header('Location: mailform.php');
        exit;
}

Funkcí exit() ukončíme běh skriptu, jelikož samotné přesměrování ho nezastaví, jen pošle prohlížeči návštěvníka informaci o tom, že se má přesunout na jinou lokaci.

Formulář již netrpí opětovným odesláním při obnovení stránky. Nezobrazuje ovšem ani hlášku o úspěchu. Mělo by vám být jasné proč, když přesměrujeme na jinou stránku, obsah proměnných na stránce stávající se ztratí a tím i ten v $hlaska. Řešení je několik, tím nejjednodušším je předat pomocí GET parametru stránce nějakou proměnnou. Podle toho stránka pozná, že na ní bylo přesměrováno po úspěšném odeslání a zobrazí o tom zprávu. Kód změníme na následující podobu:

header('Location: mailform.php?uspech=ano');
exit;

Přesuneme se na začátek skriptu, kde hlášku nastavujeme na prázdný string. Podíváme se, zda nám nepřišel v adrese parametr úspěch. Pokud ano, nastavíme hlášku na text, který se má při úspěchu uživateli vypsat. Již víme, že parametry z URL adresy přicházejí na rozdíl od parametrů z formuláře v poli $_GET:

$hlaska = '';
if (isset($_GET['uspech']))
        $hlaska = 'Email byl úspěšně odeslán, brzy vám odpovíme.';

Můžete si vychutnat dobře fungující formulář.

POZOR! Přesměrovat můžeme pouze v případě, že jsme ještě nevypsali žádné HTML. Jakmile se totiž začne něco vypisovat, PHP prohlížeči odešle hlavičku, kde mu říká, že mu posílá HTML soubor. Hlavičku lze samozřejmě odeslat jen jednou, když se pokusíme veprostřed souboru přesměrovat, dostaneme chybovou hlášku "Headers already sent" a k přesměrování nedojde. To by se stalo třeba v tomto případě:

<html>
        <body>
                <?php
                        // Tento kód je špatně
                        header('Location: index.php');
                        exit;
                ?>
        </body>
</html>

Dávejte si pozor i na tento případ, ve kterém chyba není na první pohled vidět:

-- Zde je prázdná řádka --
<?php

        header('Location: index.php');

?>
<html>
        <body>
        </body>
</html>

Na začátku souboru je odřádkování. I to je znak, který odstartuje výstup a PHP ho odesílá prohlížeči spolu s hlavičkou. Podobný problém bývá s mezerou. Pokud ukládáte UTF-8 soubory s tzv. BOM, může dělat právě tyto problémy. Pokud však používáte kvalitní IDE, tak se vám to nestane.

Pozn.: Někteří začátečníci přesměrovávají tak, že vyechují JavaScript, pomocí kterého změní adresu okna. Vypadá asi takto:

// Tento kód je špatně
echo('<script type="text/javascript">
        window.location = "index.php"
</script>');

Tento způsob je však nespolehlivý a někdy i nebezpečný. Nepoužívejte ho.

Formulář si můžete nastylovat, aby vypadal lépe. Hlášku dát do nějaké bubliny a podobně. To ale již není předmětem tohoto PHP kurzu, stylování se probírá v jiném on-line kurzu :) Kompletní formulář je ke stažení níže. V příští lekci, Skládání stránek v PHP, se podíváme na skládání stránek pomocí PHP.


 

Stáhnout

Staženo 2702x (1.33 kB)
Aplikace je včetně zdrojových kódů v jazyce php

 

 

Článek pro vás napsal David Čápka
Avatar
Jak se ti líbí článek?
29 hlasů
Autor pracuje jako softwarový architekt a pedagog na projektu ITnetwork.cz (a jeho zahraničních verzích). Velmi si váží svobody podnikání v naší zemi a věří, že když se člověk neštítí práce, tak dokáže úplně cokoli.
Unicorn College Autor sítě se informační technologie naučil na Unicorn College - prestižní soukromé vysoké škole IT a ekonomie.
Miniatura
Všechny články v sekci
Základní konstrukce jazyka PHP
Miniatura
Následující článek
Skládání stránek v PHP
Aktivity (7)

 

 

Komentáře
Zobrazit starší komentáře (122)

Avatar
hrncal.junior:15. března 18:15

Díky moc, jsem úplný blb. Cpal jsem jabka z hruškama a divím se, že to nefunguje. Už mi to frčí jak má.

 
Odpovědět 15. března 18:15
Avatar
Roman Duchoň:16. dubna 16:47

Nějaké základy už jsem pochytal jinde, jedu si tu kurzy, abych si ty základy rozšířil a tenhle díl nemá chybu! :-)
XSS (jsem nevěděl, že se to tak jmenuje) už mi samo o sobě vrtalo hlavou a přemýšlel jsem, jak mu zabránit, takže htmlspecialchars() se hodí.
header('Location: stranka.php'); s použítím GET je pro mě taky suprová novinka.
Až jsem z toho zaražen a mám obavy, že víc takových informací a budu muset celé doposud vytvořené stránky smazat a udělat odznova...
Ale jednu otázku bych přece jen měl - co se stane, když při "přesměrování" nepoužiji ve funkci exit?

Odpovědět 16. dubna 16:47
RD
Avatar
Jiřina Trojánková:29. dubna 19:25

Ahoj, poraďte prosím, když napíšu: header('Location: #message'); tak mě to po odeslání hodí na správné místo, ale nevypíše hlášku. Když napíšu: header('Location: #message?uspech=a­no'); tak mě to ani nehodí na správnou sekci, ani nevypíše hlášku, ale vypise se uspech=ano v url.. Nedava mi to smysl..
<?php
mb_internal_en­coding("UTF-8");
$hlaska = '';
if (isset($_GET['us­pech']))
$hlaska = 'Email byl úspěšně odeslán, brzy vám odpovíme.';
if ($_POST)
{
if (isset($_POST['fir­stname']) && $_POST['firstname'] &&
isset($_POST['las­tname']) && $_POST['lastname'] &&
isset($_POST['e­mail']) && $_POST['email'] &&
isset($_POST['pho­ne']) && $_POST['phone'] &&
isset($_POST['mes­sage']) && $_POST['message'])
{
$hlavicka = 'From:' . $_POST['email'];
$hlavicka .= "\nMIME-Version: 1.0\n";
$hlavicka .= "Content-Type: text/html; charset=\"utf-8\"\n";
$adresa = 'jirina.trojan­[email protected]';
$predmet = 'Nová zpráva z mailformu';
$uspech = mb_send_mail($a­dresa, $predmet, $_POST['message'], $hlavicka);
if ($uspech)
{
$hlaska = 'Email byl úspěšně odeslán, brzy vám odpovíme.';
header('Location: #message');
//http://www.tes­t.com/index.htm?na­me1=value1&na­me2=value2
exit;
}
else
$hlaska = 'Email se nepodařilo odeslat. Zkontrolujte adresu.';
}
else
$hlaska = 'Formulář není správně vyplněný!';
}
?>

 
Odpovědět 29. dubna 19:25
Avatar
Pavel Kvasil
Člen
Avatar
Pavel Kvasil:21. května 16:45

Zdravím,
super návod, jako .php laikovi velmi přínosný. Mám, prosím, dva dotazy.

/1/ jak mohu řádkovat text v emailu, pokud to jeho těla vložím více položek formuláře? Např.:

$textzpravy = 'jmeno : ' . $_POST['jmeno'];
$textzpravy .= '; prijmeni : ' . $_POST['prijmeni'];
$textzpravy .= '; zprava : ' . $_POST['zprava'];
$uspech = mb_send_mail($a­dresa, $predmet, $textzpravy, $hlavicka);

jde mi o to, aby v mailu byli jednotlivé položky formuláře na samostatném řádku.

/2/ hláška o úspěšném odeslání mi zůstává viditelná i po obnovení stránky?! Jak mohu script upravit tak, aby se hláška zobrazila třeba jako systémová hláška Windows? Nebo aby byla otevřena samostatná stránka, kterou bych mohl upravit de vzhledu původních stránek s formulářem? Načetl jsem tady u vás, že je možné použít běžné html tagy, ale to bylo pořád v rámci stránky s formulářem?!

Díky moc a hezký den.
Pavel K.

Editováno 21. května 16:46
 
Odpovědět 21. května 16:45
Avatar
Ivo Silber
Člen
Avatar
Ivo Silber:24. května 16:40

Tyto stránky jsou super :)

 
Odpovědět 24. května 16:40
Avatar
Pavel Janda
Člen
Avatar
Pavel Janda:28. května 22:35

Ahoj, celkem mi to funguje - mailuje, leč podstatný problém.
Jako SMTP mám v ini souborech nastaven smtp.google.com a jako mailový účet svůj gmail.účet, takže to posílá maily přes google. Nicméně ať už je v $hlavicka přítomno cokoli (tj mail zadaný z formuláře), vždy je v mailu jako odesílatel uveden můj mail. Tj. absolutně nefunguje ona záměna zobrazeného odesílatele pomocí "From:" v příkazu mb_send_mail. Provozuji na lokálně nainstalovaném XAMPP, na Win7.
Zkusil jsem si na jednoduchém webhostingu - ale tam nemám možnost nastavení PHP, takže tam to samozřejmě nelze.
Pokud místo gmailu použiji seznam (tj. smtp.seznam.cz a svůj seznam účet, tak dokonce ani nemohu uvést v From něco jiného než vlastní seznam účet - pokud parametr "From" vynechám tak můj seznam účet musí být v sendmail.ini jako "Force sender", jinak v obou případech se v error logu dozvím , že mám smůlu, že jiného odesílatele než vlastní účet použít nesmím....

Dotaz tedy zní zda nedělám nějakou blbinu (jakou asi), či zda je to tak jak se zdá, že zkrátka zaměnit odesílatele mailu zkrátka asi nelze ... ?

Díky za případný komentář.

 
Odpovědět 28. května 22:35
Avatar
Odpovídá na Pavel Janda
Michal Štěpánek:28. května 23:56

Odesílatel podle mě musí být ten, jehož službu k odeslání používáš, protože tě "něco" musí autentifikovat. Nelze nijak jednoduše poslat z webu mail z adresy "[email protected]­jemail.cz" a k odeslání použít účet na google...

Odpovědět 28. května 23:56
Nikdy neříkej nahlas, že to nejde. Vždycky se totiž najde blbec, který to neví a udělá to...
Avatar
Pavel Janda
Člen
Avatar
Odpovídá na Michal Štěpánek
Pavel Janda:29. května 11:57

Díky za reakci, to jasné, jenže !

  • celý příklad (lekce 8 - 9 ) je postaven právě na tom, že na mou adresu (... adresu "tvůrce" webu" ) přijdou maily s "odesílateli" (tj. From...) zadanými v poli "email" kontaktního formuláře.

Přímo ve výkladu je také jasně uvedeno, že pomoci parametru From v mb_send_mail se přesně tohoto má dosáhnout a že, cituji "Email potom vypadá jako že přišel z této adresy, i když ho odeslalo PHP z vašich stránek".

No a celý problém je, že takto to nefunguje a že logicky maily chodí z mého mailu, přes který se to celé honí...
Uvedenou záměnu adres zkrátka nedocílím. I když bych rád a ostatně logika existence parametru From by tomu napovídala...

Tak zatím stále nevím. Asi by pomohlo vyjádření autora - to by ale pak nedělal nic jiného a už takhle si s tím seriálem dal opravdu hodně práce.

 
Odpovědět  +1 29. května 11:57
Avatar
Jan Jedlička:3. června 19:53

ahoj, proc pisi htmlspecialchars v horni casti nad formularem ?.myslim tuto cast: if ($hlaska) echo('<p>' . htmlspecialchar­s($hlaska) . '</p>'); chapu ze to ma zmenit spicate zavorky html kodu na neskodny text, ale pritom tuto promennou nemuze nejak zvenci ovlivnit. jsem zacatecnik, tak se omlouvam za tak hloupou otazku :-)

 
Odpovědět 3. června 19:53
Avatar
Jimmy Jack
Člen
Avatar
Jimmy Jack:Včera 10:22

Ahoj, předem moc díky za vaší práci. Naprosto pecková záležitost! A teď k dotazu, v odstavci Přesměrování se řaší, aby formulář po odeslání nezůstal vyplněný. Co by bylo špatně na následujícím kódu namísto přesměrování? Nebude to naopak rychlejší a méně náročné? Díky!

<?php
    $hlaska = '';
    $jmeno = '';
    $email = '';
    $zprava = '';
    if ($_POST) { // V poli _POST něco je, odeslal se formulář
            if (isset($_POST['jmeno']) && $_POST['jmeno'] && isset($_POST['email']) &&                  $_POST['email'] &&
            isset($_POST['zprava']) && $_POST['zprava'] && isset($_POST['rok']) && $_POST['rok'] == date('Y')) {
                // Sem přijde odeslání emailu
                $hlaska = 'Formulář je správně vyplněný.';
                $jmeno = '';
                $email = '';
                $zprava = '';
            }
            else {
                $hlaska = 'Formulář není správně vyplněný!';
                $jmeno = (isset($_POST['jmeno'])) ? $_POST['jmeno'] : '';
                $email = (isset($_POST['email'])) ? $_POST['email'] : '';
                $zprava = (isset($_POST['zprava'])) ? $_POST['zprava'] : '';
            }
    }
?>
 
Odpovědět Včera 10:22
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 10 zpráv z 132. Zobrazit vše