A fejezet első részében HTML oldalak PHP-val történő előállítása szerepel kerül részletesebb tárgyalásra, azaz az, hogy hogyan állítja elő a szerveroldali program a HTML oldalt. A második felében azt az utat követjük nyomon, amely során a klienstől adat érkezhet a szerveroldali PHP szkriptnek.
Az előző fejezetben a PHP-ra mint programozási nyelvre tekintettünk, és ennek megfelelően a nyelvi elemeivel ismerkedtünk meg, a könnyebb megértés érdekében a JavaScript nyelvvel párhuzamba állítva. Láthattuk, hogy a hasonló nyelvtípusuk és a C szintaxis miatt nem kell túl sok újdonságot elsajátítanunk a nyelv alapfokú használatához. Külön hangsúlyt fektettünk megint csak az adatok modellezésének, az adatszerkezetek leírásának, valamint a feladatmegoldáshoz használt programozási tételek bemutatásának PHP nyelven. A C++ vagy JavaScript megfelelőivel összevetve jól kivehetőek a hasonlóságok és az eltérések is.
A csak nyelvi tulajdonságokat bemutató PHP programokat legegyszerűbb parancssori környezetben kipróbálni, mivel ekkor még a webbel kapcsolatos ismeretek nem lényegesek.
Ebben a fejezetben azonban azokat az ismeretek mutatjuk be, amelyek ahhoz kellenek, hogy a PHP-t dinamikus weboldalak generálására használjuk. Még egyszer áttekintjük, hogy a PHP hogyan illeszkedik az oldalkiszolgálás folyamatába, hogyan tudunk vele webes tartalmat (főleg HTML dokumentumokat) előállítani, és hogyan tudunk szkriptjeinknek paramétereket átadni, hogy valóban dinamikusan tudjanak reagálni környezetük változásaira. A fejezet végén pedig a bemeneti adatok kapcsán az űrlapok adatainak feldolgozásával foglalkozunk részletesebben.
Ahogy a fenti ábra is mutatja, a PHP az oldalkiszolgálás folyamatában igazából egy speciális CGI program, legalábbis olyan értelemben, hogy bizonyos (tipikusan .php kiterjesztésű) állományok esetében a webszerver az állományt a PHP értelmezővel lefuttatja, majd ennek eredményét küldi le a kliensnek. A kommunikáció a webszerver és a PHP szkript között (2. és 3. pont) a CGI interfész szabályainak betartásával történik. A bemenő és kimenő adatok átadását tehát ez az interfész írja le, és szkriptünknek ekként kell működnie.
A fenti ábra szerint tehát webes környezetben a PHP egyetlen célja, hogy a megfelelő webes tartalmat programozottan előállítsa, ami az esetek nagy részében HTML dokumentumok generálását jelenti.
A CGI szabvány szerint a webszerver által leküldendő tartalmat a programnak a standard kimenetre kell kiírnia. Innen veszi fel a webszerver és továbbítja a kliens felé. A PHP szkriptnek tehát a generált HTML állományt a standard kimenetre kell írnia. Ezt alapvetően kétféleképpen lehet megtenni:
Példaképpen nézzük meg egy nagyon egyszerű, valid oldalsablon kiírását PHP-ban. Célunk a következő HTML forrás generálása:
<!doctype html> <html> <head> <meta charset="utf-8"> <title></title> </head> <body> <p>Hello világ!</p> </body> </html>
Első körben ezt a tartalmat soronként kiírva állíthatjuk elő az echo parancs használatával:
<?php echo '<!doctype html>'; echo '<html>'; echo ' <head>'; echo ' <meta charset="utf-8">'; echo ' <title></title>'; echo ' </head>'; echo ' <body>'; echo ' <p>Hello világ!</p>'; echo ' </body>'; echo '</html>'; ?>
Látjuk és érezzük, hogy ez a megoldás nagyon bőbeszédű, és rengeteg felesleges kódrészletet tartalmaz. A HTML forrást nem szükséges soronként feldolgozni. Megadhatjuk egy nagy szövegként, és ezt a szöveget íratjuk ki az echo paranccsal. A szöveg megadását az alábbiakban nowdoc formátumban végezzük el:
<?php echo <<<'VEGE' <!doctype html> <html> <head> <meta charset="utf-8"> <title></title> </head> <body> <p>Hello világ!</p> </body> </html> VEGE; ?>
A legegyszerűbb megoldás azonban mégiscsak az, hogy kihasználjuk: ami nincs PHP blokkban, az automatikusan kiírásra kerül. Így a PHP állományunk e példában csupán a HTML forrást tartalmazza, és nincsen benne értelmezendő PHP blokk:
<!doctype html> <html> <head> <meta charset="utf-8"> <title></title> </head> <body> <p>Hello világ!</p> </body> </html>
Böngészőből meghívva ezt az állományt (pl. http://localhost/hello.php), a PHP szkript kimenetére ez a HTML forráskód kerül, ezt küldi vissza a webszerver a böngészőnek, amely megjeleníti azt.
Érdemes annyit megjegyezni, hogy a két fejezettel ezelőtti C++ kóddal összehasonlítva itt már nem kellett HTTP fejléceket küldeni (Content-Type). Bár fejléceket PHP is lehet küldeni, a tipikusan előforduló fejlécek generálását átveszi a programozótól, hogy az a lényegi tartalom generálására koncentrálhasson.
Természetesen a fenti példa elég laboratóriumi, azt hivatott bemutatni, hogy statikus tartalom kiírását nem érdemes PHP blokkban elvégezni. Másrészt azt a következtetést is levonhatjuk, hogy statikus HTML állományokat nyugodtan bújtathatunk .php kiterjesztés mögé, azokkal a PHP értelmező semmit sem tesz, csak kiírja őket.
PHP használatának azonban akkor van értelme, ha a kimenet generálásához valamilyen logika kell. Ez a logika jelenik meg a nyelvi elemekben, vezérlési szerkezetekben, mely utóbbiak mindig valamilyen adat feldolgozásához kapcsolódnak. A következőkben nézzük meg a tartalomgenerálás szempontjából előforduló tipikus eseteket.
Példaképpen ragadjuk ki egy nagyobb feladat egy kis részletét: egy termékről a felhasználó visszajelzések alapján adott egy pontszám 1 és 5 között.
1. feladat: írjuk ki a pontszámot!
A feladatban szereplő adat, a pontszám egy változóban jelenik meg a programban. Egyelőre nem fontos, hogy hogyan kerül oda, tekintsük adottnak.
<?php $pontszam = 3.8; ?>
A feladat e változó értékének a kiírása. A megoldáshoz biztosan programot kell használni, hiszen adat csak programban jelenhet meg, és csak programmal lehet feldolgozni. PHP-ban a kiírást programozottan az echo paranccsal tudjuk elvégezni. Kérdés viszont, hogy mit írjunk ki. Csak a változó értékét? Vagy ezt valamilyen HTML elemben tegyük meg? Első lépésként tehát fogalmazzuk meg, hogy milyen kimenetet várunk el. Ebben az esetben a generálandó tartalom legyen a következő:
<p>A termék értékelése: 3.8</p>
A következő lépés, hogy ezt a kimenetet PHP segítségével állítjuk elő. A megoldás adja magát:
<?php echo "<p>A termék értékelése: {$pontszam}</p>"; ?>
Igen ám, de ezt a kimenetet másféleképpen is előállíthatjuk:
<p><?php echo "A termék értékelése: {$pontszam}"; ?></p>
Vagy akár így is:
<p>A termék értékelése: <?php echo $pontszam; ?></p>
Melyiket válasszuk? Ha lefuttatjuk ezeket, akkor mindhárom ugyanazt a kimenetet adja. Ilyen szempontból mindegy is. Viszont legelső példánkból azt vontuk le következtetésként, hogy a statikus tartalmak kiírásához felesleges PHP-t használnunk. Érdemes tehát az elvárt kimenetet elemezni, és megnézni: melyik része dinamikus, mely részének előállításához kell program? Egyedül a 3.8 kiírásához. Ennek az elvnek tehát a fenti három megoldás közül egyedül az utolsó felel meg:
<p>A termék értékelése: <?php echo $pontszam; ?></p>
Mivel egy változó értékének kiírása viszonylag gyakori, ezért bevezettek egy kényelmesebb, rövidebb formulát erre (5.4-es PHP verziótól külön beállítás nélkül is használható):
<p>A termék értékelése: <?=$pontszam?></p>
2. feladat: Nem értékelt termékeknél a pontszám 0. Az értékelést csak akkor írjuk ki, ha a pontszám 1 és 5 közé esik, ellenkező esetben jelezzük a felhasználó felé, hogy még nincs értékelés.
Mivel HTML generálásáról van szó, megint csak érdemes az elvárt kimenetből kiindulni, hogy azt aztán össze lehessen vetni a generált tartalommal. Pontszám értékétől függően kétféle kimenetünk lehet:
<p>A termék értékelése: 3.8</p> <!-- vagy --> <p>A termék még nincs értékelve.</p>
A feladat megoldásához a pontszám értéke alapján elágazást kell használni. Továbbra is ügyelünk arra, hogy csak a dinamikus rész legyen PHP blokkokban.
<?php if ($pontszam >= 1 && $pontszam <= 5) { ?> <p>A termék értékelése: <?php echo $pontszam; ?></p> <?php } else { ?> <p>A termék még nincs értékelve.</p> <?php } ?>
Amikor kiíráshoz használjuk a PHP vezérlési szerkezeteit, akkor az utasításblokkok zárójele kevésbé olvasható a sok egyéb jelölő között. Ilyen esetekben (csak kiírásnál!) szokták használni a PHP alternatív szintaxisát, amely a következőképpen néz ki elágazás esetén:
<?php if ($pontszam >= 1 && $pontszam <= 5) : ?> <p>A termék értékelése: <?php echo $pontszam; ?></p> <?php else : ?> <p>A termék még nincs értékelve.</p> <?php endif; ?>
Ebben jól látszik, hogy az elágazás melyik része, hol van a kódban.
3. feladat: Az értékelést vizuálisan is jelezzük annyi csillag kiírásával, ami a pontszám alsó egész része.
A generált HTML-lel kapcsolatos elvárásaink 3.8-as pontszám mellett a következők:
<p>A termék értékelése: 3.8 (***)</p>
A csillagokat egy ciklussal állíthatjuk elő:
<p>A termék értékelése: 3.8 ( <?php for ($i = 1; $i <= floor($pontszam); $i++) { ?> * <?php } ?> )</p>
Az olvashatóság érdekében itt is érdemes áttérni az alternatív szintaxisra:
<p>A termék értékelése: 3.8 ( <?php for ($i = 1; $i <= floor($pontszam); $i++) : ?> * <?php endfor; ?> )</p>
Ezekhez az elvekhez ragaszkodva kiírásunk sablonszerű lesz: az alapvetően statikus HTML-ben speciális jelölőkkel jelezzük a dinamikus tartalom beszúrását és annak jellegét. Ha megfigyeljük, akkor a kiíráshoz csupán háromféle utasítást szükséges használni: változók kiírásához az echo parancsot, elágazásokat (if) és ciklusokat (for valamelyik variánsa).
A fenti példa egyes részeit összegyúrva az alábbi végeredmény jön ki:
<?php if ($pontszam >= 1 && $pontszam <= 5) : ?> <p>A termék értékelése: <?php echo $pontszam; ?> ( <?php for ($i = 1; $i <= floor($pontszam); $i++) : ?> * <?php endfor; ?> )</p> <?php else : ?> <p>A termék még nincs értékelve.</p> <?php endif; ?>
Az előzőekben részletesen foglalkoztunk PHP szkriptek kimenetével, azaz a HTML oldalak generálásával. Weblapjaink azonban többek között attól válnak dinamikussá, hogy a körülményektől, általában a felhasználó tevékenységétől függően másképp működnek vagy mást jelenítenek meg. Egy szerveroldali szkriptnek a működését az adatok határozzák meg. A szerveroldali webprogramozásnál a dinamizmust tehát az jelenti, hogy kérésenként eltérő adatokat kap az oldal paraméterül.
Szkriptjeinknek a következő forrásokból érkezhetnek adatok:
A PHP szkripthez érkező adatok egy része a böngészőből érkezik. A fenti ábrán látható az a sematikus folyamat, ahogy egy böngészőbeli tevékenység végül a PHP szkript futását eredményezi. A böngészőben keletkező adatokból HTTP kérés lesz (1), és ebben a formában érkezik a szerverre. Itt a webszerver a HTTP kérést a CGI interfész szerint feldolgozza (2), majd a szkriptet elindítja, amely az adatokat beolvassa (3).
A fenti folyamatból egy korábbi fejezetben részletesen foglalkoztunk a közbülső lépéssel, azaz azzal, hogy a HTTP kérés adatait a webszerver a CGI szabvány alapján hogyan készíti elő a szkript számára. Láthattuk, hogy a HTTP kérés során a kérés körülményeiből, az URL-ből és a HTTP fejlécekből környezeti változók lesznek, a HTTP üzenettörzs pedig a standard bemeneten jelenik meg. A HTTP kérés egyes részei közül elsősorban az URL <query> része és az üzenettörzs tartalmazhat szabadon megadható paramétereket. Így a szkript számára felhasználó által megadott adat a QUERY_STRING környezeti változón és a standard bemeneten érkezhet. (Megemlítjük, hogy a kérés körülményei, pl. a kérés módja, és a fejlécek is szabályozhatók kliensoldalról, de ennek mélyebb taglalása nem e tananyag célja.)
Alábbiakban látható egy URL általános felépítése, és az, hogy ennek egyes részei hogyan jelennek meg a kérésben:
A folyamatot az alábbi ábra foglalja össze:
A következőkben a folyamat elejére koncentrálunk, és azt nézzük meg azt, hogy a böngészőben milyen módon tudunk kérést indítani, és milyen lehetőségeink vannak paraméterek megadására. Az előzőekben kiderült, hogy főleg azt vizsgáljuk, miként tudjuk beállítani az URL <query> részét vagy az üzenettörzset.
HTTP kérést az alábbi módokon tudunk indítani:
Mindegyik esetben az URL megadása szükségszerű, ezzel együtt viszont a <query> is tölthető.
A böngésző címsorát használva vagy hivatkozásra kattintva, a böngésző egy GET kérést indít az URL-ben megadott szerver felé. Ebben az esetben csak az URL <query> része határozható meg. Például egy link esetében ez így néz ki:
<a href="http://server.hu/index.php?adat=ELTE">Valami</a>
A felhasználói interakció és adatmegadás egy másik fő forrását az űrlapok jelentik. Szerveroldali webprogramozás szempontjából a form elem következő attribútumai fontosak:
Az enctype attribútumot ritkán szoktuk beállítani, mivel az alapértelmezett értéke általában megfelelő, mégis néha meg kell határozni az értékét:
Űrlap küldésének lépései a következők:
Az elküldés szempontjából a következőket érdemes az űrlapelemekről ismerni:
Példaképpen tekintsük az alábbi űrlapot:
<form action="http://localhost/cgi-bin/cgi.exe" method="post"> <input type="text" name="alma" value="piros"> <input type="password" value="kek"> <input type="hidden" name="rejtett" value="titkos"> <input type="checkbox" name="check1" value="ertek1" checked> <input type="checkbox" name="check2" checked> <input type="submit"> </form>
Elküldéskor az alábbi kérésszöveg kerül elküldésre:
A password és submit mezőknek nincsen neve, így nem kerülnek az elküldött adatok közé. A második checkboxnak nincsen value attribútuma megadva, így on értékkel kerül elküldésre.
JavaScripttel a fenti folyamatok mind programozottan is elérhetőek a location objektumon keresztül, vagy űrlapok submit metódusával. Ezen kívül programozottan a HTTP fejlécek és mindenféle HTTP metódus megadható, de ezek már nem ennek a tananyagnak a témái.
Kliensoldalon tehát tipikusan kérésszöveget állítunk elő, amely vagy az URL <query> részében, vagy HTTP üzenettörzsben kerül elküldésre. A lehetőségeket az alábbi ábra foglalja össze:
Az adatáramlás utolsó lépéseként azt nézzük meg, hogy a környezeti változókban és a standard bemeneten megjelenő adatokat, tipikusan a kérésszöveget hogyan érhetjük el PHP-ban. Szerencsékre a PHP sok mindenben leveszi a terhet a fejlesztő válláról, és ezeket az adatokat kényelmes formában, szuperglobális asszociatív tömbként készíti elő.
Az URL <query> részében érkező és QUERY_STRING környezeti változóban elérhető kérésszöveg részei a $_GET tömbben érhetőek el, ahol név=érték párosokból a tömb kulcs => érték párosai lesznek.
<?php //kérésszöveg: alma=piros&korte=sarga $_GET['alma']; //"piros" $_GET['korte']; //"sarga" ?>
Az üzenettörzsben érkező és a standard bemeneten elérhető kérésszöveg adatai a $_POST szuperglobális tömb elemeiként érhetőek el.
<?php //kérésszöveg: alma=piros&korte=sarga $_POST['alma']; //"piros" $_POST['korte']; //"sarga" ?>
Az egyéb környezeti változókat pedig a $_SERVER szuperglobális tömbön keresztül lehet kiolvasni.
Ha a folyamat elejét és végét tekintjük csak, akkor azt látjuk, hogy
A kliensen tehát vagy hivatkozások, vagy űrlapok formájában adhatunk meg adatokat szerveroldali feldolgozás céljából. Szerveroldalon ezeket vagy a $_GET vagy a $_POST tömbön keresztül tudjuk elérni. Ennek megfelelően a mostani példák ugyanazt a PHP szkriptet (adat.php) hívják meg, ami nem csinál mást, mint hogy kiírja e két tömb tartalmát:
<?php function kiirTomb($tomb) { echo '<pre>'; print_r($tomb); echo '</pre>'; } kiirTomb($_GET); kiirTomb($_POST); ?>
Első példánkban egy hivatkozásnak adunk meg rögzített paramétereket:
<a href="adat.php?oldal=12&stilus=sotet">Példa hivatkozás</a>
Rákattintva az alábbi képernyő fogad minket. Elvárásainknak megfelelően az URL-ben megadott paraméterek a $_GET tömbből olvashatók ki.
Második példánkban egy űrlapot küldünk el POST metódussal. A dolog specialitását az adja, hogy a meghívandó URL-be ugyancsak rögzített paramétereket csempésztünk:
<form action="adat.php?oldal=42" method="post"> Név: <input type="text" name="nev" value="valaki"> <br> Jelszó: <input type="password" name="jelszo" value="titkos"> <br> <input type="submit" name="gomb" value="Bejelentkezés"> </form>
Az űrlapot elküldve láthatjuk, hogy az űrlapadatok a $_POST tömbben jelennek meg, viszont az URL-ben megadott paraméter is elérhető a $_GET tömbön keresztül.
Természetesen a CGI szabvány nem korlátozódik a PHP használatára. Egy CGI programnak a környezeti változókat és a standard bemenetet kell kezelni a kliensről érkező adatok eléréséhez. A következő C++ nyelven írt példa a fontosabb paramétereket jeleníti meg.
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> using namespace std; int main() { char* request_method; char* content_length_str; char* query_string_get; int content_length; request_method = getenv("REQUEST_METHOD"); content_length_str = getenv("CONTENT_LENGTH"); content_length = atoi(content_length_str); query_string_get = getenv("QUERY_STRING"); string query_string_post = ""; if (strcmp(request_method, "POST") == 0) { int db = 0; char c; while (db < content_length && EOF != (c = fgetc(stdin))) { query_string_post += c; db++; } } cout << "Content-Type: text/plain" << endl; cout << endl; cout << "REQUEST_METHOD = " << request_method << endl; cout << "CONTENT_LENGTH = " << content_length << endl; cout << "QUERY_STRING_GET = " << query_string_get << endl; cout << "QUERY_STRING_POST = " << query_string_post << endl; return 0; }
A tananyag az ELTE - PPKE informatika tananyagfejlesztési projekt (TÁMOP-4.1.2.A/1-11/1-2011-0052) keretében valósult meg.
A tananyag elkészítéséhez az ELTESCORM keretrendszert használtuk.