9. díl - 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

V minulém dílu seriálu tutoriálů se základy PHP jsme naprogramovali jednoduchý emailový formulář. V dnešním dí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:

Předvyplnění formuláře v 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 seriálu, stylování se probírá v jiné sekci :) Kompletní formulář je ke stažení níže. Příště se podíváme na skládání stránek pomocí PHP.


 

Stáhnout

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

 

  Aktivity (3)

Článek pro vás napsal David Čápka
Avatar
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 se informační technologie naučil na Unicorn College - prestižní soukromé vysoké škole IT a ekonomie.

Jak se ti líbí článek?
Celkem (15 hlasů) :
4.533334.533334.533334.533334.53333


 


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

 

 

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

Avatar
Bebbana
Člen
Avatar
Bebbana:

Děkuji, pak jsem na to taky přišla. Taková hloupost :)

 
Odpovědět 19.9.2015 14:24
Avatar
patysta
Člen
Avatar
patysta:

Zdravím,
mám dotaz k upozornění "POZOR! Přesměrovat můžete pouze v případě, že jste ještě nevypsali žádné HTML.". Lze použít ten chybný zápis za pomoci příkazu ob_start("ob_gzhan­dler");? Chápu správně, jak funguje tento příkaz?

 
Odpovědět 27.11.2015 11:28
Avatar
kivdul
Člen
Avatar
kivdul:

ahoj
marně se snažím o .... budete-li mít nějaký tip jak na to, budu vděčný
díky Ludvík

mám index.php a v něm obyčejný <table> a v jednolivých <td> odkazy na podstránky/.... .php - to funguje

na podstránce mám <form a metodu=post> s <input name=10...> a pomoci header(location) se vracim zpět na index - taky funguje

na index.php mam:

<?php
        if($_GET['odpoved'])
        {
            $skore += $_GET['odpoved'];
        }
        echo ($skore);
        ?>

což funguje ale jen jednou :-( hodnoty ve $skore se mi nesčítají!

Jak mám napsat kod, aby se mi nově vypočítaná hodnota v $skore uchovala? aby se při každé načtení stránky nenulovala?

ještě jednou dík za radu(y)

 
Odpovědět 13.12.2015 20:08
Avatar
IT Man
Redaktor
Avatar
Odpovídá na kivdul
IT Man:

$_SESSION je tvé řešení. Platí 20 minut od poslední změny.

<?php
if (isset($_GET['odpoved']) && $_GET['odpoved'])
{
    if (isset($_SESSION['skore']) && $_SESSION['skore'])
        $_SESSION['skore'] += $_GET['odpoved'];
    else
        $_SESSION['skore'] = $_GET['odpoved'];
}
Odpovědět 13.12.2015 20:54
Když nevíš jak dál, podá ti ruku někdo, od koho by jsi to nečekal. A tu šanci musíš přijmout!
Avatar
kivdul
Člen
Avatar
Odpovídá na IT Man
kivdul:

díky

ta globální proměná $_SESSION[ ] je skvělá :-D

 
Odpovědět 19.12.2015 11:13
Avatar
Karel Šlaj
Člen
Avatar
Karel Šlaj:

Tak poprvé se mi při postupování dle vašich návodů vyskytl problém (y), na které jsem krátký. Využívám dvě textarea a snažím se je odeslat společně:

$sucess = mb_send_mail($adress, $subject, $_POST['message'], $_POST['strategy'], $head);

Tímhle způsobem mi začal chodit email z adresy mého hostingu namísto zadané a odeslalo se jen jedno textové pole.

$sucess = mb_send_mail($adress, $subject, $_POST['message' + 'strategy'],  $head);

Byl další neůspěšný pokus.
Hodně dlouho jsem hledal chyby ve zbytku kódu, ale nenašel. Dále mám problém ještě s tím, že přesměrování mě posílá na afort-crowfall.clan­web.eu/mailfor­m.php?sucess=y­es , což je neexistující stránka. Můj mailform se jmenuje recruiting.php
Jinak kdyby přece jenom byla chyba jinde, tak vkládám celý obsah souboru recruiting.php
http://www.itnetwork.cz/dev-lighter/770

Děkuji za případnou pomoc. Moc si vážím zdejších návodů!

 
Odpovědět 13. srpna 11:57
Avatar
seejay.15.3
Redaktor
Avatar
Odpovídá na Karel Šlaj
seejay.15.3:

Já to udělal takto, jedná se o script z register.php, myslím, že by ti to mohlo pomoct :)

