Utolsó frissítés: 2012.02.13.
Facebook Twitter iWiW MySpace Digg Delicious Google bookmarks Startlap Windows Live

Vendégkönyv

Ebben a leckében egy vendégkönyvet fogunk elkészíteni. A "vendégkönyv" alatt egy olyan rendszert értek, ami egy szöveget kér be az oldal látogatójától egy űrlap segítségével (meg általában a nevét is, esetleg néhány egyéb adatot), majd az így bekért szövegeket az oldalon megjeleníti. Az általunk beírt szövegnek is az űrlap elküldése után meg kell jelennie. Ilyen rendszereket hívnak néha üzenőfalnak, fórumnak vagy blognak is, a különbség általában csak az összetettségben és a megjelenésében van, meg abban, hogy mire akarjuk használni. A megvalósításuk alapja azonban ugyanaz. Az ezen az oldalon található fórum is egy ilyen rendszer, csak van még pár fícsör beleépítve.

Alapelv

Mielőtt nekiesnénk a kódolásnak, nem árt tisztázni, hogy egyáltalán hogy működik egy ilyen rendszer. Az egyszerűség kedvéért most egy olyan rendszert képzeljünk el, ami egy szöveget (valami rövid üzenetet) kér be. Az addig világos, hogy van egy űrlap egy szövegmezővel meg egy submit gombbal, amivel el lehet küldeni. Aztán a feldolgozó oldalnak (ez lehet ugyanaz az oldal, ahol az űrlap van, de nem feltétlenül) az a dolga, hogy kiírja az üzenetemet. Nem nagy dolog, a $_POST tömbben van amit beírtam. Igen ám, csakhogy ebben a tömbben az a szöveg van, amit én az imént elküldtem az űrlappal. Ha már tegnap is randalíroztam a vendégkönyvben, annak így nyomát se fogom látni. Ráadásul ha egy másik felhasználó eközben megnézi a vendégkönyvet, semmit se fog látni abból, hogy én hogyan osztom itt az észt, mivel nála nem lesz $_POST változó, hiszen az űrlapot ő nem küldte el. Ahhoz, hogy az okosságok amiket beírok holnap is megjelenjenek az oldalon (és ami szintén fontos, hogy más is lássa), az elküldött karakterláncokat el kell tárolni valahol. Pontosan erre valók a szöveges állományok. Tehát az alapelv a következő:

  1. amikor meg kell jeleníteni az eddig a vendégkönyvbe írt szövegeket, beolvassuk a szöveges fájlt, és kiírjuk a tartalmát valamilyen formában,
  2. ha pedig elküldik az űrlapot, a szöveges fájlba írjuk az elküldött szöveget

Az egyszerűség kedvéért az utóbbi esetben a szöveges fájl végéhez fűzzük az elküldött szöveget.

Az űrlap

Egy vendégkönyvben (ha nincs regisztrációhoz/bejelentkezéshez kötve), általában nem csak egy szöveget (továbbiakban nevezzük hozzászólásnak) szokás bekérni, hanem pl a nevet is. Néha e-mail címet vagy egyéb adatokat is meg lehet adni, de a megvalósítás lényegén nem változtat, így most a vendégkönyvünkben egy nevet és a hozzászólást fogjuk bekérni. Úgy fog működni, hogy az oldal tetején lesznek kilistázva a hozzászólások, és a lista alatt lesz az űrlap. Az űrlap így néz ki:

	<form method="post" action="insert.php">
		Név:
		<input type="text" name="name" value="" />
		<br /><br />
		Hozzászólás:
		<textarea name="comment"></textarea>
		<br /><br />
		<input type="submit" name="send" value="Küldés" />
	</form>

A feldolgozó kód egy insert.php fájlban lesz. Lehetne ugyanitt is (az űrlapot tartalmazó legyen guestbook.php), de egyszerűbb, ha a két műveletet (olvasás és írás) különválasztjuk. Látható, hogy 3 változó fog létrejönni az űrlap elküldése után:
$_POST["name"], $_POST["comment"], $_POST["send"]
A harmadikat használjuk annak eldöntésére, hogy az űrlap el lett-e küldve.

A tárolási mód

Mielőtt még megírnánk az insert.php-t, el kell döntentünk, hogyan fogjuk tárolni az adatokat a szöveges fájlban. Legyen a neve mondjuk data.txt. Hogy kézzel is bele tudjunk nyúlni, tároljuk külön sorokban az egyes hozzászólásokat! Egy hozzászóláshoz tartozik még egy név is, ezt is ebben a sorban kell tárolnunk. A hozzászólások listázásánál általában meg szokták jeleníteni, hogy mikor írták be az okosságot, így a hozzászólás mellett még egy dátumnak is szerepelnie kell. Az egyszerűség kedvéért tároljuk el a time() függvény által visszaadott értéket, ez egy nagy egész szám, és a date() függvénnyel tetszőleges formában megjeleníthetjük a listázásnál. A time() álatal visszaadott értéket (az 1970 óta eltelt másodpercek száma) egyébként a szakmában timestampnek szokták nevezni (magyarul időbélyeg), amit csak azért mondtam el, hogy tudjak valahogy hivatkozni rá.
A hozzászólásokat tehát újsor-karakterek fogják elválasztani, de kell egy másik speciális karakter a név-hozzászólás-timestamp adatok elválasztására. Legyen ez most különösebb magyarázkodás nélkül a | karakter (függőleges vonal, tudomásom szerint pipe a neve, általában az AltGr+W kombinációval lehet előállítani). A feldolgozás részben visszatérünk erre.
Egy tipikus data.txt ezek után így fest:

Hörb|1321556002|Megnyílt a vendégkönyv. Aztán csak ésszel!
Pistike|1321556780|Csákány! Jöttem osztani az észt
dfgdf|1321557012|milyen fos ez az oldal. nem is tudom hogy kerültem ide.
Robot|1321557302|Viagra Soft -34% discount at http://viagra.com!!!!!! GO NOW!
Pistike|1321558000|Éppen most nyomtam agyon egy levéltetűt!
Pistike|1321558004|Ja bocsesz, ezt twitterre akartam kiírni...
Hörb|1321558412|Asszem ennyi ösztönködés elég is volt! Vendégkönyv lezárva!

Mivel az újsor-karaktert használjuk a hozzászólások adatainak elválasztására, könnyen áttekinthetőek az adatok. Például könnyebben ki tudjuk törölni a robot által beírt üzenetet, de sokminden miatt szükség lehet arra, hogy bele tudjunk nyúlni. Például egy üzenőfalat csináltunk meg ilyen módon, ami 150 pixel széles, de valaki egy hosszú linket másolt bele, ami szétnyomja az üzenőfalat. Ilyenkor lecserélhetjük mondjuk HTML kódra a linket.
Emlékezzünk vissza az Űrlapok 5. leckében az újsor-karakterrel kapcsolatban leírtakra. Ha Windows alatt akarjuk szerkeszteni ezt a fájlt, az újsor-karakter a "\r\n" karakterlánc lesz! Beolvasásnál e mentén kell szétvagdosni, és új hozzászólás beszúrásakor is ilyeneket rakjunk a sor végére!

A beolvasás

A guestbook.php-ben, az űrlap kódja felett szeretnénk kilistázni ezt a sok értelmes hozzászólást. A hozzászólásokat be kell raknunk egy PHP tömbbe, azután úgy jeleníthetjük meg őket, ahogy akarjuk. Mivel az adatszerkezet egy táblázat, egy kétdimenziós tömbbe célszerű berakni őket. Legyenek a sorok a tömb egyes elemei, amik 3-elemű asszociatív tömbök lesznek. A 3 elem a 3 oszlopot fogja jelenteni. Tehát valami ilyesmi kellene:

Array(
    [0] => Array(
        [name] => Hörb
        [timestamp] => 1321556002
        [comment] => Megnyílt a vendégkönyv. Aztán csak ésszel!
    )
    [1] => Array(
        [name] => Pistike
        [timestamp] => 1321556780
        [comment] => Csákány! Jöttem osztani az észt
    )
    ...
)

Egy ilyen tömböt könnyen kaphatunk. Olvassuk be a fájlt, majd a kapott hosszú karakterláncot daraboljuk fel:

	// beolvasás
	$file = fopen("data.txt", "r");
	$content = fread($file, filesize("data.txt"));
	fclose($file);

	// darabolás
	$data = array();
	$rows = explode("\r\n", $content);
	foreach ($rows as $n => $row){
		$cells = explode("|", $row);
		$data[$n]["name"] = $cells[0];
		$data[$n]["timestamp"] = $cells[1];
		$data[$n]["comment"] = $cells[2];
	}

A beolvasó rész mostanra remélhetőleg nem túl izgalmas, a $content karakterláncba kerül a fájl tartalma. Ezután ezt az újsor-karakterek (Windows-os újsor) mentén feldaraboljuk és a $rows tömbbe kerülnek a sorok. Ezen egy ciklussal végiglépegetünk, majd a | karakter mentén darabolva kapunk egy 3-elemű tömböt, ennek az elemeit berakosgatjuk a $data tömbbe. Ebbe a tömbbe bekerült az adat a kívánt formában.
Itt nem kell különösebb ellenőrzéseket végeznünk, a beszúrást végző kód (insert.php) feladata annak biztosítása, hpgy a data.txt tartalma megfelelő formátumú legyen!

Mielőtt még saját vállunkat megveregetve hátradőlnénk, ki kell listázni a kapott kétdimenziós tömböt. Először el kell készítenünk a HTML-t, vagyis azt, hogyan nézzen ki egy hozzászólás. Vegyük például az első hozzászólást, és írjuk meg hozzá a HTML kódot, amiben a név, dátum és a hozzászólás szerepel. Például legyen ilyen:

	<table border="0">
		<tr>
			<td>Hörb</td>
			<td align="right">
				2011.12.27 14:00
			</td>
		</tr>
		<tr>
			<td colspan="2">
				Megnyílt a vendégkönyv. Aztán csak ésszel!
			</td>
		</tr>
		<tr>
			<td colspan="2"><hr /></td>
		</tr>
		...
	</table>

Tehát itt az az elgondolás, hogy az egyes hozzászólások egy táblázat 3-soros blokkjaiban jelennek meg. Aki jártasabb a CSS-ben, az már menekül is az oldalról, de a kedvükért nézzük meg a modernebb változatát a HTML kódnak:

	<div id="messagelist">
		<div class="item">
			<div class="header">
				<strong>Hörb</strong>
				<span class="date">
					2011.12.27 14:00
				</span>
			</div>
			<p>
				Megnyílt a vendégkönyv. Aztán csak ésszel!
			</p>
		</div>
		...
	</div>

Ehhez a tömörebb kódhoz társítva egy megfelelő CSS-kódot ugyanolyan (sőt akár szebb) megjelenítést érhetünk el, mint a fenti táblázattal. Mióta a CSS általánosan támogatottá vált a böngészők körében, a HTML-CSS programozók egyre ritkábban használnak táblázatokat az oldal formázása céljából. Egyrészt az általános elemekkel (div, span) írt HTML kód tömörebb, rugalmasabb (könnyebben átalakítható a megjelenítés), másrészt a táblázat elvileg nem erre való, hanem adatok megjelenítésére. Ebbe most nem megyek bele mélyebben, mivel ez nem HTML és nem is CSS tanfolyam, szóval folytassuk a PHP írását. A lecke végén letölthető a teljes vendégkönyv kódja, ott van egy CSS is, ami beformázza ezt a div-es HTML kódot.

Akkor válasszuk a div-es kódot, és töltsük meg a fájlból kiolvasott adatokkal. Jelenítsük meg az összes hozzászólást abban a sorrendben, ahogy a fájlban is szerepel! Ez az alábbi módon fog kinézni:

	<div id="messagelist">
		<?php foreach ($data as $row){ ?>
			<div class="item">
				<div class="header">
					<strong><?php print $row["name"]; ?></strong>
					<span class="date">
						<?php print date("Y.m.d H:i", $row["timestamp"]); ?>
					</span>
				</div>
				<p>
					<?php print $row["comment"]; ?>
				</p>
			</div>
		<?php } ?>
	</div>

Egy foreach ciklussal járjuk be a $data tömböt, és kiírjuk a benne lévő adatokat. Egy hozzászólást egy <div class="item"> elem jelenít meg, a hozzá tartozó névvel és dátummal együtt. A fenti listázás a legegyszerűbb eset. Tegyük fel, hogy azt szeretnénk, ha a legújabb hozzászólás lenne legfelül (vagyis időrendben visszafelé listázódna), és legfeljebb csak az utolsó 10 hozzászólás jelenne meg. A txt fájlban időrendi sorrendben vannak a hozzászólások, mivel a következőt mindig hozzáfűzzük a fájl végéhez, így nem kell rendeznünk a tömböt, elegendő ha visszafelé járjuk be. Erre a foreach szerkezet nem alkalmas, egy for ciklussal tudjuk a legegyszerűbben megtenni.
Az alapszerkezet:

	$num = count($data);
	for ($n = $num - 1; $n >= 0; $n--){
		// hozzászólás megjelenítése (most a $data[$n] elemben vannak az adatok)
	}

Arról is gondoskodnunk kell, hogy ha már 10 hozzászólást kiírtunk, lépjünk ki a ciklusból. Ezt megtehetjük a ciklusmagban egy break utasítással, de egyszerűbb, ha kiegészítjük a ciklusfeltételt. Ennek megfelelően a teljes program:

	<div id="messagelist">
		<?php
		$num = count($data);
		for ($n = $num - 1; $n >= 0 && $num - $n <= 10; $n--){
		?>
			<div class="item">
				<div class="header">
					<strong><?php print $data[$n]["name"]; ?></strong>
					<span class="date">
						<?php print date("Y.m.d H:i", $data[$n]["timestamp"]); ?>
					</span>
				</div>
				<p>
					<?php print $data[$n]["comment"]; ?>
				</p>
			</div>
		<?php
		}
		?>
	</div>

Annyi a változás a foreach-es kódhoz képest, hogy a ciklusmagban nem egy $row változóban van egy hozzászóláshoz tartozó tömb (ilyen változó itt nincs), hanem a $data[$n]-ben. A ciklusfeltétel második része gondoskodik róla, hogy 10-nél több hozzászólás ne íródjon ki. Tulajdonképpen kész is van a listázás. Akkor nézzük a teljes fájl (guestbook.php) szerkezetét:

	<?php
		// fájl beolvasás
		// darabolás ($data tömb előállítása)
	?>
	<html>
		<head>
			<title>Vendégkönyv</title>
		</head>
		<body>
			<!-- listázás -->
			<!-- űrlap HTML-kódja -->
		</body>
	</html>

Érdemes követni azt a struktúrát, amit még az Alapok 20. leckében javasoltam, vagyis hogy a dinamikusan kiírandó adatokat még a nyitó <html> teg előtt állítjuk elő egy PHP blokkban, majd a HTML kód megfelelő helyén írjuk ki őket.

Az űrlap feldolgozása

A listázás kész, jöhet az insert.php fájl megírása, aminek az a dolga, hogy a kapott $_POST tömbben lévő adatokat hozzáfűzze a txt fájlhoz. Ehhez nem kell sok erőfeszítés, kapunk 3 változót:
$_POST["name"], $_POST["comment"], $_POST["send"]
Az űrlap elküldését is megvizsgáljuk a $_POST["send"] segítségével. Erre azért van szükség, hogy ha valaki az űrlap elküldése nélkül nyitja meg ezt a fájlt, akkor ne módosuljon a txt fájl. Az űrlap által elküldött adatokat a txt formátumának megfelelően hozzá kell fűzni a fájlhoz:

	if (isset($_POST["send"])){
		$name = $_POST["name"];
		$comment = $_POST["comment"];
		$file = fopen("data.txt", "a");
		fwrite($file, "\r\n".$name."|".time()."|".$comment);
		fclose($file);
	}

Az utolsó sor után nincs újsor-karakter, mivel akkor beolvasásánál az újsor menti darabolásnál az utolsó tömbelem üres lenne. Így a hozzáfűzendő karakterlánc elejére kell egy újsor-karakter (Windows-os, "\r\n"), majd utána jöhet a három adat.
Az űrlap elküldése után lefut ez a fájl, beíródnak az adatok a txt-be. Mi meg nézegethetjük az insert.php által kiírt tartalmat, ami jelen esetben a nagy semmi, kapunk egy üres, fehér lapot. A legjobb az lenne, ha lefutás után visszajutnánk a guestbook.php oldalra, ahol lefut a listázás, és már láthatjuk is a legújabb okosságot, amit beírtunk. Erre használható PHP-ben a header() függvény.

header()

A függvénynek egy karakterlánc paramétere van, visszatérési értéke nincs. Ezzel a függvénnyel sokmindent meg lehet csinálni, most csak a fenti probléma megoldására használjuk:

	header("Location: URL");

Az URL helyére egy teljes URL-t kell beírni, vagyis http://domain/... alakban kell megadni. A böngészőben ekkor megnyílik a beírt URL. Ezt a műveletet hívják átirányításnak. Ha például erre az oldalra (amit most látsz) akarod átirányítani a látogatót, akkor:

	header("Location: http://php.sikerweb.hu/phptanfolyam/urlapok/8-vendegkonyv");

A vendégkönyv esetén, ha localhost-on dolgozol, akkor valami ilyesmit kell megadni:

	header("Location: http://localhost/vendegkonyv/guestbook.php");

Érdemes ezt az if ágon kívülre rakni (természetesen utána), hogy akkor is visszajusson a listázáshoz, ha űrlapelküldés nélkül nyitotta meg a fájlt.

Egy fontos dolgot meg kell jegyezni ezzel a függvénnyel kapcsolatban. A függvény hívása előtt nem lehet semmilyen művelet, ami a kimenetre küld valamit (szöveget). Vagyis nem lehet se print vagy echo függvényhívás, se HTML kód. Hogy HTML kód nem lehet, azt jelenti, hogy a PHP blokk előtt nem lehet semmi, még egy szóköz, vagy újsor-karakter se. Lehetséges, hogy egyes szervereken működik így is, de ha feltoljuk a netre a weblapunkat, ne lepődjünk meg, ha egy ilyen hibaüzenetet tolnak az arcunkba:

Warning: Cannot modify header information - headers already sent by (output started at D:\wamp\www\vendegkonyv\insert.php:1) in D:\wamp\www\vendegkonyv\insert.php on line 8

Ez a hibaüzenet akkor jön, ha egy header() vagy valami hasonló tulajdonságú függvény hívása előtt (itt a 8. sorban hívtuk) a kimenetre lett küldve valami (itt az 1. sorban). Ehhez hasonló érdekes függvénnyel még az Űrlapok 10. leckében fogunk találkozni, ott majd elmagyarázom mi annak az oka, hogy nem lehet előtte kimenet.

Na akkor végeztünk is. A fenti kód után berakjuk ezt a header() hívást, és gyönyörködhetünk a vendégkönyvünkben. Most fel szeretnénk tolni a netre az egyik oldalunkra. Nincs más dolgunk, mint felrakni valahova az elkészített 3 fájlt: guestbook.php, insert.php és data.txt. Berakhatjuk mondjuk egy vendegkonyv nevű könyvtárba. Ezután, ahol meg akarjuk jeleníteni a listázást (alatta az űrlappal), ott egyszerűen beszújuk ezt:

	include("vendegkonyv/guestbook.php");

Természetesen a megfelelő relatív (vagy akár abszolút) elérési út beírásával. A vigyorgásunk egészen addig fog tartani, amíg nem érkezik egy magát hackernek képzelő gyerek a weboldalunkra, aki azért hacker, mert akkora tudás birtokában van, hogy már ismeri valamennyire a HTML nyelvet. Ez mit csinál? Azt veszi észre, hogy amit beír a vendégkönyvbe, az változatlan formában megjelenik az oldalon. És akkor beszúr szépen egy jópofa HTML teget, amit a múltkor ismert meg, mondjuk a <hr> teget (ilyen laza, lezáratlan formában persze). A txt-be ez bekerül, a listázásnál meg változatlan formában kiíródik. A böngésző pedig ezen a helyen egy vízszintes vonalat fog megjeleníteni. De nem ez a legrosszabb, ami megtörténhet. Ha például egy <div> nyitó teget szúr be, akkor az adott listaelem, amit egy div-ben jelenik meg, nem lesz lezárva, és innentől kezdve jó eséllyel szanaszét esik az oldalunk.
A HTML tegek ellen tehát védekezni kell valahogy. Az insert.php-vel még egyáltalán nem végeztünk, bele kell tennünk egy részt, ami a bemenő adatok szűréséről gondoskodik. A kacsacsőröket valamire le kell cserélnünk. Ne legyünk igénytelenek, jelenítsük meg a hackerünk által beírt kódot változatlanul! Lecseréljük a kacsacsőröket &lt; és &gt; karakterláncokra (HTML entitásokra). Ha ez esetleg új, nem kell megijedni, ez valami olyasmi mint a szóközt helyettesítő &nbsp; kód (ld. Alapok 7. lecke). Erre a már korábban megismert str_replace() függvényt használhatjuk:

	$comment = $_POST["comment"];
	$comment = str_replace("<", "&lt;", $comment);
	$comment = str_replace(">", "&gt;", $comment);

Ezt persze meg kell tennünk a $_POST["name"] változóval is, mert oda is beírhat akármit. Valójában elég lenne csak a nyitó kacsacsőröket lecserélni, de a biztonság kedvéért cseréljük le a csukókat is. A flótáskodásra nehéz felkészülni, akár beírhat egy HTML entitást is, például a "&lt;" karakterláncot. Ez egy kacsacsőr formájában jelenne meg, de mi változatlan formában szeretnénk megjeleníteni. Ezt úgy érhetjük el, ha a & jelet lecseréljük az ennek megfelelő entitásra (&amp;), de még a kacsacsőrök lecserélése előtt:

	$comment = str_replace("&", "&amp;", $comment);

Azért kell a kacsacsőrök előtt lecserélni, mert különben a már lecserélt &lt; alakú entitásokat is módosítaná. Még mindig van két karakter, amivel keresztbe tehet nekünk a hackerkedő gyerek (sőt, akárki más). Az a két karakter, amit elválasztó karakterként használunk, a | (pipe) és a \r\n alakú újsor-karakter. Az újsor-karaktert lecseréljük <br /> tegre (így legalább a hozzászólásban is sortörésként jelenik meg), a pipe-ot meg a neki megfelelő entitásra (&#124; vagy &VerticalLine;):

	$comment = str_replace("\r\n", "<br />", $comment);
	$comment = str_replace("|", "&#124;", $comment);

Ezeket is a & jel lecserélése után kell elintézni. Ez az 5 sor teljes védelmet nyújt a HTML kódok és az elválasztó karakterek használata ellen, minden pontosan úgy fog megjelenni a listázásnál, ahogy a felhasználó beírta. Ebből az is látható, hogy miért választottam a | jelet elválasztó karakternek. Olyat ugyanis nem lehet választani, ami a HTML entitások kódjában van, mivel erre cseréljük a problémás karaktereket. Ez a három jel pedig:
& # ;
Ezeket nem használhatjuk elválasztó karakternek, bármilyen más írásjelet igen, de minden esetben gondoskodni kell a megfelelő entitásra történő cseréléséről abban a szövegben, ami a felhasználótól jön!
Már csak az a kérdés, hogyan határozhatom meg egy írásjel HTML entitását (vagyis az őt megjelenítő kódot). A legegyszerűbb, ha beírjuk a keresőbe hogy "HTML entity" és akkor valószínű megtaláljuk valamelyik táblázatban az általunk választott írásjelet, de mutatok egy általánosan használható módszert. A Windows Start menüjében keressük meg a Karaktertábla nevű programot (elvileg Programok/Kellékek/Rendszereszközök/Karaktertábla). Ebben keressük meg az általunk választott karaktert. Például itt a képen megtaláltam a pipe-ot:

karaktertábla

Ha rákattintunk, akkor a bal alsó sarokban látható: U+007C, amiből a 007C karakter Unicode kódja 16-os számrendszerben. Ezt át kell konvertálnunk 10-es számrendszerbe (mondjuk a Windows Számológép nevű programjával), ami jelen esetben a 124. Ebből pedig úgy képezhető az entitás, hogy a számot &# és ; közé írjuk: &#124;
Ezzel a módszerrel bármelyik, karaktertáblában talált jelet megjeleníthetjük a weboldalunkon, olyat is, amit semmilyen billentyűzettel nem lehet közvetlenül bevinni.
Még egy dologra érdemes felkészülni. Ne engedjük, hogy üres üzeneteket küldözgessenek! Sőt, olyat se, amiben csak szóközök vagy újsor-karakterek vannak (vagyis nem látható karakterek). Utóbbi kiszűrésére jól használható a trim() függvény.

trim()

Ez a függvény a paramétereként kapott karakterlánc elejéről és végéről leszedi az üreshely-karaktereket (szóköz, újsor, tabulátor és még pár nem látható karakter). Visszatérési értéke a így előállított karakterlánc.
Az a dolgunk tehát, hogy a $name és $comment változóknak a $_POST tömb "trim-melt" értékeit adjuk. Ezek után azt ellenőrizzük, hogy a $comment változóban maradt-e valami (pl. az strlen() függvénnyel). Csak akkor végezzük el a karakterek lecserélését és a fájlba írást, ha nem üres.

Akkor nézzük is meg, hogyan néz ki ezek után az insert.php fájlunk. Használjuk fel az előző leckében tanult módszert az str_replace() függvény esetében:

	if (isset($_POST["send"])){
		$name = trim($_POST["name"]);
		$comment = trim($_POST["comment"]);
		
		if (strlen($comment) > 0){
			// karakterek lecserélése
			$chars    = array(    "&",    "<",    ">",   "\r\n",      "|");
			$entities = array("&amp;", "&lt;", "&gt;", "<br />", "&#124;");
			$name = str_replace($chars, $entities, $name);
			$comment = str_replace($chars, $entities, $comment);
			
			// fájlba írás
			$file = fopen("data.txt", "a");
			fwrite($file, "\r\n".$name."|".time()."|".$comment);
			fclose($file);
		}
	}
	header("Location: http://localhost/vendegkonyv/guestbook.php");

A $chars tömbben lévő karakterláncok lecserélődnek az $entites tömbben megadottakra, méghozzá a megfelelő sorrendben.

Hordozhatóság

Még mindig nem lennénk készen? A vendégkönyv két fő funkciója (listázás, fájlba írás) már működik. Maga a vendégkönyv is működik, de jó lenne úgy megírni, hogy a már említett include() függvénnyel a listázás és űrlapmegjelenítés (vagyis a guestbook.php) bárhova beszúrható legyen! Az "inklúdolás" önmagában nem lesz elég, mert 1-2 elérési utat (pl. a header()-ben szereplő url-t) majd át kell írni, de ha ezeket egy helyre gyűjtjük, könnyen hordozhatóvá válik a programunk.
Sok esetben a hordozhatóság elérése a programozás legnehezebb része, de nagyban megkönnyíti a műveletet, ha olvasható és áttekinthető, a PHP és HTML részeket a lehető legkevésbé keverő kódot írunk. Jelen esetben ezt amennyire a tanfolyam során szerzett ismereteink megengedték, megtettük.
A hordozhatóságot akkor értük el, ha a weboldalunkba, ahova a listázást és az űrlapot el akarjuk helyezni, ennyit kell csak írnunk:

	<?php
		// változók létrehozása, amikben az elérési utakat adjuk meg
		// ...
		include("vendegkonyv/guestbook.php");
	?>

...és már hasít is a vendégkönyv! Legyen ez a fájl az index.php, hogy hivatkozhassunk rá. Ide építjük be a listázást, alatta az űrlappal. A három fájlt, amiből a vendégkönyv áll (guestbook.php, insert.php, data.txt) beraktuk a vendegkonyv könyvtárba. A fenti fájlban a guestbook.php behúzása előtt megadott változókat fogjuk használni a vendégkönyv kódjában. Olyan adatokat (elérési utak, url-ek) kell itt összeszedni, ami a vendégkönyv áthelyezésekor megváltozhatnak. Végignézve a kódot, az alábbi három helyet szedhetjük össze:
- az insert.php-ben a header() függvényben megadott url, ahova az új üzenet beszúrása után átirányít minket a vendégkönyv
- a guestbook.php-ben a szöveges fájl elérési útja az index.php-hez képest
- a guestbook.php-ben az űrlap action attribútuma, ami az insert.php elérési útja az index.php-hez képest
A guestbook.php elérési útjait azért az index.php-hez képest kell megadni, mert ide lett behúzva a kód az include() függvénnyel. Utóbbi két elérési úthoz elég ha a vendégkönyv könyvtárának az elérési útját adjuk meg, mivel mindkettő ebben van. Úgyhogy az alábbi két változót kell létrehoznunk az index.php-ben:

	<?php
		$gb_homepage = "http://localhost/guestbook1/index.php";
		$gb_path = "vendegkonyv/";
		include("vendegkonyv/guestbook.php");
	?>

A $gb_homepage adja meg az index.php abszolút elérési útját a domain neven keresztül, ezt kell beszúrnunk az insert.php-ben a header() függvénybe. A $gb_path pedig a vendégkönyv könyvtárának elérési útja az index.php-hez képest. Azért írjuk bele a / jelet, hogy ha az index.php-el megegyező könyvtárba szórnánk a vendégkönyv fájljait, akkor egy üres karakterlánccal tudjuk megadni az elérési utat. A guestbook.php-ben a megfelelő helyekre beszúrjuk ezeket a változókat. A fájl elején a txt beolvasása:

	<?php

	// beolvasás
	$filepath = $gb_path."data.txt";
	$file = fopen($filepath, "r");
	...

Az űrlap <form> teg:

	<form method="post" action="<?php print $gb_path; ?>insert.php">

Most az insert.php-be kellene beszúrnunk a $gb_homepage változót. Igen ám, csakhogy az insert.php nincs behúzva az index.php-be. Az akkor nyílik meg, ha az űrlapot elküldjük, ez a változó ott nem fog létezni. Semmi pánik, az űrlappal szépen átküldjök ennek a változónak az értékét. Ezt kétféleképpen is megtehetjük. Vagy az action attribútumba szúrjuk be get paraméterként (majd a túloldalon a $_GET tömbből kihalásszuk), vagy az űrlap egy hidden elemébe rakjuk bele (és akkor meg a $_POST-ból kell kihalászni, ld. Űrlapok 4. lecke). Utóbbi átláthatóbb kódot eredményez, mivel az urlencode() függvényt se kell használnunk (ld. Űrlapok 3. lecke), így nem rondítjuk tovább az action attribútumot. Tehát az űrlap kódja:

	<form method="post" action="<?php print $gb_path; ?>insert.php">
		<input type="hidden" name="homepage" value="<?php print $gb_homepage; ?>" />
		Név:
		<input type="text" name="name" value="" />
		<br />
		Hozzászólás:
		<textarea name="comment"></textarea>
		<br />
		<input type="submit" name="send" value="Küldés" />
	</form>

A hidden elem ott van az elején, az űrlap elküldése után a $_POST["homepage"] változóban lesz a $gb_homepage értéke. A header() hívás ennek megfelelően módosul:

	header("Location: ".$_POST["homepage"]);

Az insert.php-ben hivatkozunk a txt fájlra is (mikor megnyitjuk hozzásűzésre), ott azonban nem kell a vendégkönyv elérési útja, mivel az insert.php közvetlenül fut a böngészőben (nem az index.php-n keresztül van behúzva), így a benne lévő relatív elérési utakat önmagához (az insert.php-hez) képest kell megadni.

És most már végre készen vagyunk! Ha át akarjuk rakni más weboldalra is a vendégkönyvet, csak a behúzó helyen kell átírnunk a két változót és az include() paraméterét az új környezethez igazítva.

A vendégkönyv teljes kódját (CSS fájllal együtt) innen töltheted le:

guestbook1.zip

A házi feladatok megoldásaival bővített változatot pedig innen (persze csak a feladatok megoldása után!):

guestbook2.zip

Házi feladat

A megoldásnál most nem írtam le a kódot, csak a magyarázatot, illetve hogy a fent letölthető guestbook2.zip-ben hol találod. A hordozhatóság érdekében az index.php-ben létrehozott két változóra gb-változók néven hivatkozok a házi feladatok magyarázatában.

1.) A vendégkönyvben jelenleg csak az utolsó 10 okosságot listázzuk ki. Szúrj be egy "archív" linket a listázás alá, amire kattintva jelenjenek meg a régebbi hozzászólások, továbbra is időrendben visszafelé (muszáj, mert valószínűleg ezek is hihetetlenül fontos információkat tartalmaznak). Ezek jelenjenek meg egy külön oldalon, mondjuk a vendegkonyv/archive.php fájl listázhatja őket, vagyis ide mutasson a link! Ha még nincs archív üzenet, akkor ezt írjuk ki, ne egy üres oldal jelenjen meg.

