Návštěvní kniha v PHP bez použití databáze – bezpečnostní mechanismy

12. prosince 2000

V prvním díle tohoto článku jsme si ukázali, jak zapsat nové příspěvky na začátek, jak odstranit nebezpečné znaky, které by mohly narušit formátování stránky apod. Po uveřejnění článku mně přišla spousta dotazů. V zásadě se týkaly těchto dvou věcí: výpis příspěvků po 20 a zákaz zápisu do souboru. Skript na vypisování příspěvků po 20 byl přidán. Navíc byla přidána kontrola správnosti e-mailové adresy a kód, který zamezí opětovnému odeslání příspěvku po stisknutí Refresh.

Zákaz zapisování do souborů

Když jsem napsal poprvé aplikaci „Návštěvní kniha“, ani mě nenapadlo, že by někde mohl být zápis zakázán. Umístil jsem ji na server WebZdarma.cz – vše fungovalo – napsal jsem tedy článek. Ale ejhle, hned po jeho uveřejní jsem byl zaplaven dotazy, ve kterých se psalo: „Warning: fopen(„book.dat“,“w“) – Permission denied – toto se mi vypsalo po odeslání příspěvku, co s tím?“. V překladu tato věta znamená, že je zakázán zápis do souboru „book.dat“.

Ještě jednou jsem se podíval na WebZdarma.cz – vše fungovalo. Vytvořil jsem si tedy účet na Yo.cz a Host.sk. Vyzkoušel jsem tam svůj skript, ale nastala ona chyba – zápis do souboru zakázán. Teď jsem uvěřil, že zápis do souboru může být skutečně zakázán. Začal jsem tedy pátrat, jak to vyřešit. A našel jsem zcela prostou radu – změnit atributy souboru, tak aby byl povolen i zápis.

Na FTP komunikaci používám Windows Commander. Je v něm i funkce Změna atributů (např. IW FTPort nic takového nemá). Prázdný soubor „book.dat“ jsem nejprve nahrál na server, tam změnil atributy podle přiložené obrázku a spustil skript znovu. Na Yo.cz i Host.sk aplikace začala fungovat. Zkusil jsem to ještě na kamarádově serveru, kde byl zápis taktéž nejprve zakázán, a vše fungovalo rovněž bez problémů.

Změna atributů ve Windows Commanderu
Změna atributů ve Windows Commanderu

Kontrola správnosti e-mailové adresy

Na toto téma přišel sice pouze jeden dotaz, ale myslím, že je to myšlenka velice zajímavá, proto se ji budu věnovat. Zkontrolovat platnost e-mailové adresy lze dvěma způsoby. V PHP se vytvoří regulární výraz (maska), který zjišťuje zda zadaná e-mailová adresa odpovídá této masce. Regulární výrazy ještě nebyly na Intervalu probírány (článek mám již připraven – takže se brzy dočkáte), a tak jsem použil druhý, a to rozhodně elegantnější způsob – JavaScript. Obrovská výhoda je, že se nemusí požadavek odesílat na server, ale kontrola probíhá v prostředí klienta.

Zároveň budeme kontrolovat, zda byly zadány další povinné položky. JavaScriptu a konkrétně e-mailovým adresám se na Intervalu věnuje jiná sekce, takže zdrojový kód nebudu vůbec rozebírat.

<SCRIPT LANGUAGE="JavaScript"><!–
function zkontroluj(formular)
{
if (formular.jmeno.value=="")
{
alert("Vaše jméno (přezdívku) musíte vyplnit!");
formular.jmeno.focus();
return false;
}
else if (formular.email.value=="")
{
alert("Adresu elektronické pošty musíte vyplnit!");
formular.email.focus();
return false;
}
else if (formular.popis.value=="")
{
alert("Text zprávy musíte vyplnit!");
formular.popis.focus();
return false;
}
else if (window.RegExp)
{
re = new RegExp("^[^.]+(\.[^.]+)*@([^.]+[.])+[a-z]{2,3}$");
if (!re.test(formular.email.value))
{
alert("Zadaná adresa není správnou adresou elektronické pošty!");
formular.email.focus();
return false;
}
}
else
return true;
}
// –>
</SCRIPT>

Tím, že všechny údaje kontrolujeme pomocí JavaScriptu se o něco zjednodušil formulář. Pozor, abychom nezapomněli u formuláře uvést „onSubmit=“return zkontroluj(this)“. To způsobí, že po odeslání formuláře se provede výše uvedený skript.