if(mysql_affected_rows($link)==1)
                {
            $to = $_POST['email'];
            $subject = "Yourradio-registration info";

            $message = '<html xmlns="http://www.w3.org/1999/xhtml"><head><meta name="viewport" content="width=device-width" />
          <style>
            a{color:#2BA6CB;}
            .btn{text-decoration:none;color:#FFF;background-color:#666;padding:10px 16px;font-weight:bold;margin-right:10px;text-align:center;cursor:pointer;display:inline-block;}
            .btn.btn-primary{background:#1CD5B4;;  color:#fff;  border:none !important;  border:2px solid transparent !important;  font-family:"Source Sans Pro",Arial,sans-serif;  margin:0px auto;}
             .btn:hover,.btn:active,.btn:focus{background:#393e46 !important;  color:#fff;  outline:none !important;}
            p.callout{padding:15px;background-color:#ECF8FF;margin-bottom:15px;}
            .callout a{font-weight:bold;color:#2BA6CB;}
            table.social{background-color:#ebebeb;}
            .social .soc-btn{padding:3px 7px;font-size:12px;margin-bottom:10px;text-decoration:none;color:#FFF;font-weight:bold;display:block;text-align:center;} a.fb{background-color:#3B5998!important; }
            a.tw{background-color:#1daced!important; } a.gp{background-color:#DB4A39!important; } a.ms{background-color:#000!important; } .sidebar .soc-btn{display:block;width:100%;}
            table.head-wrap{width:100%;} .header.container table td.logo{padding:15px; } .header.container table td.label{padding:15px; padding-left:0px;} table.body-wrap{width:100%;}
            table.footer-wrap{width:100%;clear:both!important;} .footer-wrap .container td.content  p{border-top:1px solid rgb(215,215,215); padding-top:15px;} .footer-wrap .container td.content p{font-size:10px;font-weight:bold;}
            h1, h2, h3, h4, h5, h6{font-family:"HelveticaNeue-Light","Helvetica Neue Light","Helvetica Neue",Helvetica,Arial,"Lucida Grande",sans-serif; line-height:1.1; margin-bottom:15px; color:#000;} h1 small,h2 small,h3 small,h4 small,h5 small,h6 small{font-size:60%; color:#6f6f6f; line-height:0; text-transform:none; } h1{font-weight:200; font-size:44px;}
            h2{font-weight:200; font-size:37px;} h3{font-weight:500; font-size:27px;} h4{font-weight:500; font-size:23px;} h5{font-weight:900; font-size:17px;} h6{font-weight:900; font-size:14px; text-transform:uppercase; color:#444;} .collapse{margin:0!important; p,ul{margin-bottom:10px; font-weight:normal; font-size:14px; line-height:1.6;}
            p.lead{font-size:17px; } p.last{margin-bottom:0px;} ul li{margin-left:5px;list-style-position:inside;}
            ul.sidebar{background:#ebebeb;display:block;list-style-type:none;} ul.sidebar li{display:block; margin:0;} ul.sidebar li a{text-decoration:none;color:#666;padding:10px 16px; margin-right:10px; cursor:pointer;border-bottom:1px solid #777777;border-top:1px solid #FFFFFF;display:block;margin:0;}
            ul.sidebar li a.last{border-bottom-width:0px;}
            ul.sidebar li a h1, ul.sidebar li a h2, ul.sidebar li a h3, ul.sidebar li a h4, ul.sidebar li a h5, ul.sidebar li a h6, ul.sidebar li a p{margin-bottom:0!important;}
            .container{display:block!important;max-width:600px!important;margin:0 auto!important;clear:both!important;}
            .content{padding:15px;max-width:600px;margin:0 auto;display:block; }
            .content table{width:100%; }
            .column{width:300px;float:left;}
            .column tr td{padding:15px; }
            .column-wrap{padding:0!important; margin:0 auto; max-width:600px!important;}
            .column table{width:100%;}
            .social .column{width:280px;min-width:279px;float:left;} .clear{display:block; clear:both; }
            @media only screen and (max-width:600px){a[class="btn"]{display:block!important; margin-bottom:10px!important; background-image:none!important; margin-right:0!important;} div[class="column"]{width:auto!important; float:none!important;}table.social div[class="column"]{width:auto!important;}}
            </style>
            <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /><title>Yourradio registration</title>
            </head>
            <body bgcolor="#FFFFFF"><table class="head-wrap" bgcolor="#999999"><tr><td></td><td class="header container" >
            <div class="content"><table bgcolor="#999999"><tr><td>
            <a href="http://radiodb.tk/index.php"><img src="http://radiodb.tk/images/logo.png" width="50px" heigth="50px"/></td></tr></table></div></td><td></td></tr></table><!-- /HEADER -->
            <!-- BODY --><table class="body-wrap"><tr><td></td><td class="container" bgcolor="#FFFFFF"><div class="content"><table><tr><td><h3>Hi '.$_POST["f_name"].' '.$_POST["l_name"].'</h3>
            <p class="lead">Welcome to Yourradio!</p><p>In this email you can find some important info about your registration.</p><p>DON´T DELETE THIS EMAIL!</p><p><b>Your registration:</b></p>
            <table width="100%"><tr><td><b>Username:</b></td><td>'.$_POST["username"].'</td></tr><tr><td><b>Password:</b></td><td>'.$_POST["password"].'</td></tr></table>
            <p class="callout"><a href="http://radiodb.tk/form.php" class="btn btn-primary">Login to your account &raquo;</a></p>
            <table class="social" width="100%"><tr><td><table align="left" class="column"><tr><td><h5 class="">Contact Info:</h5>Email: <strong><a href="emailto:admin@radiodb.tk">admin@radiodb.tk</a></strong></p></td></tr></table><span class="clear"></span>       </td></tr></table>
            </td></tr></table></div></td><td></td></tr></table><table class="footer-wrap"><tr><td></td><td class="container"><div class="content"></div></td><td></td></tr></table></body></html>';

            $headers = "MIME-Version: 1.0" . "\r\n";
            $headers .= "Content-type:text/html;charset=UTF-8" . "\r\n";
            $headers .= 'From: donotreply@radiodb.tk>' . "\r\n";
if(mail($to,$subject,$message,$headers)):
    $successMsg = 'Email has sent successfully.';
else:
    $errorMsg = 'Email sending fail.';
endif;

EDIT:

if(mail($to,$subject,$message,$headers)):
    $successMsg = 'Email has sent successfully.';
else:
    $errorMsg = 'Email sending fail.';
endif;
Editováno 13. srpna 12:21
 
Odpovědět  +1 13. srpna 12:19
Avatar
mkub
Redaktor
Avatar
Odpovídá na Karel Šlaj
mkub:

najprv si musis spojit $_POST['message'] a $_POST['strategy'], a nasledne to posles ako jednu premennu, totiz skustocna syntax tej funkcie je:

bool mb_send_mail ( string $to , string $subject , string $message [, string $additional_headers = NULL [, string $additional_parameter = NULL ]] )

kde:
$to - znamena prijemcu
$subject - znamena policko "Subject"
$message - je samotna sprava
$additional_headers - rozsirujuce hlavicky (nepovinna, standardne nastavena na "NULL")
$additional_pa­ramter - doplnujuce parametre (nepovinna, standardne nastavena na "NULL")
viac o funkcii: http://php.net/…end-mail.php

cize asi nejako takto:

...
$sucess = mb_send_mail($adress, $subject, $_POST['message'] . $_POST['strategy'],  $head);
...

resp. takto:

...
$msg = $_POST['message'] . $_POST['strategy'];
$sucess = mb_send_mail($adress, $subject, $msg, $head);
...

totiz vyraz "+" nesluzi na spajanie retazcov, ale na scitavanie cisiel

 
Odpovědět  +1 13. srpna 13:32
Avatar
Karel Šlaj
Člen
Avatar
Odpovídá na mkub
Karel Šlaj:

Děkuju tady oboum redaktorům za výpomoc. Už to funguje tak jak jsem si představoval (*_*)

 
Odpovědět  +1 16. srpna 9:49
Avatar
mkub
Redaktor
Avatar
Odpovídá na Karel Šlaj
mkub:

ak ti to pomohlo, oznac to ako riesenie

 
Odpovědět 16. srpna 10:12
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 87. Zobrazit vše