Megoldás

A vendegkonyv/guestbook.php 41. sorában van a link (a lista alján), és a vendegkonyv/archive.php listázza ki a régebbi hozzászólásokat. Mivel ez nem az index.php-be van behúzva, nincs szüksége a gb-változók átadására.

2.) Egészítsd ki a vendégkönyvet smiley kezeléssel! Tehát rakosgass a textarea alá vigyorgó fejeket ábrázoló kis képeket, majd ha rákattint valamelyikre, egy spéci karakterlánc fűződjön hozzá a textarea szövegéhez (az itt lévő fórumhoz hasonlóan; ehhez javascript kell, Űrlapok 4. lecke). Az insert.php-ben (a txt-be beírásnál) cseréld le ezeket a spéci karakterláncokat a megfelelő <img> tegekre!
Érdemes elgondolkodni azon is, miért az insert.php-ben érdemes a cserélgetést megcsinálni. Ugyanis azt is megtehetnénk, hogy változatlanul beírjuk a spéci smiley-kódokat a txt-be, majd a listázásnál cserélgetnénk le ezeket <img> tegekre.

Megoldás

A smiley-k a vendegkonyv/form.php-be (következő házi miatt van külön) vannak berakva, egy onclick eseménykezelő attribútum van mindegyik img tegen. Egyébként az 5 db kép a vendegkonyv/smileys könyvtárban van. Az index.php head tegjébe került a javascript függvény. A vendegkonyv/insert.php 14-23. sorai gondoskodnak a lecserélésekről.
Azért jobb, ha a beszúrásnál, és nem a listázásnál van a smiley-kódok lecserélése, mert a lecserélés egy bizonyos időt vesz igénybe. A listázás sokkal gyakoribb művelet szokott lenni, mint a beszúrás, így kevesebbszer kell megvárni a cserélgető kód lefutását, a lista gyorsabban megjelenik. A lista gyors megjelenítése akkor különösen fontos, ha például ez egy üzenőfal, ami a honlapunk összes aloldalára be van húzva. Ráadásul a lista megjelenítésénél biztosan több smiley-t kéne lecserélni, mint egyetlen hozzászólás beszúrása esetén.

3.) Mi van akkor, ha mi nem ugyanazon az oldalon szeretnénk megjeleníteni az űrlapot, mint az utolsó 10 hozzászólás listáját? Biztosítsuk azt, hogy az űrlap kódja egy külön helyre behúzható legyen, függetlenül a listától! Például a listázást továbbra is az index.php jeleníti meg, amiben van egy link (mondjuk "Én is okoskodok!" felirattal), amire kattintva megnyílik az aloldal.php, és az űrlap kódja ide van behúzva (ez nyilván már nem a vendegkonyv/guestbook.php-ben van, hanem legyen egy vendegkonyv/form.php nevű fájlban).

Megoldás

Ez igazából nem olyan bonyolult, mint amilyennek látszik, csak arról kell gondoskodni, hogy mindkét behúzásnál átadjuk a szükséges gb-változókat. Mivel a vendegkonyv/guestbook.php-ben már nincs űrlap, a $gb_homepage változóra már nincs szüksége, így azt törölhetjük az index.php-ből. Az aloldal.php-ben viszont meg kell adnunk mindkettőt. A javascript kódra is csak az űrlapnak van szüksége, így azt elegendő az aloldal.php-be berakni.