<table border="0" cellspacing="0" cellpadding="0" align="center">
<form action="insert.php3" method="post" onSubmit="return zkontroluj(this)">
<tr><td width=120 class=povinne>
Jméno:
</td><td>
<input type="text" name="jmeno" size="30" maxlength="60" class="inputbook">
</td></tr>
<tr><td width=120 class=povinne>
E-mail:
</td><td>
<input type="text" name="email" value="@" size="30" maxlength="60" class="inputbook">
</td></tr>
<tr><td width=120 class=nepovinne>
Web (i s http://):
</td><td>
<input type="text" name="web" value="http://" size="30" maxlength="60" class="inputbook">
<input type="hidden" name="odeslano" value="ano">
</td></tr>
<tr><td valign=top width=120 class=povinne>
Text zprávy:
</td><td>
<textarea cols="29" rows="5" class=inputbook name="popis"></textarea>
</td></tr>
<tr><td width=120> </td><td>
<center><input type="submit" class=bluebutton value="Odeslat">   <input type="Reset" class=bluebutton value="Vymazat"></center>
</td></tr></form></table>

Výpis příspěvků po 20

Nejprve nastíním, jak budeme vše provádět. Podle počtu uložených příspěvků (jednomu příspěvku odpovídá jeden řádek v souboru „book.dat“ – v minulé verzi jsme ukládali na samostatný řádek každý prvek příspěvku – to musíme změnit) zjistíme kolik bude celkem stránek s příspěvky. Poté se vygenerují odkazy, ve kterých budeme předávat číslo stránky, která se má zobrazit.

Pomocí funkce "File()" načteme obsah souboru "book.dat" do pole. V prvku pole s indexem [0] bude 1. řádek, s indexem [1] 2. řádek, atd. Funkce "Count()" nám spočítá počet prvků pole, který vydělíme 20 a pomocí "Ceil" zaokrouhlíme nahoru (např. 2,4 -> 3; 1,1 -> 2), a tak získáme počet stran.

if (File_Exists ("book.dat")):
echo "<center><font class=cas>Zobrazit příspevky:<br><br>";
$prispevek = File("book.dat"); //načte obsah souboru do pole
$strana = Ceil(Count($prispevek)/20); //vypočítá kolik zaberou příspěvky stran (po 20)

Nyní pomocí cyklu vytvoříme odkazy na jednotlivé strany s příspěvky. Cyklus se nám provedete přesně tolikrát, kolik je stran. V odkazu budeme předávat číslo stránky, která se má zobrazit. Navíc podle čísla stránky spočítáme jaké odkazy budeme zobrazovat (např. 1-20, 21-40).

for ($x=1;$x<=$strana;$x++): //zobrazí tolik odkazů kolik je stran
echo "<a href=kniha.php3?idprispevek=$x>" . ($x*20-19) . "-" . $x*20 . "</a>  &nbsp"; //do každého odkazu přidá číslo strany a jaké příspěvky budou zobrazovány
endfor;
echo "</center>";
endif;

Z celé této části uděláme funkci, protože odkazy, budeme zobrazovat i v dolní části stránky. Bylo by zbytečné to psát dvakrát.

function Odkaz()
{
if (File_Exists ("book.dat")):
echo "<center><font class=cas>Zobrazit příspevky:<br><br>";
$prispevek = File("book.dat"); //načte obsah souboru do pole
$strana = Ceil(Count($prispevek)/20); //vypočítá kolik zaberou příspěvky stran (po 20)
for ($x=1;$x<=$strana;$x++): //zobrazí tolik odkazů kolik je stran
echo "<a href=kniha.php3?idprispevek=$x>" . ($x*20-19) . "-" . $x*20 . "</a>  &nbsp"; //do každého odkazu přidá číslo strany a jaké příspěvky budou zobrazovány
endfor;
echo "</center>";
endif;
}

V další části budeme vypisovat příspěvky podle hodnoty předané odkazem v proměnné „$idprispevek“. Pokud není tato proměnná zinicializována, dosadíme do ní hodnotu „1“ a budeme tak zobrazovat příspěvky „1-20“. Spočítáme si od kolikátého do kolikátého řádku se budou příspěvky zobrazovat. Např. pokud chceme zobrazovat příspěvky 21-40 (tj. stranu 2), vyjde nám „$pocatek=20“ a „$konec=39. Proč ne 21 a 40? Protože funkce „File()“ přiřazuje řádky od „0“ – o tom již ale byla řeč. Jednoduchým cyklem pak již jen zobrazíme příspěvky a na konec stránky přidáme odkazy pomocí námi definované funkce „Odkaz()“.

if (File_Exists ("book.dat")):
if (!IsSet($idprispevek)) $idprispevek=1;//je zinicializována proměnná id, pokud ne přiřadí standartní jedničku
$pocatek = $idprispevek*20-20;//podle $idprispevek spočítá od kolikátého
$konec = $idprispevek*20-1;//… do kolikáté příspěvku se bude zobrazovat
$prispevek = File("book.dat");//načte do pole obsah book.dat
for ($i=$pocatek;$i<=$konec;$i++):
echo $prispevek[$i];//vypíše příspěvky mezi $prispevek a $konec
endfor;
endif;
Odkaz();

Opakované odeslání příspěvku po stisknutí Refresh

Obrovskou slabinou minulé verze bylo opakované odeslání příspěvku po stisknutí Refresh. To se dá vyřešit třeba tak, že hlavní část skriptu (zápis do datového souboru) umístíme do souboru „insert.php3“. Do formuláře v „kniha.php3“ napíšeme jako hodnotu parametru ACTION u tagu FORM – „insert.php3“. Po odeslání příspěvku se tedy provedou všechny příkazy v souboru „insert.php3“ a pomocí „Meta-tagu Refresh“ se ihned vrátíme na „kniha.php3“.

Takto tedy bude vypadat soubor „insert.php3“.

<?
$name = "<table><tr><td class=jmeno>$jmeno</td></tr>";
$e_mail = "<tr><td class=odkaz><A HREF=mailto:$email>$email</A></td></tr>";
if ($web!="" && $web!="http://"):
$www = "<tr><td class=odkaz><A HREF=\"$web\">$web</A></td></tr>";
endif;
$cas = "<tr><td class=cas>" . Date("j. " . "m. " . "Y, " . "H:i:s") . "</td></tr>";
$tag = HTMLSpecialChars($popis);
$br = Str_Replace("\n"," <BR> ", $tag);
$zprava = "<tr><td class=text><br>$br</td></tr></table><HR color=\"#00008B\">\n";
$write = StripSlashes($name . $e_mail . $www . $cas . $zprava);
if (File_Exists ("book.dat")):
$fp = FOpen ("book.dat", "r");
$data = FRead ($fp, FileSize("book.dat"));
FClose($fp);
endif;
$fp = FOpen ("book.dat", "w");
FWrite ($fp, $write.$data);
FClose ($fp);
?>
<html><head>
<META HTTP-EQUIV="Refresh" CONTENT="0; URL=kniha.php3">
</head></html>

Oproti minulé verzi se liší tím, že každý příspěvek se ukládá na 1 řádek, a to do tabulky. Zdálo se mi, že si Netscape lépe poradí se styly v tabulkách. Protože nyní máme nastavenou v položce Web jako standardní hodnotu „http://“, musíme do podmínky přidat další část $web!=“http://“.

Pokud bychom v textu zprávy několikrát načali další řádek, funkce "NL2BR()" způsobí uložení textu zprávy také na několik řádků. To je ale nepřípustné, jestliže chceme zobrazovat příspěvky po 20. Použijeme místo toho funkci "Str_Replace("\n"," <BR> ", $tag);", která nahrazuje jeden řetězec druhým (v našem případě konec řádku \n tagem <BR>).

Celý další kód je totožný s tím minulým. Ještě k Meta-tagu Refr esh, první parametr udává za kolik sekund bude provedeno přesměrování, druhý na jakou stránku se přesměrujeme.

Menších změn doznal i soubor „styl.css“ kvůli lepší kompatibilitě s Netscape. Celou aplikaci si můžete pochopitelně stáhnout tady. Balík obsahuje soubory „kniha.php3, insert.php3, styl.css i prázdný book.dat“. Posledně jmenovaný soubor je ale nutný pouze v případě, pokud máte zakázáno zapisování do souboru, jinak se vytvoří automaticky. Ještě jednou opakuji pro ty, kteří mají zakázán zápis. Soubor „book.dat“ nahrajte na server a tam změňte atributy.

Příště bude řeč o regulárních výrazech. Ty využijeme i v dalším díle „Návštěvní kniha“, kde si povíme, jak odstranit příliš dlouhá slova, která způsobí protahování stránky (tabulky) a jak povolit některé tagy – „<b>, <i>, <u>“. Kromě tohoto využijeme regulární výrazy i k automatické detekci odkazů v Textu zprávy.

Starší komentáře ke článku

Pokud máte zájem o starší komentáře k tomuto článku, naleznete je zde.

Štítky: Články

Mohlo by vás také zajímat

Nejnovější

9 komentářů

  1. Katka

    Zář 7, 2009 v 1:21

    Dobrý den, jsem začátečník a v PHP se orientuji jen velmi málo. Stále se mi nedaří, aby se příspěvky zobrazovaly. Po vyplnění všech políček problikne stránka s již zobrazeným příspěvkem, ale vše se vrátí zpět do původního stavu s prázdnými políčky. http://www.tgkzlin.cz/kniha.php3
    Kde je chyba?

    Odpovědět
  2. rada

    Zář 30, 2009 v 1:54

    Pekny den,
    zacinam s praci v php, pro zacatek jsem si vytvoril jednodussi navstevni knihu. Resim nyni problem po odeslani zpravy-komentare. Zprava se uspesne odesle, zapise se jako nova do knihy. Pokud nasledne zvolim refresh F5, dojde k opetovnemu(nežádanému) vypisu teto zpravy. Pri programovem vyvolani refresh header(‚Refresh: 3;URL=http://www.mypage.php‘); vse probehne v poradku-zadne opetovne vypsani zpravy do knihy. Lze tedy zabranit znovu odeslani zpravy pri stisku F5?
    Dekuji za radu. R.

    Odpovědět
  3. thanatos

    Zář 30, 2009 v 9:56

    to: rada

    ano možné to je, nejjednodušší způsob je pří úspěšném zapsání komentáře přesměrovat stránku pomocí header na stránku s výpisem a je knihy (je jedno jestli to je ta samá co přidání komentáře do knihy zpravuje)

    Odpovědět
  4. rada

    Říj 1, 2009 v 19:47

    to: thanatos

    je to ok, diky za radu

    Odpovědět
  5. matohvk3

    Úno 17, 2010 v 18:20

    Ahoj … som začiatočník s prácou v php a skúšal som túto knihu návštev, pričom sa mi vyskytol problém … robím to teraz pre jeden klub a mám problém pri vypisovaní jednotlivých príspevkov … nechce mi ich ukázať … poradíte mi v čom môže byť problém? … môže byť problém aj v tom že je to php3 a server na ktorý to dávam zrejme php3 nepodporuje? … ak áno čo mám v tom prípade urobiť … pretože som už skúšal dať aj s php bez 3 ale vtedy mi vyskočí chyba s ktorou si absolútne neviem poradiť … pomôže mi niekto?

    Odpovědět
  6. mates

    Čvc 25, 2010 v 0:00

    Vse funguje, jen mi nefunguje to strankovani, testnul jsem asi 30 prispevku, ale kdyz kliknu na dalsi stranu, vzdy se objevi prvni strana – nevi nekdo co s tim ???

    Odpovědět
  7. mates

    Čvc 25, 2010 v 0:05

    Komu nefunguje zobrazovani prispevku pouzijte tento insert.php3

    <?
    $jmeno = $_POST['jmeno']; $email = $_POST['email']; $web = $_POST['web']; $popis = $_POST['popis'];
    $name = "$jmeno“;
    if ($web!=““ && $web!=“http://“):
    $www = „
    $web„;
    endif;
    $cas = „“ . Date(„j. “ . „m. “ . „Y, “ . „H:i:s“) . „“;
    $tag = HTMLSpecialChars($popis);
    $br = str_replace(„\n“,“ „, $tag);

    $popis = „$popis\n“;
    $write = StripSlashes($name . $e_mail . $www . $cas . $popis);

    if (File_Exists („book.dat“)):
    $fp = FOpen („book.dat“, „r“);
    $data = FRead ($fp, FileSize(„book.dat“));
    FClose($fp);
    endif;

    $fp = FOpen („book.dat“, „w“);
    FWrite ($fp, $write.$data);
    FClose ($fp);

    ?>

    Odpovědět
  8. Fisak

    Říj 26, 2011 v 10:33

    zdravím… mám dotaz… dá se toto využít i tak že když mam výpis z DB který chci aby se aktualizoval bez refreshu když se něco v db změní tak jestli jde tento script použít… Př. z db se mi vypíše „auto“ v db změním „auto“ na „autobus“ a na stránce aniž bych jí refreshoval by se mi změnilo „auto“ na „autobus“… jde to ?

    Odpovědět
  9. Jozef

    Dub 12, 2012 v 15:30

    Zdravim, tou opravenou insert.php3 dava odkazy resp. cely text komplet za sebou, je tam nejaka chyba. Myslite, ze to mozete opravit?

    Odpovědět

Napsat komentář: Jozef Zrušit odpověď na komentář

Vaše e-mailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *