PHP nyelv 2. rész
Űrlapok Eddigi programjainkból egy fontos dolog hiányzott: nem tettük lehetővé, hogy a felhasználó által megadott adatokkal dolgozzon a program, vagy hogy a felhasználó módosíthassa az adatokat. Ahhoz, hogy ezt a felhasználó megtehesse, egy felületet kell számára biztosítani. Az Interneten alapvetően a HTML űrlapokon keresztül áramlik az információ a felhasználó és a kiszolgáló között. A PHP-t úgy tervezték, hogy a kitöltött HTML űrlapokat könnyen fel tudja dolgozni. Mielőtt azonban rátérnénk az űrlapok használatára, meg kell ismerkednünk néhány lehetőséggel, ugyanis információt a felhasználóról nem csak űrlapok segítségével kaphatunk.
Globális és környezeti változók A globális változókat a program legfelső szintjén találjuk, és a függvényeken belül is használhatjuk őket. A PHP rendelkezik egy $GLOBALS tömbbel, ami tartalmazza például a programban definiált változókat, és az ún. környezeti változókat is. A példaprogram a $GLOBALS tömb elemeit listázza ki: <?php $nev="Péter"; foreach ($GLOBALS as $kulcs=>$ertek) { print $kulcs." = ".$ertek."<br>"; } ?> A listában megtalálhatjuk az általunk bevezetett $nev változót a ’Péter’ értékével, a tömb kilistázásához felhasznált $kulcs és $ertek változókat értékükkel, és láthatunk sok, elsőre ijesztőnek tűnő nevet is.
A PHP fontosabb környezeti változói Leírás Példa $HTTP_GET_VARS Az űrlap mezőinek neve és értéke a GET kérelemnél (asszociatív tömb). nev=peter kor=32 $HTTP_POST_VARS Az űrlap mezőinek neve és értéke a POST kérelemnél (asszociatív tömb). $HTTP_REFERER Az oldal címe, amelyről a kérés érkezett. http://www.proba.hu/index.html $HTTP_USER_AGENT Az ügyfél böngészőjének neve és verziószáma. Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; .NET CLR 1.1.4322) $PHP_SELF Az éppen futó program elérési útja és neve. /proba.php $QUERY_STRING A GET kérelmeknél az URL-hez kapcsolt adat. nev=peter&kor=32 $REMOTE_ADDR Az ügyfél IP címe. 195.199.26.252 $REQUEST_METHOD A kérelem módja: GET vagy POST. GET $REQUEST_URI A kérelem teljes címe. /proba/urlap.php? nev=peter&kor=32
Űrlap létrehozása Készítsünk el egy olyan egyszerű űrlapot, amely egy beviteli mező segítségével bekéri a felhasználó nevét, majd egy gombra kattintva később megjeleníti. Az űrlap-fájl! <html> <head> <title>Egyszerű űrlap</title> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-2"> </head> <body> <form action="nevf.php" method="POST"> <p>Adja meg a nevét: <input type="text" name="nev"></p> <input type="submit" value="Mehet"> </form> </body> </html>
Űrlap létrehozása Mint látható, az űrlapot a HTML kódban a <form> és a </form> határolja. E két tag közé kell elhelyeznünk az űrlapelemeket, amelyek lehetnek szöveges beviteli mezők, listák, rádiógombok, elküld (submit) gombok, stb. A 8. sorban egy beviteli mezőt láthatunk, melynek azonosítója nev, a 9. sorban pedig egy elküld gombot, melynek értéke a Mehet (ez a gomb felirata), és feladata az űrlap továbbítása a megadott helyre. A 7. sorban látható, hogy az űrlap az értékeket POST–módszerrel továbbítja a célfájlnak (nevf.php), melyet az action-nel határoztuk meg. A POST mind nagy értékekkel, mind az értékek hosszú listájával működik. Amennyiben egy űrlapot a GET-módszerrel küldünk el, akkor annak mezői és azok értékei (URL kódolásban) hozzáfűződnek ahhoz a címhez (URL-hez), ahová az űrlapot küldjük. Az értékek ekkor elérhetők lesznek a kiszolgáló, illetve az általa futtatott programok számára. A kérés például a következőképpen nézne ki, ha a fenti példában a POST helyett GET szerepelne: http://localhost/nevf.php?nev=Kiss+Pista
Űrlap létrehozása Minden mező nevét egy ”=” jel választja el annak értékétől, a név-érték párokat pedig ”&” jelek határolják. A PHP visszafejti a jeleket, a talált adatokat a $HTTP_GET_VARS asszociatív tömbben teszi elérhetővé a program számára, valamint az űrlapelemek nevével azonos nevű globális változókat létrehoz a megfelelő tartalommal, függetlenül a kérelem típusától. A nev nevű GET változóhoz az alábbi két módon férhetünk hozzá: $HTTP_GET_VARS["nev"]; $nev; A GET lekérések szerencsére nem csak űrlapokkal kapcsolatban használhatók, könnyen készíthetünk mi is ilyen karakterláncokat, így a fontos adatokat könnyedén továbbíthatjuk lapról lapra.
Űrlap létrehozása Az adatok feldolgozásának vagy tárolásának megvalósítása a célfájl, ebben az esetben a nevf.php feladata lesz. A feldolgozáshoz már szükséges a PHP, míg magát az űrlapot leíró fájl tisztán HTML-kódot tartalmaz. Nézzük a nevf.php fájlt, aminek a feladata, hogy a beírt nevet megjelenítse: <?php print "Az Ön neve: $nev"; ?> Az űrlapelemek neveiből PHP változók lesznek, így a nev name-paraméterével megadott beviteli mező tartalma egy $nev változóba került. Semmi más dolgunk nem volt, mint kiíratni a böngészőbe a $nev változó tartalmát (2. sor).
Űrlap létrehozása Nézzünk egy másik példát: Adjuk meg a másodfokú egyenlet megoldásait a felhasználó által megadott együtthatók segítségével! Az űrlap állomány! <html> <head> <title>A másodfokú egyenlet megoldásai</title> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-2"> </head> <body> <form action="masodf.php" method="POST"> Adja meg az együtthatókat: <br><br> <input type="text" name="a"> *x*x + <input type="text" name="b"> *x + <input type="text" name="c"> = 0<br><br> <input type="submit" value="Megoldás"> </form> </body> </html>
Űrlap létrehozása <?php $d=$b*$b-4*$a*$c; if ($a==0) if ($b==0) print "Nincs megoldás"; else { $x=-$c/$b; print "Megoldás:<br>x = "; printf("%.3f",$x); } if ($d<0) $d=$d*(-1); $e=-$b/(2*$a); $f=sqrt($d)/(2*$a); print "Megoldások:<br>x1 = "; printf("%.3f",$e);print " + "; printf("%.3f",$f); print " * i<br>x2 = "; printf("%.3f",$e);print " - "; printf("%.3f",$f);print " * i"; }
Űrlap létrehozása Ez pedig az űrlap feldolgozó php-állomány! else { $x1=(-$b+sqrt($d))/(2*$a); $x2=(-$b-sqrt($d))/(2*$a); print "Megoldások:<br>x1 = "; printf("%.3f",$x1); print "<br>x2 = "; printf("%.3f",$x2); } ?> Ez pedig az űrlap feldolgozó php-állomány! Itt a feldolgozás már bonyolultabb volt, mint az előző esetben, de jól látszik, hogy a három változó – a $a, a $b, és a $c – a beviteli mezők neveiből keletkeztek. A printf() függvénnyel a kapott lebegőpontos számokat fixpontosan íratjuk ki 3 tizedesjegyet megjelenítve.
Űrlapelemek használata A nem és a kontinens kiválasztásához rádiógombokat használtunk. Ezek rádiógombok általában egy csoportot alkotnak, és a csoportból mindig csak egy választható ki (a checked tulajdonság jelzi azt, hogy kezdetben melyik van kiválasztva). A name tulajdonság értéke (nemcsop és kontcsop) lesz a változó neve, ami tartalmazza a kiválasztott értéket (a value tulajdonság értékét). Az életkor kiválasztásához egy listát használtunk (20. sor), a később létrejövő $eletkor változó valamelyik <option> és </option> közötti részt fogja tartalmazni. Az érdeklődési kör egy olyan lista (63. sor), amelyből több elem kiválasztható (multiple), neve az erdelkodes[], jelezve, hogy ebből egy tömb keletkezik. Ennek tartalmát később egy foreach segítségével íratjuk ki. Végül az űrlap alján a válaszok megerősítésére szolgáló jelölőt (checkbox) láthatunk, melynek később az állapotát vizsgáljuk meg (kipipáltuk vagy sem).
Űrlapelemek használata <?php if ($nev=="") print "<h3>Nem adta meg a nevét!</h3>"; elseif (!isset($erdeklodes)) print "<h3>Nem adta meg az érdeklődési körét!</h3>"; elseif (!isset($megfelel)) print "<h3>Nem hitelesítette adatait!</h3>"; else { print "<h3>Ön a következő adatokat adta meg:</h3>" ."<b>Név: </b>".$nev."<br>" ."<b>Neme: </b>".$nemcsop."<br>" ."<b>Életkora: </b>".$eletkor."<br>" ."<b>Kontinens: </b>".$kontcsop."<br>" ."<b>Érdeklődési köre:</b><br>"; foreach ($erdeklodes as $kor) print $kor."<br>"; } ?>
Űrlapelemek használata A programunk már tartalmaz némi ellenőrző részt is: az űrlap kitöltöttségét ellenőriztük le (2-7. sor). A későbbi fejezetekben láthatunk példát egyéb űrlapelemekre, és az adatok fájlban ill. adatbázisban tárolására is. A kód eredménye!
Űrlap és PHP kód egy oldalon A célunk az, hogy egy olyan oldalt készítsünk, amely tartalmazza egyrészt az űrlapot, másrészt a feldolgozását is megvalósítja. Ez csak úgy lehetséges, hogy az űrlap-fájl tartalmazza a PHP kódot, és önmagát hívja meg. A példaprogram egy számkitalálós játék, 1 és 100 közötti egész számot kell kitalálni maximum 10 lépésből.
Űrlap és PHP kód egy oldalon <html> <head> <title>Számkitalálós játék</title> </head> <body> <?php if (isset($szam)) { if ($db==10 || $szam==$aszam) print "<h3>"; if ($szam!=$aszam) print "Sajnos Ön nem tudta kitalálni a számot 10 próbálkozással!"; else print "Ön kitalálta a számot $db próbálkozással!"; print "</h3>"; unset($szam); print "<a href=$PHP_SELF>Új játék</a>"; die(); }
Űrlap és PHP kód egy oldalon else { print "<h3>"; if ($szam<$aszam) print "Próbálkozzon nagyobbal!"; else print "Próbálkozzon kisebbel!"; print "</h3>"."<p>Próbálkozások száma: $db</p>"; $db++; } $aszam=rand(1,100); print "<h3>Adja meg a számot:</h3>"; $db=1; ?> <form action="<?php print $PHP_SELF."?db=".$db ?>” method="POST"> <p><input type="text" name="szam"></p> <input type="submit" value="Mehet"> <input type="hidden" name="aszam" value="<?php print $aszam ?>"> </form> </body> </html>
Űrlap és PHP kód egy oldalon Az elgondolásunk az, hogy ha nincs a beviteli mezőnek értéke, azaz nincs kitöltve (8. sorban vizsgáljuk), akkor az azt jelenti, hogy most indul a játék. Ekkor generálunk egy véletlen számot ($aszam), és a számlálót ($db) 1-re állítjuk (34. és 36. sorok). Az elküld gombra kattintva a fájl önmagára hivatkozik a $PHP_SELF változó segítségével. Kapcsolt adat formájában küldjük tovább a próbálkozások számát tartalmazó számlálót (39. sor), és a kitalálandó számot egy rejtett mező segítségével küldjük tovább a későbbi vizsgálatok elvégzéséhez (42. sor). A számlálót minden új kitöltésnél növeljük eggyel (29. sor), és megjelenítjük a felhasználó számára, hogy az általa megadott szám nagyobb vagy kisebb –e a kitalálandónál (26. és 27. sorok). Amennyiben eltaláltuk a számot, akkor kiírjuk, hogy hány próbálkozással sikerült (17. sor), majd a beviteli mező nevéből keletkezett változót ($szam) megszűntetjük (19. sor), és a program futását megállítjuk a die() függvénnyel (21. sor). A változó megszűntetése a játék újraindításához szükséges. Ugyan ezt az utat járjuk végig, ha 10 lépésből nem sikerült kitalálni a számot, csak akkor a ”Sajnos Ön nem tudta kitalálni a számot 10 lépésből!” szöveg kiíratása után szűntetjük meg a változót, és állítjuk meg a programot.
Fájlok és könyvtárak A fájlok számtalan formátumban és funkcióban fordulnak elő a különböző operációs rendszerekben, azonban elmondható, hogy mindenhol az adattárolás alapegysége. Szerencsére számos programozási nyelv mellett a PHP is biztosít számunkra lehetőséget a fájl- ill. könyvtárkezelésre.
Könyvtárak A PHP az opendir() - könyvtár megnyitása, readdir() - könyvtár olvasása, closedir() - megnyitott könyvtár bezárása, és az is_dir() - eldönti valamiről, hogy könyvtár-e, függvényeket nyújtja a könyvtárak tartalmának megjelenítéséhez, míg az mkdir() új könyvtárat hoz létre, az rmdir() pedig létező könyvtárat töröl.
Könyvtárak listázása Két lehetőséget mutatunk be a sok közül erre a célra. Az első úgy listázza a könyvtár tartalmát, hogy megjeleníti a fájlok és könyvtárak nevét, és a könyvtárakhoz a [Dir] jelzést hozzáteszi. A másik leszámolja, hogy hány darab fájl ill. könyvtár található az aktuális könyvtárban. Az első: <?php $kvtnev="C:"; $kvt=opendir($kvtnev); while ($fajl=readdir($kvt)) { print "$fajl"; if (is_dir("$kvtnev/$fajl")) print " [Dir]"; print "<br>"; } closedir($kvt); ?>
Könyvtárak listázása Az egyetlen magyarázatra szoruló sor a 4. sor. A while feltételében egy értékadás szerepel, ami azt jelenti, hogy a ciklus addig ismétel, amíg sikeres az értékadás. A readdir() így addig olvas a könyvtárból a $fajl változóba, amíg van fájl a könyvtárban. A $fajl változóról pedig később eldöntjük az is_dir() függvény segítségével (függvényparaméterének abszolút elérési útnak kell lennie), hogy könyvtár volt –e (7. sor). A második megoldás:
Könyvtárak listázása <?php $kvtnev="C:"; $kvt=opendir($kvtnev); while ($fajl=readdir($kvt)) { $tipus=filetype("$kvtnev/$fajl"); if (isset($melyik[$tipus])) $melyik[$tipus]++; else $melyik[$tipus]=0; } closedir($kvt); foreach ($melyik as $kulcs=>$ertek) print $kulcs.": ".$ertek."<br>"; ?> A 6. sorban található filetype() függvény sztringként a fájl típusát adja vissza, ami a dir, file vagy link közül valamelyik lehet. A különböző típusok számát $melyik asszociatív tömbben tároljuk (7. sor).
Egyéb könyvtárfüggvények A dirname() függvénybe a fájl elérési útját írva az elérési út könyvtárrészét kapjuk, míg a basename() a maradékot, a fájlnevet eredményezi. A pathinfo() ugyanazt az információt adja, mint a dirname() és a basename() összekapcsolva. A realpath() a relatív útvonalat abszolút elérési úttá konvertálja.
Fájlok A következőkben megtudhatjuk, hogy hogyan kell egy létező PHP fájlt beágyazni egy másik PHP fájlba, hogyan lehet fájltulajdonságokat megjeleníteni, hogyan lehet fájlokat létrehozni, olvasni, írni és törölni, valamint a fájlfeltöltésről is lesz szó.
Fájlok beágyazása Az include() függvény lehetőséget ad arra, hogy fájlt ágyazzunk be a PHP dokumentumokba. A fájlban szereplő PHP kód úgy hajtódik végre, mintha a fődokumentum része lenne, ami hasznos, ha egy többoldalas programban külső kódokat szeretnénk beágyazni. Másrészről a többször felhasználandó részeket célszerű külön fájlokba elhelyezni, így azokat bármikor, bármelyik fájlba könnyedén beilleszthetjük. include("proba.php");
Fájlok listázása attribútumokkal együtt <?php $kvtnev="C:/"; $kvt=opendir($kvtnev); while ($fajl=readdir($kvt)) { fileinformaciok($kvtnev.$fajl); } closedir($kvt); function fileinformaciok($f) print "$f ".(is_file($f)?"":"nem")." fájl<br>"; print "$f ".(is_dir($f)?"":"nem")." könyvtár<br>"; print "$f ".(is_readable($f)?"":"nem")." olvasható<br>"; print "$f ".(is_writable($f)?"":"nem")." írható<br>"; print "$f ".(is_executable($f)?"":"nem")." futtatható<br>"; print "$f ".(filesize($f))." bájt méretű<br>"; print "$f utolsó megnyitásának dátuma: ".date("Y.m.d H:i",fileatime($f))."<br>"; print "$f utolsó módosításának dátuma: ".date("Y.m.d H:i",filemtime($f))."<br>"; print "$f utolsó változásának dátuma: ".date("Y.m.d H:i",filectime($f))."<br>"; ?>
Fájlok létrehozása és törlése Amennyiben egy üres fájlt szeretnénk létrehozni, a touch() függvényt kell használnunk. A paraméterként megadott fájlnév elé az elérési útvonalat is meg kell adnunk, ha nem az aktuális könyvtárban szeretnénk a fájlt létrehozni. Amennyiben a fájl már létezik, akkor az utolsó módosítás dátuma módosul az utasítás végrehajtási idejére. Pl.: touch("proba.txt"); Létező fájlt törölni az unlink() függvénnyel lehet. Az unlink() paramétere a fájl elérési útja: Pl.: unlink("proba.txt"); A létrehozás, törlés, írás, olvasás, módosítás csak akkor lehetséges egy fájlon, ha a megfelelő jogosultságokkal rendelkezünk.
Fájlok olvasása A fájlokat szekvenciálisan tudjuk olvasni és írni. A szekvenciális input fájt háromféleképpen lehet a PHP-ben olvasni: bájtonként, karakterenként, soronként. Ezek közül az egyiket mutatja be az alábbi példa fájl megnyitás után: A 3. sorban a file_exists() függvénnyel a fájl létezését döntjük el. A karakterenkénti olvasást a 13. sortól kezdve láthatjuk. A 15. sorban a fájlt megnyitjuk olvasásra (r - read) az fopen() függvénnyel, melynek eredménye a $f fájlváltozó (a továbbiakban ezzel dolgozunk). Ezek után egy karaktert olvasunk a fájlból az fgetc() függvénnyel, amíg a fájl végére nem érünk (feof() - logikai értékű függvény, igaz értéket ad, ha a fájl végére értünk).
Fájlok írása Fájlok írásánál ugyanúgy az fopen() függvényt használjuk, de a második paraméterét ”w”-re (write-ra) állítjuk, míg a hozzáfűzésnél ”a”-ra (append-re). Abból nem lehet probléma, ha több felhasználó is ugyanazt a fájlt olvassa, de azt nem engedélyezhetjük, hogy valaki írjon egy fájlt, míg más olvassa, vagy többen ugyanazt a fájlt írják. Amennyiben egy fájlt írásra vagy hozzáfűzésre meg tudunk nyitni, akkor zároljuk, hogy más felhasználó ne tudja addig írni, amíg mi írjuk. A példa az írást és a hozzáfűzést mutatja be:
Fájlok írása <?php $fajlnev="proba.txt"; print "$fajlnev fájlba írás<br>"; if (!file_exists($fajlnev)) touch($fajlnev); $fa=fopen($fajlnev,"w") or die("$fajlnev nem nyitható meg"); flock($fa,2); //más folyamatok nem olvashatják és írhatják ebben az időben fwrite($fa, "Helló, világ!\n"); flock($fa,3); //zárolás feloldása fclose($fa); print "$fajlnev fájlhoz hozzáfűzés"; $fa=fopen($fajlnev,"a") or die("$fajlnev nem nyitható meg"); flock($fa,2); fputs($fa,"Helló, Web!"); flock($fa,3); ?>
Fájlok írása Egy proba.txt nevű fájllal dolgozunk a példaprogramban. A 4. sorban döntjük el, hogy létezik-e a fájl, ha nem akkor létrehozzuk. Amennyiben a fájl létezett, akkor az írásnál elveszik korábbi tartalma. Az 5. sorban a fájlt megnyitjuk olvasásra, majd a 6. sorban zároljuk az flock() utasítással. Az flock() második paramétere lehet 1, 2 és 3. Az 1-es jelentése, hogy más folyamatok olvashatják, de nem írhatják. Ezt akkor használjuk, ha olvassuk a fájlt. A 2-es jelentése, hogy más folyamatok nem olvashatják és nem írhatják. Ezt akkor használjuk, amikor írunk a fájlba. A 3-as feloldja a zárolást. A fájlba íráshoz az fwrite() függvényt használjuk, melynek első paramétere a fájlváltozó, második paramétere jelen esetben egy string. A példaprogram további részében hozzáfűzzük a fájlhoz a ”Helló, Web!” szöveget az fputs() utasítással. Az fwrite() és az fputs() eredménye ugyanaz, az fputs() az fwrite() aliasa.
Fájlok feltöltése <html> <head> <title>Fájl feltöltése</title> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-2"> </head> <body> <?php $konyvtar="C:/munka/kepek"; if(isset($uploadfile)) /*ha kiválasztottuk a fájlt, akkor kiírjuk az adatait*/ { print "Fájl: " . $uploadfile; print "<br>Fájl neve: " . $uploadfile_name; print "<br>Fájl mérete: " . $uploadfile_size; print "<br>Fájl típusa: " . $uploadfile_type; } else print "<br>Adja meg a fájlt: "; print "<form enctype=\"multipart/form-data\"" ." action=$PHP_SELF method=\"post\">" ."<input type=\"hidden\" name=\"MAX_FILE_SIZE\"" ." value=\"200000\">" ."<input type=\"file\" name=\"uploadfile\" size=\"40\">" ."<br><input type=\"submit\" value=\"Feltöltés\"></form>";
Fájlok feltöltése if(is_uploaded_file($uploadfile)) /*ha a Feltöltés gombra rákattintottunk, akkor átmozgatjuk az ideiglenes könyvtárból a fájlt a célkönyvtárba*/ { if(move_uploaded_file($uploadfile,”$konyvtar/$uploadfile_name”)) print "<br><br>Fájl feltöltése sikerült."; } else print "<br><br>Fájl feltöltése sikertelen."; ?> </body> </html>
Fájlok feltöltése Az űrlap megjelenítése hasonló elven történik, mint a számkitalálós játékban: ha nincs még kiválasztva a fájl, akkor meg kell jeleníteni az űrlapot (9. sor). Az űrlap a 20. sortól a 25. sorig tart, ahol láthatjuk, hogy a fájlátvitelhez a form enctype=”multipart/form-data” sor szerepel a HTML részben, és a fájl önmagára fog hivatkozni a $PHP_SELF változóval. A fájl feltöltéséhez szükséges űrlapelemet a 24. sorban találjuk, melynek neve uploadfile, így ebből a $uploadfile változó fog keletkezni. A maximális fájlméretet (MAX_FILE_SIZE) beállítjuk 200000 bájtra, és egy rejtett mező segítségével küldjük tovább (22-23. sor). Ha kiválasztottuk a fájlt, akkor a fájl jellemzőit megjelenítő rész fut le (12-15. sor). A 27. sortól már a záró lépések láthatók: a fájl a célszerver ideiglenes fájlokat tartalmazó könyvtárába került, és onnan el kell mozgatnunk a célkönyvtárba (30. sor). Ha ez sikerül, akkor készen vagyunk a feltöltéssel, ellenkező esetben valami hiba folytán vagy nem került a fájl az ideiglenes fájlokat tartalmazó könyvtárba (pl. túl nagy a fájl), vagy nem lehet onnan elmozgatni.
Adatbázisok: MySQL A PHP előnyei közé soroltuk, hogy rengeteg adatbázissal képes dolgozni. Az adatbázisokhoz való csatlakozás, illetve azok kezelése a PHP-ben nagyon hasonló. A MySQL fejlesztői azt a célt tűzték ki, hogy egy praktikus, mindennapi használatra alkalmas adatbázist fejlesszenek, amely biztosítja a MySQL további sikerességét. Több érv is a MySQL adatbázis mellett szól: Egyszerű és gyors. Nyílt forráskódú. A kicsi és közepes méretű honlapokhoz ideális. A legtöbb szolgáltató a PHP-MySQL párost szokta nyújtani. A phpMyAdmin Database Manager egy olyan felületet nyújt, amely segítségével könnyen kezelhetjük a MySQL adatbázisainkat. Természetesen nem azt állítjuk, hogy a MySQL a legjobb adatbáziskezelő. Az adott feladat dönti el, hogy milyen adatbázist célszerű választani. Csupán megjegyeztük, hogy egy közepes méretű honlaphoz tökéletes az egyszerűsége és gyorsasága miatt.
MySQL adattípusok Egész számok Amennyiben adatbázisokban akarunk adatokat tárolni, ugyanúgy figyelembe kell vennünk, hogy milyen adattípusokat használhatunk. A következő táblázatok a különféle típusokat tartalmazzák: Egész számok Típus Hossz (bájt) Értéktartomány tinyint 1 -128..+127 smallint 2 -32768..+32767 mediumint 3 -8388608..+8388607 int 4 -2147483648..+2147483647 bigint 8 17-től 18 számjegyig decimal változó nincs korlátozva
MySQL adattípusok Lebegőpontos számok: Sztringek: Dátum és idő: Típus Hossz (bájt) Értéktartomány float 4 6 tízes helyiérték double 8 15 tízes helyiérték Típus Hossz (bájt) Értéktartomány char 1 1..255 varchar tinytext text 2 1..65536 longtext 3 1..16777216 mediumtext 4 1..4294967295 Típus Hossz (bájt) Formátum date 3 éééé-hh-nn datetime 8 éééé-hh-nn óó:pp:mm timestamp 4 ééééhhnnóóppmm time óó:pp:mm year 1 éééé (1901-től 2125-ig)
Kapcsolódás a kiszolgálóhoz Miután telepítettük a MySQL-t számítógépünkre, távoli vagy helyi gépek felhasználói bármikor csatlakozhatnak hozzá. A csatlakozáshoz természetesen felhasználónévre és jelszóra van szükség, és miután sikerült a csatlakozás sem biztos, hogy minden adatbázishoz hozzáférhetünk. A MySQL telepítés közben kér egy rendszergazda felhasználónevet és jelszót. A látogatókat azonban a későbbiekben nem célszerű a rendszergazda felhasználónévvel és jelszóval csatlakoztatni a MySQL-hez, mert a rendszergazdának teljes jogosultsága van. A MySQL sikeres telepítése után javasolt létrehozni egy olyan felhasználót, akinek csak a feltétlenül szükséges jogai vannak meg, majd ezzel csatlakoztatni az összes látogatót.
Kapcsolódás a kiszolgálóhoz A kapcsolódáshoz a mysql_connect() függvényt használjuk, ami 3 paramétert vár: szervernév, felhasználónév, jelszó. Ha nem adunk meg szervernevet, úgy alapértelmezetten a localhost-ot használja. Az üresen hagyott felhasználónév helyére a webszerverfolyamat tulajdonosának neve kerül, az üresen hagyott jelszó helyére pedig üres sztring. Ily módon el is lehet hagyni mindent, ha a MySQL a gépünkön fut, és a honlap az egyedüli honlap a gépen. Mi az összes példában megadjuk a paramétereket. $kapcsolat=mysql_connect("localhost","felhasznalo","jelszo"); if (!$kapcsolat) die("Nem lehet csatlakozni a MySQL kiszolgálóhoz!"); A $kapcsolat változóba mentjük a függvény által visszaadott azonosítót, hogy később folytathassuk munkánkat. Ha kapcsolat bontására a mysql_close() függvény szolgál.
Adatbázisok kiválasztása, listázása Az adatbázis kiválasztásához a mysql_select_db() utasítást használjuk: mysql_select_db("filmek",$kapcsolat) or die("Nem lehet megnyitni a filmek adatbázist: ".mysql_error()); A függvény logikai értéket ad vissza, hamis logikai érték esetén ellenőriznünk kell, hogy létezik-e az adatbázis, illetve, hogy van-e jogunk hozzá. A mysql_error() függvény segítségével jeleníthetjük meg a hiba okát (a MySQL által legutoljára jelentett hibát adja vissza). A $kapcsolat paraméter elhagyható a legtöbb függvény esetében, az aktuális kapcsolat automatikusan behelyettesítődik. Az létező adatbázisok listázásához a mysql_list_dbs() függvényt használjuk, ami egy eredményazonosítóval tér vissza, melyet a mysql_db_name() függvénynek kell átadnunk, hogy az adatbázisok neveit megkapjuk. Az utóbbi függvény vár egy sorszámot is, ami az adatbázis sorszámát jelöli. A példa a létező adatbázisok neveit jeleníti meg:
Adatbázisok kiválasztása, listázása <?php $felhasznalo="latogato"; $jelszo="valami"; $kapcsolat=mysql_connect("localhost",$felhasznalo,$jelszo); if (!$kapcsolat) die("Nem lehet kapcsolódni a MySQL kiszolgálóhoz!"); $adatbazisok=mysql_list_dbs(); $adatbazisok_szama=mysql_num_rows($adatbazisok); for ($i=0;$i<$adatbazisok_szama;$i++) print mysql_db_name($adatbazisok,$i)."<br>"; mysql_close(); ?> A 7. sorban található mysql_num_rows() függvény adja meg az adatbázisok számát az eredményazonosító segítségével. Ez a függvény egy tábla sorainak számát adja meg, így a későbbiekben még használni fogjuk.
Adatbázisok kiválasztása, listázása Az előbbi feladatot megoldhattuk volna máshogy is. Ha ismerjük az adatbázisok kiíratásához szükséges SQL parancsot (SHOW DATABASES;), akkor ezt sztringként átadva a mysql_query() függvénynek (az SQL utasítások végéről el kell hagynunk a pontosvesszőt), megkapjuk az adatbázisok neveit. A mysql_query() függvény továbbítja az SQL-ben leírt utasításokat a kiszolgálóhoz. <?php $felhasznalo="latogato"; $jelszo="valami"; $kapcsolat=mysql_connect("localhost",$felhasznalo,$jelszo); if (!$kapcsolat) die("Nem lehet kapcsolódni a MySQL kiszolgálóhoz!"); $eredmeny=mysql_query("SHOW DATABASES"); while ($sor=mysql_fetch_row($eredmeny)) print $sor[0]."<br>"; mysql_close(); ?> A 6. sorban kiadtuk a parancsot a MySQL kiszolgálónak, a kapott eredmény egy tábla formájában a $eredmeny változóba került. A táblát soronként tudjuk olvasni az első sortól kezdődően. Egy sor kiolvasásához a mysql_fetch_row() függvény szükséges, amely a $sor tömbbe tölti a sorban található adatokat. Mivel minden sor csak egy adatot tartalmaz (az aktuális adatbázis nevét), így azt a $sor[0]-ban érjük el. A while ciklus addig fut, amíg a mysql_fetch_row() tud új sort olvasni, azaz amíg van sora a táblának.
Adatbázisok tábláinak listázása A táblanevek megjelenítéséhez használhatjuk a mysql_list_tables() PHP utasítást, vagy adhatunk ki SQL parancsot (SHOW TABLES FROM adatbazisnev;), mint ahogy azt az előbb tettük. A példaprogram szerkezete nagyon hasonlít az előzőre: <?php $felhasznalo="latogato"; $jelszo="valami"; $kapcsolat=mysql_connect("localhost",$felhasznalo,$jelszo); if (!$kapcsolat) die("Nem lehet kapcsolódni a MySQL kiszolgálóhoz!"); $adatbazis="stone"; mysql_select_db($adatbazis,$kapcsolat) or die("Nem lehet megnyitni a(z) $adatbazis adatbázist: ".mysql_error()); $tablak=mysql_list_tables($adatbazis,$kapcsolat); while ($sor=mysql_fetch_row($tablak)) print $sor[0]."<br>"; mysql_close(); ?>
Táblák mezőinek listázása Az előbb eljutottunk odáig, hogy egy adatbázis tábláinak nevét meg tudtuk jeleníteni, most a táblák mezőinek nevére, és egyéb tulajdonságára vagyunk kíváncsiak. <?php $felhasznalo="latogato"; $jelszo="valami"; $kapcsolat=mysql_connect("localhost",$felhasznalo,$jelszo); if (!$kapcsolat) die("Nem lehet kapcsolódni a MySQL kiszolgálóhoz!"); $adatbazis="stone"; mysql_select_db($adatbazis,$kapcsolat) or die("Nem lehet megnyitni a(z) $adatbazis adatbázist: ".mysql_error()); $tabla="hir"; $mezok=mysql_list_fields($adatbazis,$tabla); $mezok_szama=mysql_num_fields($mezok); print "<table border=1><tr><td>Mező</td><td>Típus</td> <td>Hossz</td><td>Flag-ek</td></tr>"; for ($i=0;$i<$mezok_szama;$i++) print "<tr><td>".mysql_field_name($mezok,$i)."</td> <td>".mysql_field_type($mezok,$i)."</td> <td>".mysql_field_len($mezok,$i)."</td> <td>".mysql_field_flags($mezok,$i)."</td></tr>"; print "<table>"; mysql_close(); ?>
Táblák adatainak megjelenítése Az adatok lekérdezéséhez a SELECT * FROM tablanev; SQL parancsot használjuk a mysql_query() függvényben: <?php $felhasznalo="latogato"; $jelszo="valami"; $kapcsolat=mysql_connect("localhost",$felhasznalo,$jelszo); if (!$kapcsolat) die("Nem lehet kapcsolódni a MySQL kiszolgálóhoz!"); $adatbazis="stone"; mysql_select_db($adatbazis,$kapcsolat) or die("Nem lehet megnyitni a(z) $adatbazis adatbázist: ".mysql_error());; $tabla="hir"; $mezok=mysql_list_fields($adatbazis,$tabla); $mezok_szama=mysql_num_fields($mezok); $parancs="SELECT * FROM ".$tabla; $eredmeny=mysql_query($parancs); print "<table border=1><tr>"; for ($i=0;$i<$mezok_szama;$i++) print "<td>".mysql_field_name($mezok,$i)."</td>"; print "</tr>"; while ($sor=mysql_fetch_row($eredmeny)) { print "<tr>"; print "<td>".$sor[$i]."</td>"; } print "<table>"; mysql_close(); ?>
Adatbázis létrehozása és törlése Adatbázis létrehozásához használhatjuk a PHP mysql_create_db() utasítását, vagy kiadhatjuk a CREATE DATABASE parancsot a kiszolgálónak: <?php $felhasznalo="latogato"; $jelszo="valami"; $kapcsolat=mysql_connect("localhost",$felhasznalo,$jelszo); if (!$kapcsolat) die("Nem lehet kapcsolódni a MySQL kiszolgálóhoz!"); $adatbazis="forum"; if (mysql_create_db($adatbazis)) print "A(z) $adatbazis adatbázis sikeresen létrehozva."; else print "A(z) $adatbazis adatbázist nem sikerült létrehozni: mysql_error(); mysql_close(); ?> Adatbázis törléséhez a mysql_drop_db() függvényt használhatjuk a fentiekhez hasonló módon, vagy a DROP DATABASE parancsot adhatjuk ki.
Tábla létrehozása A táblák létrehozásához nincs külön PHP-függvény, itt mindenképpen az CREATE TABLE parancsot kell használnunk, miután kiválasztottuk a megfelelő adatbázist. <?php $felhasznalo="latogato"; $jelszo="valami"; $kapcsolat=mysql_connect("localhost",$felhasznalo,$jelszo); if (!$kapcsolat) die("Nem lehet kapcsolódni a MySQL kiszolgálóhoz!"); $adatbazis="forum"; mysql_select_db($adatbazis,$kapcsolat) or die("Nem lehet megnyitni a(z) $adatbazis adatbázist: ".mysql_error()); $tabla="forumuser"; $parancs="CREATE TABLE ".$tabla."(nev VARCHAR (30),fnev VARCHAR (30) NOT NULL PRIMARY KEY,jszo VARCHAR(50),email VARCHAR (40))"; if (mysql_query($parancs)) print "A(z) $table tábla sikeresen létrehozva."; else print "A(z) $table táblát nem sikerült létrehozni: ".mysql_error(); mysql_close(); ?>
Tábla törlése Táblák törléséhez a DROP TABLE utasítást használjuk: $tabla="forumuser"; $parancs="DROP TABLE ".$tabla; if (mysql_query($parancs)) print "A(z) $table tábla sikeresen törölve. "; else print "A(z) $table táblát nem sikerült törölni: ".mysql_error();
Adatok beszúrása Amennyiben már sikerült létrehoznunk egy adatbázist egy táblával, akkor adatokat helyezhetünk el a táblában. Ahhoz, hogy az adatokat felvegyük, egy újabb SQL parancsra lesz szükségünk, az INSERT-re. A példa az előbb létrehozott forum adatbázis forumuser táblájába szúr be egy sort, azaz egy felhasználó adatait rögzíti: Egy honlap esetében az adatokat természetesen majd egy űrlap segítségével fogjuk bekérni a felhasználótól. Amikor azonban a felhasználó adja meg az adatokat, elkerülhetetlen az adatok ellenőrzése.
Adatok beszúrása <?php $felhasznalo="latogato"; $jelszo="valami"; $kapcsolat=mysql_connect("localhost",$felhasznalo,$jelszo); if (!$kapcsolat) die("Nem lehet kapcsolódni a MySQL kiszolgálóhoz!"); mysql_select_db("forum",$kapcsolat) or die("Nem lehet megnyitni a forum adatbázist: sql_error()); $parancs="INSERT INTO forumuser(nev,fnev,jszo,email) VALUES('Kiss Béla','bela','csigabiga','kissb@freemail.hu')"; if (mysql_query($parancs)) print "Az adatok sikeresen be lettek szúrva az adatbázisba."; else print "Az adatokat nem lehet beszúrni: ".mysql_error(); mysql_close(); ?>
Adatok lekérdezése Miután rögzítettünk adatokat az adatbázisban, később vissza szeretnénk nyerni őket valamilyen formában. Egy tábla összes sorát már sikerült megjelenítenünk egy táblázatban, most egy olyan példa következik, amely eldönti, hogy van –e ”bela” nevű felhasználó a forum adatbázis forumuser táblájában: <?php $felhasznalo="latogato"; $jelszo="valami"; $kapcsolat=mysql_connect("localhost",$felhasznalo,$jelszo); if (!$kapcsolat) die("Nem lehet kapcsolódni a MySQL kiszolgálóhoz!"); mysql_select_db("forum",$kapcsolat) or die("Nem lehet megnyitni a forum adatbázist: ".mysql_error()); $parancs="SELECT * FROM forumuser WHERE fnev='bela'"; $eredmeny=mysql_query($parancs) or die("A parancsot nem lehet végrehajtani: ".mysql_error()); $sor=mysql_fetch_row($eredmeny); if (isset($sor[0])) print "Van bela nevű felhasználó."; else print "Nincs bela nevű felhasználó."; mysql_close(); ?>
Adatok lekérdezése A 9. sorban kiadtuk, azt az SQL parancsot, amely egy ”bela” nevű felhasználó összes adatát (a SELECT-ben a *-ot használtuk) egy eredménytáblába tölti. A táblának egy sora van, ha létezik ”bela” nevű felhasználó (mert az fnev elsődleges kulcs), és a táblának nincs egyetlen sora sem, ha nem létezik ”bela” nevű felhasználó. Ezután a 11. sorban beolvastuk a $sor változóba a tábla első sorát. A 12. sorban azt döntöttük el, hogy került-e valami a $sor tömbbe. Az eredménytábla sorait a mysql_fetch_object() függvénnyel is olvashatjuk, ami egy objektumot ad vissza, így a mezőket csak a nevük alapján lehet elérni (pl.: $sor->nev). Amennyiben a mezőket indexük és nevük alapján is el szeretnénk érni, akkor a mysql_fetch_assoc() utasítást használjuk, ami egy asszociatív tömbbel tér vissza (ilyenkor írhatjuk: $sor[0] vagy $sor["nev"]).
Adatok frissítése Az adatok frissítéséhez az UPDATE parancsot kell átadnunk a mysql_query() függvény segítségével a kiszolgálónak. A példában módosítjuk ”bela” felhasználó jelszavát és e-mail címét: <?php $felhasznalo="latogato"; $jelszo="valami"; $kapcsolat=mysql_connect("localhost",$felhasznalo,$jelszo); if (!$kapcsolat) die("Nem lehet kapcsolódni a MySQL kiszolgálóhoz!"); mysql_select_db("forum",$kapcsolat) or die("Nem lehet megnyitni a forum adatbázist: ".mysql_error()); $parancs="UPDATE forumuser SET jszo='kisbeka', email='kissb@hotmail.com' WHERE fnev='bela'"; if (mysql_query($parancs)) print "Az adatok sikeresen módosítva lettek."; else print "A parancsot nem lehet végrehajtani: ".mysql_error(); mysql_close(); ?> Amennyiben elhagyjuk az UPDATE parancs WHERE záradékát, akkor a módosítás a tábla összes során végrehajtódik (jelen esetben az összes felhasználó jelszavát és e-mail címét módosítja), ha pedig a feltételt több sor is kielégíti, akkor azokon mind végrehajtódik.
Adatok törlése Gyakran előfordul, hogy az adatbázisban szereplő adatokat meg szeretnénk szűnteti. Ehhez is egy SQL utasítás szükséges: a DELETE. A következő példa törli a ”bela” nevű felhasználót, azaz a forumuser tábla egy sorát: <?php $felhasznalo="latogato"; $jelszo="valami"; $kapcsolat=mysql_connect("localhost",$felhasznalo,$jelszo); if (!$kapcsolat) die("Nem lehet kapcsolódni a MySQL kiszolgálóhoz!"); mysql_select_db("forum",$kapcsolat) or die("Nem lehet megnyitni a forum adatbázist: ".mysql_error()); $parancs="DELETE FROM forumuser WHERE fnev='bela'"; if (mysql_query($parancs)) print "Az adatok sikeresen törölve lettek."; else print "A parancsot nem lehet végrehajtani: ".mysql_error(); mysql_close(); ?> Erre a parancsra is igaz az, hogy ha elhagyjuk a WHERE záradékot, akkor minden sorra érvényes, azaz a tábla teljes tartalmát törli, egyébként csak azokat a sorokat, amelyek a WHERE utáni feltételt kielégítik.
Képek A honlapok sokszínűségét a képek nagyban biztosítják, így elvárjuk, hogy egy nyelv különböző formátumú képeket tudjon létrehozni és manipulálni. A PHP képes GIF, JPEG, PNG, BMP, PDF és SWF kimenetet produkálni. Mindenképpen ajánlott, hogy a PHP dokumentációt tanulmányozzuk a képmanipuláló függvények címszó alatt, mert néhány függvény eléréséhez tudnunk kell a PHP fordítási és egyéb beállításait. A következőkben a képek megjelenítéséről, majd a különböző formátumú képek létrehozásáról, manipulálásáról lesz szó.
Képek listázása Az első példaprogram megjeleníti egy könyvtárban található összes képet: <?php $kvtnev="C:/munka/kepek"; $kvt=opendir($kvtnev); while ($fajl=readdir($kvt)) { if (is_file("$kvtnev/$fajl") && exif_imagetype("$kvtnev/$fajl")) print "<p><img src=$kvtnev/$fajl border=0></p>"; } closedir($kvt); ?>
Képek listázása A könyvtárat a $kvtnev változóba mentettük el (2. sor), majd a könyvtár tartalmát kilistáztuk egy while ciklus segítségével (4-8. sor). Amennyiben a könyvtárból kiolvasott (és a $fajl változóba mentett) sztring egy fájlt jelent (6. sor), ami kép formátumú, akkor azt megjelenítettük a böngészőben (7. sor). Az exif_imagetype() függvény kiolvassa egy fájl fejlécét, és ez alapján eldönti, hogy kép formátumú-e (ha a fejléc egy lehetséges képformátumot jelent, a függvény az igaz logikai értékkel tér vissza). A második példaprogram egy könyvtárban található képek tulajdonságait (fájlnév, fájlméret, valamint szélesség, magasság képpontokban megadva) írja ki a böngészőbe:
Képek listázása <?php $kvtnev="C:/munka/kepek"; $kvt=opendir($kvtnev); while ($fajl=readdir($kvt)) { if (is_file("$kvtnev/$fajl") && exif_imagetype("$kvtnev/$fajl")) $file[]["Név"]=$fajl; $file[]["Fájlméret"]=filesize("$kvtnev/$fajl"); $size=getimagesize("$kvtnev/$fajl"); $file[]["Szélesség"]=$size[0]; $file[]["Magasság"]=$size[1]; } closedir($kvt); foreach ($file as $info) foreach ($info as $kulcs=>$ertek) print $kulcs.": ".$ertek."<br>"; print "<br>"; ?>
Képek listázása A 8. sortól kezdődően látható, hogy egy kétdimenziós tömbben tároltuk képfájlokból nyert információkat. A kép méretét a már ismert filesize() függvénnyel kérdeztük le, míg a szélességét illetve magasságát a getimagesize() függvény segítségével tudtuk meg. A getimagesize() egy tömböt ad vissza, ahol a 0. indexnél a szélesség, az 1. indexnél pedig a magasság található. Végül bejártuk a kétdimenziós tömböt az ismert módon (16-23. sor).
Képek létrehozása Függvény Leírás imagearc() Ellipszis mentén vonalat rajzol. Ha a két paraméter, a magasság és a szélesség egyenlő, akkor körívet kapunk. imagefilledarc() Eredménye az előző függvényével megegyezik, majd az ív elejét a végével összekötve kitölthetjük a területet egy meghatározott színnel. imageellipse() Ellipszist rajzol, amennyiben a magasság egyenlő a szélességgel, kört kapunk. imagefilledellipse() Ellipszist rajzol, és maghatározott színnel kitölti. imagechar() Vízszintesen ír egy karaktert. imagecharup() Függőlegesen ír egy karaktert. imagestring() Vízszintesen ír egy sztringet. imagestringup() Függőlegesen ír egy sztringet. imagecolorallocate() Színt rendel egy képhez. Az első hozzárendelt színt a háttérre alkalmazza. imagecolordeallocate() Törli a színhozzárendelést, így újat adhatunk meg. imagecolorat() Adott pixelben levő szín kódját adja meg. imagecolorclosest() RGB színkódot fogad el, és a legközelebb álló szín kódját adja vissza.
Képek létrehozása imagecolorexact() RGB színt fogad el, és az adott szín kódját adja vissza, vagy -1-et, ha nem találja a színt. imagegammacorrect() Gamma-korrekciót hajt végre egy képen. imagecolorset() Színt állít be a skálán. imagecolortransparent() Átlátszónak állít be egy színt (a GIF támogatja). imagecolosforindex() A megadott szín piros, zöld és kék összetevőit egy tömbben adja vissza. imagecolorstotal() A színskála színeinek számát adja vissza. imagecopy() Egy kép téglalap alakú részét egy célterületre másolja. imagecopymerge() Az előző függvényhez képest még megadható, hogy a másolt rész a célterületet milyen mértékben fedje. imagecopyresized() Működése hasonló az imagecopy()-hoz, de átméretezi a képet, ha a célterület és a másolt rész nem azonos méretű. imagecopyresampled() Működése hasonló az imagecopyresized()-hoz, de újra-mintavételezi a képet. imagecreate() Színskála alapú képet (palettaképet) hoz létre (8 bites szín). imagecreatetruecolor() True color színmélységű képet hoz létre (24 bites).
Képek létrehozása imagetruecolortopalette() True color képet palettaképre konvertál. imagecreatefromgif() GIF fájlból vagy URL-ből hoz létre képet. imagecreatefromjpeg() JPEG fájlból vagy URL-ből hoz létre képet. imagecreatefrompng() PNG fájlból vagy URL-ből hoz létre képet. imagecreatefromwbmp() WBMP (Windows Bitmap) fájlból vagy URL-ből hoz létre képet. imagegif() Elküldi a képet a böngészőnek, vagy GIF formátumban fájlba menti. imagejpeg() Elküldi a képet a böngészőnek, vagy JPEG formátumban fájlba menti. imagepng() Elküldi a képet a böngészőnek, vagy PNG formátumban fájlba menti. imagewbmp() Elküldi a képet a böngészőnek, vagy WBMP formátumban fájlba menti. imagedestroy() Eltávolítja a képet a memóriából. imagecreatefromstring() Képet hoz létre egy sztringben tárolt képről. imagesetpixel() Pontot rajzol. imageline() Egyenest rajzol két pont között. imagedashedline() Szaggatott vonalat rajzol.
Képek létrehozása imagerectangle() Télalapot rajzol két sarokpont közé (bal felső és jobb alsó). imagefilledrectangle() Kitöltött télalapot rajzol. imagepolygon() Sokszöget rajzol egy tömbben meghatározott pontok alapján. imagefilledpolygon() Kitöltött sokszöget rajzol. imagesetbrush() Ecset választása a vonalakhoz. imagesetthickness() Vonalvastagság meghatározása. imagefontheight() Egy betűtípus magasságát adja vissza. imagesfontwidth() Egy betűtípus szélességét adja vissza. imagettftext() TrueType betűtípussal ír szöveget. imagettfbbox() Az imagettftext()-tel írt szöveget határoló téglalap méretét adja vissza. imagefill() Az egész képet egy színnel tölti ki. imagefilltoborder() Meghatározott színnel tölt ki egy képet a szín által meghatározott keretig. imageinterlace() Megadja, hogy az interlace-opció be van –e kapcsolva a képen, illetve segítségével be- vagy kikapcsolhatjuk. imagesx() A kép szélességét adja vissza. imagesy() A kép magasságát adja vissza. imagetypes() A PHP által támogatott képformátumok listáját adja vissza.
Példa képekre <?php $image["Nev"]="celtabla.png"; $image["Utvonal"]=$image["Nev"]; $image["Kep"]=imagecreate(400,400); //a képváltozó létrehozása $feher=imagecolorallocate($image["Kep"],0xff,0xff,0xff); $fekete=imagecolorallocate($image["Kep"],0,0,0); $voros=imagecolorallocate($image["Kep"],0xff,0,0); imagerectangle($image["Kep"],0,0,399,399,$fekete); imagerectangle($image["Kep"],1,1,398,398,$fekete); imagerectangle($image["Kep"],2,2,397,397,$fekete); imagerectangle($image["Kep"],3,3,396,396,$fekete); imagerectangle($image["Kep"],4,4,395,395,$fekete); imagefilledellipse($image["Kep"],200,200,300,300,$fekete); imagefilledellipse($image["Kep"],200,200,200,200,$feher); imagefilledellipse($image["Kep"],200,200,100,100,$fekete); nyil=array(202,200,268,144,268,171,379,171,379,229,268,229,268,256); imagefilledpolygon($image["Kep"],$nyil,7,$voros); $betutipus="C:/Windows/Fonts/arial.ttf"; $betumeret=20; $szoveg="Ide célozzon!"; $doboz=imagettfbbox($betumeret,0,$betutipus,$szoveg); $hely=(400-$doboz[2])/2; //középre igazítjuk a szöveget imagettftext($image["Kep"],20,0,$hely,35,$voros,$betutipus,$szoveg); imagepng($image["Kep"],$image["Utvonal"]); //létrehozzuk a png fájlt imagedestroy($image["Kep"]); print "<img src=$image[Nev]>"; //megjelenítjük a fájlt a böngészőben ?>
Képek változtatása Ide sorolhatjuk a képek színeit és méretét változtató függvényeket. Érdemes végiggondolnunk, mikor melyik képformátumot célszerű használni. A GIF 8 bites színt (256 különböző szín), a JPEG 24 bites színt használ. A GIF és a PNG pontos színeket tárol, a JPEG viszont egy közelítést tárol, amely egyre pontatlanabb lesz, ha növeljük a tömörítést. Amennyiben az imagejpeg()-et használjuk, és a minőségi paramétert (alapértelmezett érték a 100) 100-nál kisebbre állítjuk, veszítünk a színhelyességből. A képek átméretezésénél a nehézséget az optimális kép előállítása jelenti. A pixeles átméretezés az éles vízszintes és függőleges vonalak esetében működik, ahol az új méret az eredetinek páros töredéke. Egyéb technikák az átlós vonalakat és színárnyalatokat részesítik előnyben. Kicsinyítés után az eredeti méretre nagyítva a képet, könnyű észrevenni, hogy az eredeti kép tiszta, éles, egyenes vonalai egyenetlen görbékbe mennek át.
Képek változtatása A PHP képek átméretezésére használható függvénye az imagecopyresized(). Az újra-mintavételezés segít az egyenetlenség eltűntetésében, viszont elmosódottá teszi a képet. Az imagecopyresampled() függvény szolgál a PHP-ben a képek újbóli mintavételezéssel történő átméretezésére. A lenti példaprogram a felhasználó által feltöltött kép kicsinyített mását készíti el, melyet megjelenít az oldalon, és elkészíti az eredeti képre való hivatkozást.
Session-ök A HTTP állapot nélküli protokoll. Ez azt jelenti, hogy ahány oldalt a felhasználó letölt kiszolgálóról, annyi önálló kapcsolat létesül, ráadásul az egyes oldalakon belül a képek, külső fájlok letöltésére is külön kapcsolatok nyílnak. A felhasználók és a készítők viszont környezetként kezelnek egy weboldalt, amelyben egyetlen oldal is része egy nagyobb egésznek. Hogyan továbbíthatjuk tehát adatainkat oldalról oldalra, azaz hogyan valósíthatjuk meg az információtárolást? Több megoldás is rendelkezésünkre áll, melyek közül egyet már ismerünk: a GET típusú lekérdezést. A kifinomultabb lehetőségeknél említhetjük meg a cookie-k (sütik) és a session-ök használatát.
Session-ök A cookie-ban kis mennyiségű adatot tárol a felhasználó böngészője egy kiszolgáló vagy egy program kérésének eleget téve. Minden cookie egy nevet, egy értéket, a lejárati időpontot, a kiszolgálóra és elérési útra vonatkozó információkat tartalmazza. Vegyünk példának egy bevásárlókosár-szoftvert, amely a bevásárlólistát cookie-ban tárolja. Lesz olyan felhasználó, aki megváltoztatja a termékek árait a cookie-ban, lesz olyan, aki a kosara felét elhagyja, mert a cookie túllépi a böngészőjében beállított 4 KB-os maximális méretet. Olyan felhasználók is lesznek, akik egyáltalán nem tudnak vásárolni, mert letiltották a cookie-kat. Ha cookie-kat használunk, nem bízhatunk meg abban, ami a cookie-ban van.
Session-ök A session-ök egy azonosítót adnak a böngészőnek és a kiszolgálónak, hogy egy oldal lekérését ugyanabban a session-ben történt többi lekéréshez kapcsolják. A session a honlap tulajdonosának és látogatójának is rengeteg hasznot nyújt. A tulajdonos - az előző példánál maradva - megnézheti a látogatók által keresett termékeket, így tudja mivel célszerű oldalát frissíteni, valamint megtudhatja, hogy ki mit vásárolt. A látogató pedig be tud jelentkezni, automatikusan a testre szabott profilját használhatja. A session-azonosítót szokták cookie-ban tárolni, de a session-ökhöz nem feltétlen kell cookie, és az azonosítón kívül semmit nem tárolnak benne. Amennyiben a session-azonosítót cookie-ban tároljuk, mindig meg kell győződnünk arról, hogy valódi-e. A cookie-k segítségével távol tarthatjuk a zavaros session-azonosító sztringeket az URL-ektől, így a látogató felveheti az oldalt a kedvencek közé anélkül, hogy a session-azonosító is belekerülne. Amikor egy látogató a honlapunkon a következő oldalra megy, keresnünk kell a bejövő cookie-t annak a megállapítására, hogy a böngészője be van –e állítva a cookie-k fogadására. Ha a látogató kikapcsolta a cookie-kat, az URL-es megoldást használjuk.
PHP-szolgáltatások A PHP-ben vannak beépített session-kezelő szolgáltatások, melyek beállításait a ”php.ini” fájl tartalmazza. Ezek a szolgáltatások a legtöbb esetben működnek, ha azonban jobban akarjuk kontrollálni a session-öket, kikapcsolhatjuk őket. A következő táblázatban néhány ilyen szolgáltatás szerepel:
PHP-szolgáltatások Szolgáltatás Leírás session.save_handler=files Alapértelmezésben fájlban tárolja a session-öket, az adatbázisban való tároláshoz a files-t user-re kell megváltoztatni. session.save_path=/tmp Meghatározza, hogy a PHP hova írja a session-fájlokat. session.use_cookies=1 Bekapcsolja/kikapcsolja a cookie-kat. Az 1 a bekapcsolt, a 0 a kikapcsolt állapot. session.name=phpsessid A cookie nevét adja. session.auto_start=0 Bekapcsolja/kikapcsolja a session-ök automatikus elindítását. session.cookie_lifetime=0 A cookie-k élettertamát határozza meg. Alapesetben addig futtatja a session-öket, amíg a böngésző aktív. session.cookie_path=/ Meghatározza, hogy a PHP hova írja a cookie-kat. session.gc_probability=1 A PHP session-hulladékgyűjtőjét állítja be. Akárhányszor egy oldalkérés PHP session-kezelő rutint indít el, a PHP ellenőrzi, hogy szükség van –e hulladékgyűjtésre. Az 1-es érték jelentése, hogy a hulladékgyűjtő 100 oldalanként egyszer fusson.
PHP-szolgáltatások session.gc_maxlifetime=1440 Meghatározza, hogy a session-rekord az utolsó használat után hány másodpercig érhető el. Az alapértelmezés 1440 másodperc, azaz 24 perc. session.referer_check= Korlátozza a felhasználókat bizonyos oldalak elérésében, ám az ellenőrzés nem igazán megbízható, mert az sem, hogy ki látogatja az oldalt. session.entropy_file= Meghatároz egy külső fájlt, amely további adatokat biztosít a session-azonosító generáló rutinnak. session.entropy_length=0 A külső fájlból beolvasott bájtok számát határozza meg, így a 0 letiltja a külső fájlból való beolvasát. url_rewriter.tags="a=href,area=href,frame=src,input=src,form=fakeentry" Meghatározza, hogy melyik HTML tag kapja a session-azonosítót, amelyikben a cookie ki van kapcsolva.
Session-függvények Függvény Leírás session_decode() A session-rekord manuális kikódolására szolgál. session_destroy() Eltávolítja az adott session-höz kapcsolódó összes adatot. session_encode() A regisztrált változókat a PHP-nek megfelelő módon kódolja. session_get_cookie_params() Visszaadja a session cookie paramétereit egy asszociatív tömbben (lifetime, domain, path, secure). session_id() Az aktuális session-azonosítót adja vissza, és újat állít be. session_is_registered() A sztringként megadott változónévre igazat ad, ha a változó regisztrálva van a session-ben, hamisat, ha nem. session_module_name() Megadja, hogy a session.save_handler files-ra vagy user-re van –e állítva, illetve újat állít be. session_name() A session.name által megadott session-nevet adja vissza, és újat állít be.
Session-függvények session_register() A session-rekordban elmentésre kerülő változókat regisztrálja, melyeket sztringként kell megadni. Ha nem futattjuk a session_start()-ot, akkor a session_register() indítja el a session-t. session_save_path() A session-fájlok tárolásánál használt elérési utat adja vissza, illetve újat állít be. session_set_cookie_params() Session cookie-t állít be. Adott könyvtárhoz adott cookie-kat állít be, és felülír cookie paramétereket. session_set_save_handler() Az összes - általunk megírt - session-rekord kezelő függvény nevét fogadja el, és regisztrálja őket a kódban való használathoz, így a PHP ezeket használja majd a session-kezelésre. Ha a session.save_handler user-re van állítva, azaz adatbázisba mentünk, akkor a session-rekord kezelő függvényeket manuálisan kell definiálni. session_start() Elindít vagy folytat egy session-t. session_unregister() Eltávolítja a regisztrációs lista egy változóját. session_unset() Minden regisztrált válozót megszűntet. Megegyezik az session_unregister() lefutattásával az összes regisztrált változón.
Session indítása A session-öket mindig manuálisan kell elindítanunk a session_start() függvénnyel (vagy a session_register()-rel) kivéve, ha a php.ini fájlban a session.auto_start-ot 1-re állítottuk. A munkamenet elindítása után a session_id() függvénnyel lekérdezhetjük a generált session-azonosítót, és akár újat is megadhatunk. Keressük meg a session mentési könyvtárat (session.save_path), és figyeljük meg, hogy minden session egy session-fájl formájában található meg. A fájlok neve a sess_ karakterekkel kezdődik, a fájlnév további része a session-azonosító: sess_11e4ac4f039606c194375fd192ca9685
Változók regisztrálása A változók munkamenetbe való regisztrálásához a session_register() PHP-függvényt használjuk: $szemely="Kiss Pista"; $kor=28; session_register("szemely","kor"); A regisztrálás a session_register() használatakor történik, de a változók ténylegesen akkor lesznek a session-rekordba írva, amikor a szkript végére érünk. A szkript végén a változóknak létezniük kell azzal az értékkel, amit el akarunk menteni. Ezután a session-rekord minden változója a memóriába kerül, regisztrálva lesz. A változókat nem kell minden szkriptben regisztrálni, amíg a session tart, a változók regisztrálva maradnak. A változók eléréséhez azonban minden oldalnál meg kell hívnunk a session_start() függvényt.
Változók regisztrálása Amennyiben regisztráltunk már változókat, érdemes megtekinteni, hogy a PHP hogyan tárolja a session-rekordot a session-fájlban: szemely|s:10:"Kiss Pista";kor|i:28; A változó neve után | jel, majd a típusa következik. Az i az egész, a d a valós, a b a logikai, az s a sztring, az a a tömb, az o pedig az objektum. A : jel jelzi, hogy attribútum következik. A sztringnél az első attribútum a hossz, a második az érték, az egésznél az egyetlen attribútum az érték. A tömböknél a {} jelek a tömbön belüli elemek definiálására szolgálnak. Amennyiben egy változó neve előtt a ! szerepel és nincsenek attribútumai, az azt jelenti, hogy a változónak nincsen értéke.
Változók regisztrálása Amennyiben ellenőrizni szeretnénk, hogy egy változó regisztrálva van-e a munkamenetben, a session_is_registered() függvénnyel tehetjük meg: if (session_is_registered("szemely")) print "A személy be van jegyezve."; else print "A személy nincs bejegyezve.";
A regisztrált változók és a session megszűntetése A session megszűntetésére a session_destroy() függvény alkalmas. Ez a függvény a munkamenethez tartozó minden adatot töröl, azaz törli a session-fájlt, de nem törli a globális változókat és a session cookie-t. Amennyiben kiadjuk a session_destroy() parancsot, utána még elérhetők a regisztrált változók, amíg a böngésző fut, hiszen azok a memóriában vannak. A session_unregister() PHP-függvény törli a paraméterként megadott változókat a session-ből. Törléshez használhatjuk a session_unset() parancsot is, amely minden bejegyzett munkamenet-változót felszabadít. Az összes említett függvényre igaz, hogy nem törli a megfelelő globális változókat, így azokat az unset() fügvénnyel szűntethetjük meg.
Session-függvények használata A lenti példaprogram az alapvető session-függvények használatát mutatja be. A ”session.php” fájlban regisztrált asszociatív tömböt a ”sessionf.php” fájlban próbáljuk meg elérni és tartalmát kiíratni. <?php session_start(); //session indítása print "Session elindítva.<br>Session azanosító: ".session_id()."<br>"; $szemely=array("Név"=>"István","Születési idő"=>"1956.10.03", "Születési hely"=>"Keszthely"); session_register("szemely"); //a 'szemely' változó regisztrálása a session-be print "<br>Adatok:<br>"; foreach($szemely as $kulcs=>$ertek) { print $kulcs.": ".$ertek."<br>"; } $kodolt=session_encode(); /*a regisztrált változók kódolása a PHP-nak megfelelő módon, az eredmény a $kodolt változóba kerül*/ print "<br>Kódolva: ".$kodolt."<br><br> <a href=sessionf.php>Tovább</a>"; ?>
Session-függvények használata A sessionf.php állomány. <?php session_start(); if (session_is_registered("szemely")) print "A 'szemely' változó regisztrált!<br>"; session_decode($szemely); //a session-rekord-információk kikódolása print "<br>Az adatok:<br>"; foreach($szemely as $kulcs=>$ertek) { print $kulcs.": ".$ertek."<br>"; } session_unset(); // a regisztrált változók megszűntetése if (!session_destroy()) print "<br>A session-t nem sikerült megszűntetni!"; else print "<br>A session sikeresen megszűntetve!"; //a session-höz tartozó adatok eltávolítása ?>
Biztonság A PHP egy igen hatékony nyelv és feldolgozó program, akár egy különálló CGI futtatható állományként, akár kiszolgálómodulként működik. Képes elérni fájlokat, futtatni parancsokat és hálózati kapcsolatokat nyitni a szerveren. Ezek a tulajdonságok veszélyessé is tehetik a webszerveren futó alkalmazások számára, a PHP-t azonban úgy fejlesztették, hogy biztonságosabb legyen CGI programok írására, mint a Perl vagy C nyelvek. A teljesen biztonságos rendszer kialakítani tulajdonképpen lehetetlen, ám a PHP fordítási és futásidejű beállításainak helyes megválasztásával, és megfelelő programírási módszerek betartásával nagy mértékben növelhetjük a biztonságot. A továbbiakban néhány biztonsági tanács kerül említésre a különböző beállítási lehetőségekkel.
Biztonság A PHP egy igen hatékony nyelv és feldolgozó program, akár egy különálló CGI futtatható állományként, akár kiszolgálómodulként működik. Képes elérni fájlokat, futtatni parancsokat és hálózati kapcsolatokat nyitni a szerveren. Ezek a tulajdonságok veszélyessé is tehetik a webszerveren futó alkalmazások számára, a PHP-t azonban úgy fejlesztették, hogy biztonságosabb legyen CGI programok írására, mint a Perl vagy C nyelvek. A teljesen biztonságos rendszer kialakítani tulajdonképpen lehetetlen, ám a PHP fordítási és futásidejű beállításainak helyes megválasztásával, és megfelelő programírási módszerek betartásával nagy mértékben növelhetjük a biztonságot. A továbbiakban néhány biztonsági tanács kerül említésre a különböző beállítási lehetőségekkel.
CGI futtatható állományként telepített PHP A PHP CGI futtatható állományként való használata egy telepítési lehetőség azok számára, akik valami oknál fogva nem szeretnék a PHP-t modulként a szerverbe integrálni (pl. Apache). Ez a forma magával vonja azt, hogy a PHP a szerver cgi-bin könyvtárába legyen telepítve. a PHP-t úgy tervezték, hogy az ilyen telepítésekből adódó támadásokat kivédje: Rendszerfájlok elérése: http://domain.nev/cgi-bin/php?/etc/passwd Az URL lekérési információja, ami a kérdőjel után található, parancssori paraméterként kerül átadásra a feldolgozónak. Általában a feldolgozók megnyitják, és lefuttatják az első paraméterként adott fájlt. Amennyiben a PHP CGI futtatható állományként hívódik meg, nem veszi figyelembe a parancssori paramétereket.
CGI futtatható állományként telepített PHP Bármilyen web dokumentum elérése a szerveren: http://domain.nev/cgi-bin/php/titkos/doc.html Az elérési út információ az URL része, a futtatható fájl neve után lévő /titkos/doc.html a CGI program által megnyitásra és futtatásra kerülő fájl elérésének meghatározására használatos. Tipikusan néhány webkiszolgáló beállítási lehetőség (Apache-ban: Action) használatos a kérések átirányítására a dokumentumhoz, mint a http://domain.nev/titkos/szkript.php a PHP értelmező számára. Ezzel a beállítással a szerver először ellenőrzi az elérési engedélyeket a /titkos könyvtárra, és ezután állítja elő az átirányító kérést a http://domain.nev/cgi-bin/php/titkos/szkript.php oldalra, amit így már a PHP feldolgoz. Azonban ha eredetileg is ebben a formában volt megadva a kérés, nem történik elérési ellenőrzés a /titkos/szkript.php fájlra, csak a /cgi-bin/php fájlra. Ilyen módon bárki, aki elérheti a /cgi-bin/php címet, egyben tetszőleges védett dokumentumot is elérhet.
CGI futtatható állományként telepített PHP Csak publikus állományok találhatók a szerveren Amennyiben a szerveren nincs olyan tartalom, ami jelszó vagy IP alapú védelemmel van ellátva, nincs szükség ezekre a konfigurációs beállításokra.
CGI futtatható állományként telepített PHP Az - enable-force-cgi-redirect használata Ez a fordítási paraméter megakadályozza, hogy bárki meghívja a PHP-t egy http://domain.nev/cgi-bin/php/titkos/szkript.php URL-el. Ehelyett a PHP csak akkor fog elfogadni egy ilyen kérést ha egy szerver átirányításban kapta. Apache esetében tipikusan a következő direktívákkal történik a beállítás: Action php-script /cgi-bin/php AddHandler php-script .php
CGI futtatható állományként telepített PHP A doc_root és a user_dir beállítások A PHP szkript dokumentumok gyökérkönyvtárát a doc_root konfigurációs beállítással határozhatjuk meg a konfigurációs fájlban, vagy a PHP_DOCUMENT_ROOT környezeti változóban adhatod meg ezt az értéket.Amennyiben ez be van állítva a PHP CGI verziója a fájl elérési útját a doc_root és a kérés elérési út információja alapján állítja elő, ami azt jelenti, hogy ezen a könyvtáron kívül nem futtatható fájl (kivéve a user_dir esetét).
CGI futtatható állományként telepített PHP Egy másik itt használható opció a user_dir. Amennyiben ez nincs megadva, akkor csak a doc_root szabályozza a megnyitható fájlok körét. Ekkor egy http://domain.nev/~user/doc.php URL nem a ”user” nevű felhasználó home könyvtárában lévő fájlt keresi, hanem a ~user/doc.php fájlt keresi a doc_root alatt. Amennyiben a user_dir meg van adva, például public_php, akkor a fenti http://domain.nev/~user/doc.php kérés a doc.php nevű fájlt fogja megnyitni a ”user” nevű felhasználó home könyvtárában lévő public_php könyvtárban. Amennyiben a ”user” home könyvtára /home/user, a lefuttatandó fájl a /home/user/public_php/doc.php lesz.
CGI futtatható állományként telepített PHP PHP feldolgozó a web könyvtárfán kívül Rendkívül biztonságos lehetőség a PHP feldolgozót valahol a webről látható könyvtárakon kívülre tenni, például a /usr/local/bin könyvtárba. Az egyetlen igazi hátránya ennek az opciónak az, hogy minden PHP szkript első sorának egy ehhez hasonló sort kell megadni: #!/usr/local/bin/php ami meghatározza, hogy hol található a PHP feldolgozó, ami lefuttatja majd ezt a kódot. Ráadásul minden PHP szkriptnek futási jogot kell adni. Ahhoz, hogy ebben az esetben a PHP helyesen kezelje a PATH_INFO és a PATH_TRANSLATED információkat, a PHP feldolgozót az --enable-discard-path ”configure” paraméterrel kell fordítani.
Apache modulként telepített PHP A PHP-t Apache modulként használva örökli az Apache-t futtató felhasználó (tipikusan a ”nobody”) jogait. Ennek többféle hatása van a biztonságra és az azonosításra. PHP-n keresztüli adatbázis elérés esetén például, az adatbázist elérhetővé kell tenni ezen felhasználó számára is, kivéve ha az adatbázisnak beépített azonosítása van. Így egy rosszindulatú szkript elérheti, és módosíthatja az adatbázist, akár felhasználói név és jelszó nélkül is. Ez ellen lehet védekezni az Apache azonosítási technikákkal, vagy LDAP segítségével megvalósított saját elérési modellekkel. Az Apache felhasználó jogainak root szintre bővítése különösen veszélyes, és tönkreteheti a teljes rendszert. Az open_basedir használatával szabályozni lehet, hogy mely könyvtárakat olvashatja a PHP. Ki lehet jelölni ún. csak apache területeket, hogy minden web alapú művelet ide, csak ezekre a nem rendszer- és nem felhasználói fájlokra korlátozódjon.
Fájlrendszer A PHP képes a fájlok és könyvtárak hozzáférési jogosultságainak kezelésére. Ez lehetőséget ad arra, hogy megszabjuk, mely fájlok olvashatóak a rendszerben. A mindenki számára olvasható fájloknál ügyelni kell arra, hogy ne tartalmazzanak olyan adatot, amit nem szabad elolvasnia bármelyik felhasználónak. Mivel a PHP úgy készült, hogy felhasználói szintű fájlrendszer hozzáférést ad, lehetséges olyan program készítése, amely a rendszerfájlokat olvassa, mint pl. az /etc/passwd fájl. Ez maga után vonja, hogy minden esetben meg kell győződni arról, hogy a helyes fájlokat olvassa illetve írja a program. Nézzük a következő szkriptet, ahol a felhasználó megadja, hogy le szeretne törölni egy fájlt a könyvtárában. Ez többnyire egy webes felületet jelent, ahol egy PHP program használatos fájlkezelésre, ezért az Apache-t futtató felhasználónak engedélyezni kell a fájlok törlését a felhasználó könyvtárában.
Fájlrendszer <?php //egy fájl törlése akárhonnan, ahol a PHP usernek joga van erre $usernev=$_SERVER['REMOTE_USER']; //ez a user azonosított neve(ha volt előtte azonosítás) $konyvtar="/home/$usernev"; $torlendo_file=basename("$userfile"); //elérési trükközés eldobása unlink($konyvtar/$torlendo_file); $fp=fopen("/home/logging/filedelete.log","a"); //törlés naplózása $logstring="$usernev $konyvtar $torlendo_file"; fputs($fp,$logstring); fclose($fp); print "$torlendo_file törölve!"; ?>
Fájlrendszer Amennyiben az aktuálisan használt hitelesítési módszer megengedi a felhasználóknak, hogy saját login nevet válasszanak, és az egyikük a ”../etc/” -t választja, akkor a rendszer ugyanolyan védtelenné válik. Ebből kiindulva a jobban testreszabott ellenőrzés a következőképp alakulna: <?php $usernev=$_SERVER['REMOTE_USER']; // hitelesites $homedir="/home/$usernev"; if (!ereg('^[^./][^/]*$',$userfile)) die('rossz fájlnév'); //vége, nincs feldolgozás if (!ereg('^[^./][^/]*$',$usernev)) die('rossz usernev'); //vége, nincs feldolgozás //stb. ?> A használt operációs rendszertől függően széles a védeni kívánt fájlok skálája, beleértve az eszköz hivatkozásokat (/dev/ vagy COM1), konfigurációs fájlokat (/etc/ és az .ini fájlok), jól ismert tárolóhelyeket (/home/, My Documents). Emiatt könnyebb egy olyan rendszert készíteni, ahol mindent tiltunk azon kívül, amelyet kifejezetten megengedünk.
Adatbázis Az első lépés mindig az adatbázis létrehozása, hacsak nem egy kívülállóét kell használni. Az adatbázis létrehozásakor a tulajdonosé lesz, azé, aki lefuttatta az utasításokat. Általában csak a tulajdonos - esetleg az ún. superuser - jogosult bármiféle az adatbázis elemeit érintő műveletre. Annak érdekében, hogy más felhasználók is hozzáférjenek, jogokat kell nekik biztosítani. Az alkalmazásoknak soha nem szabad a tulajdonosaként vagy superuserként csatlakozni az adatbázishoz, mert ezek bármilyen utasítást és lekérdezést tetszés szerint futtathatnak, így például szerkezeti módosításokat hajthatnak végre vagy táblákat törölhetnek.
Adatbázis Mindig csak a legszükségesebb jogokat szabad engedélyezni, és el kell kerülni, hogy ugyanazt a felhasználót használjuk szerepeiben egymástól különböző esetekben. Ez azt jelenti, hogy ha a behatoló meg is szerzi valamelyik ilyen minősítést (hitelesítési információ=felhasználói név+jelszó), akkor is csak akkora változást tud okozni, mint az alkalmazás maga. Nem kell minden feladatfüggő szabályozást a webalkalmazásban (PHP szkriptben) kódolni, ehelyett inkább használjuk az adatbázis lehetőségeit: view-k (nézetek), trigger-ek, rule-ok (szabályok).
Adatbázis Az űrlapokból származó adatokat gyakran tároljuk adatbázisokban. SQL-parancsokat küldünk az adatbázis-szervernek, melyek előre elkészített és a felhasználótól kapott részekből állnak össze. Amennyiben a bevitelek érvényességét nem ellenőrizzük le még egyszer szerveroldalon, azt a hackerek kihasználhatják. Egy ellenőrizetlen mezőbe egyszerűen beírnak egy további SQL-parancsot, amely a lekérdezéssel egy időben végrehajtódik. Így olvasni vagy manipulálni lehet az adatokat, ami a felhasználó előtt rejtve marad. A hackernek ehhez még az adatbázis felépítését sem kell ismernie.
Adatbázis Amennyiben a támadó közvetlen hozzáférést szerzett az adatbázishoz (megkerülve a webszervert), a tárolt adatok védtelenné váltak, és visszaélhet velük, ha csak maga az adatbázis nem védi valahogy azokat. Az adatok titkosítása enyhíti ezt a veszélyt, de jelenleg nagyon kevés adatbázis kezelő támogatja a titkosítást. Ebben az esetben a PHP segítséget nyújthat néhány kiterjesztésével, mint például az Mcrypt vagy az Mhash, amelyek nagyon sokféle titkosító algoritmust fednek le. Olyan teljesen rejtett adatok esetén, amelyeknek nyílt ábrázolásukra nincs szükség, mert nem lesznek kiíratva, a hashelés alkalmazása is meggondolandó. A hashelés jól ismert példája az, hogy a jelszavak helyett, azoknak csak MD5 hash értékét tárolják az adatbázisban.
Adatbázis Mindig ellenőrizzük a bejövő adat típusát, hogy az a vártnak megfelelő-e. A PHP a bevitelt ellenőrző függvények széles körével rendelkezik kezdve a legegyszerűbbektől (pl. változókkal kapcsolatos függvények – is_numeric()) a Perl kompatibilis reguláris kifejezések támogatásáig. Amennyiben az alkalmazás számot vár, akkor megfontolandó az is_numeric() függvénnyel ellenőrizni a típusát, vagy megváltoztatni a típusát a settype() függvénnyel, vagy szám szerinti ábrázolását használni az sprintf() függvénnyel. Semmilyen adatbázisra jellemző információt - különösen szerkezetit - nem szabad kiírni. Idézőjelek közé kell tenni minden nem szám jellegű, felhasználótól származó adatot, erre használható az addslashes() vagy az addcslashes().
Felhasználótól érkező adatok Mindig alaposan meg kell vizsgálni a felhasználók által beadott adatokat, feltéve a következő kérdéseket: Biztos, hogy ez a szkript csak a kívánt fájlokat fogja módosítani? Előfordulhat egy ponton, hogy szokatlan vagy nem kívánatos adat jelenjen meg? Használható-e az adott szkript nem kívánatos formában? Felhasználható-e más szkriptekkel együtt egy negatív hatás elérésére? Megfelelően naplózásra kerülnek-e a tranzakciók (elérések, változtatások)? Alaposan átgondolva a fenti kérdéseket a szkript írásakor, megkímélhet minket attól, hogy később észrevéve a problémákat szerencsétlen módon újra kelljen írni a teljes kódot a védelem növelése érdekében. Persze ezzel sem lehet garantálni a rendszer biztonságát, de segíthet annak növelésében vagy fenntartásában.
Hibakezelés A PHP biztonsági kérdések felől a hibajelzéseknek két oldaluk van. Az egyiket nézve hasznos a védelem növelése szempontjából, a másik szemszögből viszont káros. Egy szokásos támadási technika minél több információ begyűjtése a rendszerről. Ezt úgy próbálják megoldani, hogy helytelen adatokat küldenek be, és rögzítik a hibaüzenetek típusait és környezetüket. Ez lehetőséget ad a crackernek, hogy elég információt gyűjtsön a rendszerről, és meghatározza a lehetséges gyenge pontokat. Amennyiben például a támadó összeszedegetett elég információt az előző űrlap kitöltések alapján, akkor megpróbálhatja a változókat felülírni vagy megváltoztatni őket.
Hibakezelés A PHP által visszaadott hibaüzenetek általában hasznosak a hibákat kereső fejlesztő számára, megjelölve a fájlt, és a függvényt, ami hibás, valamint megadva a megfelelő programsor számát. Nem ritka, hogy egy PHP fejlesztő a show_source(), highlight_string(), vagy highlight_file() függvényeket a fejlesztés során hibakeresésre is használja, de egy élesben lévő webhelyen ez rejtett változókat, ellenőrizetlen kódokat, és más veszélyes információkat fedhet fel. Kifejezetten veszélyes beépített hibakezelővel rendelkező ismert forrású kódok használata. Amennyiben a támadó ráismer valamilyen általános programozási technikára, akkor megpróbálhatja feltörni az oldalt a különböző megszokott hibakereső (debugging) változók elküldésével.
Hibakezelés A fájlrendszer vagy általános PHP hibák jelezhetik, hogy milyen jogokkal rendelkezik a webszerver, és megmutathatják a fájlok elrendezését és struktúráját. A fejlesztő által írt hibás kód súlyosbíthatja a helyzetet, egykori rejtett információk könnyű kiderítését lehetővé téve. Három megoldási lehetőség adódik erre a problémára. Az első, megvizsgálni alaposan a függvényeket, és megpróbálni elkerülni a hibákat. A második a a hibajelzés kikapcsolása a teljes kódon belül. A harmadik a PHP testreszabható hibajelentési funkcióinak használata, így saját hibakezelőket definiálhatunk. A már megtett biztonsági intézkedésektől függően esetleg mindhárom fenti módszert választható. Hasznot nyújthat a PHP beépített error_reporting() függvénye, amely segít biztonságosabbá tenni a programokat és megtalálni a változók vészelyeket rejtő használati formáit.
Globálisan elérhető változók A PHP 4.2.0 változatától kezdődően a register_globals direktíva alapértelmezett értéke OFF-ra van állítva. Amikor a register_globals be van kapcsolva, megmérgezi a szkriptet mindenféle változóval, mint például a HTML űrlapokból származókkal. Ehhez még hozzáadódik az, hogy a PHP nem követeli meg a változók inicializálását, így sokkal könnyebb veszélyes kódot írni. Amennyiben bekapcsolt állapotban van, sokan úgy használják a változókat, hogy valójában nem is tudják honnan származnak, csak feltételezik. Ilyenkor a szkriptben definiált változók összekeverednek a felhasználótól érkezőkkel. A 4.1.0 változattól kezdődően bevezetésre kerültek olyan szuperglobális hatókörű tömbök, mint például a $_GET, $_POST, $_SERVER. Ezek segítségével könnyen ellenőrizhetjük, hogy a változók honnan érkeztek. A lenti példa a session-ökhöz kapcsolódik:
Globálisan elérhető változók <?php //Nem tudhatjuk, hogy a $usernev honnan származik, de azt igen, hogy //a $_SESSION a munkamenet adatokat hivatkozza if (isset($_SESSION['usernev'])) { print "Hello, <b>{$_SESSION['usernev']}</b>"; } else print "Hello, <b>Vendég</b><br>"; }?>
A PHP elrejtése Néhány egyszerű módszerrel elrejthető, hogy PHP-t használsz, így lassítva le a támadót, aki fel akarja deríteni a rendszer gyenge pontjait. A ”php.ini”-ben az expose_php=off beállítással csökkentheted ezeket az információkat. Másik taktika a webszerver (pl. apache) olyan beállítása, hogy különböző típusú fájlokat is PHP-n keresztül futtasson. HTML típus használata PHP fájlkiterjesztésként: AddType application/x-httpd-php .htm .html Ahhoz, hogy ez jól működjön, minden PHP fájlt át kell nevezni a ”htm” vagy ”html” kiterjesztések valamelyikére. Mivel ez is a rejtőzésen alapuló védelmi forma, kevés hátránya mellett csekély megakadályozó intézkedést jelent.
Frissítések A PHP, mint bármilyen más nagy rendszer folyamatosan változások és fejlesztések alatt áll. Minden új változat kisebb-nagyobb változtatásokat tartalmaz, fejlesztve a nyelvet, kijavítva biztonsági hibákat, beállítási kellemetlenségeket, és más olyan elemeket, amik a teljes rendszer stabilitására és biztonságára hatnak. A legjobb hozzáállás a gyakori frissítés, valamint a friss változatokról, és a fellépő változásokról való informálódás.