Ebben a fejezetben a szerveroldali szkriptek kódjának szervezési kérdéseit és eszközeit tekintjük át. Megnézzük a legelemibb jelölésektől kezdve a bonyolultabb fájlszintű szervezést kívánó megoldásokig a lehetőségeket.
Szerveroldali megoldásaink során is valószínűleg sok kis függvény keletkezik. Ahogy ezek száma nő, a kód úgy válhat nehezen áttekinthetővé. A függvények egy része általános célú segédfüggvény (ld. például fájl- vagy adatbázis-használat), mások az adott feladat megoldásához szükségesek. A következőkben azokat a lehetőségeket tekintjük át, amelyek segítenek kódjaink megfelelő elrendezésében.
Érdemes megjegyzésben feltüntetni az adott függvény funkcióját, paramétereit, rövid leírását. Másik lehetőségként érdemes a nagyobb összefüggő blokkokat vízszintes vonallal elválasztani. Az előző fejezetekben erre több példa is látható.
Példaként vegyük a fájlkezelésnél vett filmlistás példát. Új film beszúrásakor a kódot a következőképpen megjegyzésekkel ellátni:
<?php //-------------------------------------------------------- //Fájlkezeléssel kapcsolatos segédfüggvények //Adatszerkezet JSON fájlból betöltése function fajlbol_betolt($fajlnev, $alap = array()) { //... } //Adatszerkezet JSON fájlba mentése function fajlba_ment($fajlnev, $adat) { //... } //-------------------------------------------------------- //Adatfeldolgozás //Új film beszúrása function film_beszur($cim, $rendezo, $ev) { //... } //-------------------------------------------------------- //Kérés feldolgozása (főprogram) $hibak = array(); //... if ($_POST) { //... if (!$hibak) { //... } } //--------------------------------------------------------- //Kimenet ?> <!doctype html> <html> <!-- ... --> </html>
A megjegyzések nagyobb blokkjai jól jelölik a funkcionálisan összetartozó részeit. Általánosságban a következő részek különböztethetők meg:
A funkcionálisan elkülönülő részeket érdemes külön fájlban tárolni a hosszútávú karbantarthatóság és a kód újrafelhasználása érdekében. A fájlokba kiszervezett függvények beemelésére több megoldás is van. Mindegyik úgy működik, mintha a fájl tartalmát a helyükre másolnánk.
Példánkra visszatérve a következő fájlokat érdemes létrehozni:
A kiszervezés után az eddig meghívott ujfilm_fajl.php állomány a következőképpen néz ki:
<?php //Függvények beemelése include('fileio.php'); include('filmadat.php'); //Kérés feldolgozása //... include('ujfilm_sablon.php');
Mivel egy adott kérést feldolgozó szkript a fenti szervezésnek köszönhetően több fájlba esett szét, egy bonyolultabb alkalmazás esetében ez azt jelentené, hogy az alkalmazás könyvtárában az állományok száma nagyon hamar áttekinthetetlen mértékben megnövekszik. Ennek elkerülése érdekében érdemes a fájlokat a szerepük szerinti könyvtárba helyezni. A könyvtárstruktúra egyedi kialakítás kérdése, de a fent vázolt funkcióknak megfelelően az alábbi könyvtárakat különíthetjük el első körben:
Példánkban a főszkript a következőképpen alakul:
<?php //Függvények beemelése include('kozos/fileio.php'); include('adat/filmadat.php'); //Kérés feldolgozása //... include('kimenet/ujfilm_sablon.php');
A több fájlba szétszedéssel azonban megjelennek olyan állományok a fájlrendszerben, amelyek önmagukban nem képesek kéréseket feldolgozni (fenti példánkban ilyen a fileio.php, a filmadat.php és ujfilm_sablon.php). Ezen fájloknál szükség van a közvetlen meghívást elkerülni. A védelmet különböző szinteken lehet megtenni.
Az első lehetőség az, hogy a kérést ténylegesen kiszolgáló szkript elején definiálunk egy tokent. A védendő szkriptek elején pedig ennek a tokennek a jelenlétét vizsgáljuk. Ha ezeket a védett fájlokat közvetlenül hívjuk meg, akkor hiányzik a token, hibaüzenettel leáll a futtatás. Ha főszkriptet hívjuk meg, akkor viszont létezik a token, és lefut a védett fájl tartalma.
A főszkript elején a token definiálása nem más, mint egy konstans létrehozása (példánkban ez az ujfilm_fajl.php állomány):
<?php define('TOKEN', 'Védelem'); //... ?>
A védett fájlok elején pedig a következő sort szükséges elhelyezni:
<?php if ( ! defined('TOKEN')) exit('Közvetlenül nem elérhető!');
Az Apache webszerver egyik modulja lehetőséget ad könyvtár alapú konfigurációk, többek között hozzáférési jogosultságok ellenőrzésére. Ehhez a könyvtárban egy .htaccess nevű állományt kell elhelyezni. Ha már állományaink könyvtárba vannak szervezve, akkor minden könyvtárban egy-egy .htaccess állományt szükséges elhelyezni, amely az illetéktelen kiszolgálástól óv. Tartalma:
A webszerverek csak egy meghatározott mappának és azok alkönyvtárainak elérését engedélyezik kívülről. Ezt a könyvtárat hívják webes gyökérkönyvtárának. A PHP azonban tetszőlegesen használhatja a fájlrendszert. Így adja magát, hogy a kívülről védendő fájlokat tegyük a webes gyökérkönyvtáron kívülre.
A fájlokba való szervezés még nem oldja meg azt a problémát, ha két különböző részfeladat ugyanazt a függvénynevet használja, vagy ugyanolyan nevű globális változókat hoz létre. Eleve a globális változók használata több szempontból kétséges. Jó lenne valamilyen módon egységbe zárni az adott funkcióhoz tartozó adatokat és függvényeket. Erre – ahogy JavaScriptben is – az objektumok szolgálnak. PHP-ban objektumokat a klasszikus objektum-orientáltság elvének megfelelően osztályok példányosításával hozhatunk létre.
A PHP 5-ös verziójától kezdve kifinomult és hatékony nyelvi elemek biztosítják az osztályok kezelését. A PHP-ban szinte minden megtalálható, ami a többi korszerű OOP-s nyelvben is megvan:
Osztályokat a class kulcsszóval vezetjük be. Az osztályon belül adattagokat és metódusokat definiálhatunk. Ezek kívülről való elérhetőségét a public, protected és private kulcsszavakkal szabályozhatjuk. Egy metóduson belül az aktuális objektumra a $this mutat, az adattagokat a -> operátorral érjük el. A példányosításkor a konstruktorfüggvény fut le, ebben lehet az objektum alapértékeit beállítani.
<?php class Gyerek { public $kor; public $nev; public function __construct($nev, $kor) { $this->nev = $nev; $this->kor = $kor; } public function bemutatkozik() { echo "A nevem: {$this->nev}\n"; } public function alszik() { echo "Zzzzzzz....\n"; } } ?>
Az osztály egy példányát a new kulcsszóval tudjuk létrehozni. A példánynak kívülről csak a publikus adattagjait és metódusait érhetjük el.
<?php $zsofi = new Gyerek('Zsófia', 7); $matyi = new Gyerek('Mátyás', 2); $zsofi->bemutatkozik(); $matyi->bemutatkozik(); $zsofi->nev = 'Zsozsó'; $zsofi->bemutatkozik(); ?>
Eredménye:
Az adattagokat érdemes kívülről elrejteni és publikus metódusokon keresztül elérni.
<?php class Gyerek { private $kor; private $nev; public function __construct($nev, $kor) { $this->nev = $nev; $this->kor = $kor; } public function getNev() { return $this->nev; } public function setNev($value) { $this->nev = $value; } public function getKor() { return $this->kor; } public function setKor($value) { $this->kor = $value; } public function bemutatkozik() { echo "A nevem: {$this->nev}\n"; } public function alszik() { echo "Zzzzzzz....\n"; } } ?>
Használata:
<?php $sari = new Gyerek('Sári', 7); $sari->bemutatkozik(); $sari->setNev('Sarah'); $sari->bemutatkozik(); ?>
Eredménye:
A tulajdonságok újrahasznosítását örökléssel érjük el. Erre PHP-ban az extends kulcsszó való. A konstruktorfüggvényben érdemes a szülő konstruktorfüggvényét meghívni.
<?php class Ovodas extends Gyerek { private $jel; public function __construct($nev, $kor, $jel) { parent::__construct($nev, $kor); $this->jel = $jel; } public function getJel() { return $this->jel; } public function setJel($value) { $this->jel = $value; } public function miAJeled() { echo "A jelem: {$this->jel}\n"; } } ?>
Használata:
<?php $zsofi = new Gyerek('Zsófia', 7); $zsofi->bemutatkozik(); $david = new Ovodas('Dávid', 4, 'perec'); $david->bemutatkozik(); $david->miAJeled(); ?>
Eredménye:
Példánkban a sablonokon kívül minden kiemelt függvénycsoportot osztályokba szervezhetünk.
A fájlkezelő segédfüggvényeket érdemes statikus metódusnak felvenni, mivel nem dolgoznak saját adattal. A fileio.php tehát így néz ki:
<?php if ( ! defined('TOKEN')) exit('Közvetlenül nem elérhető!'); class FileIO { public static function fajlbol_betolt($fajlnev, $alap = array()) { $s = @file_get_contents($fajlnev); return ($s === false ? $alap : json_decode($s, true)); } public static function fajlba_ment($fajlnev, $adat) { $s = json_encode($adat); return file_put_contents($fajlnev, $s, LOCK_EX); } }
A filmadatok feldolgozásához kapcsolódó funkciók egy osztályba kerülnek. Azért, hogy elkerüljük a folyamatos betöltést és mentést, az osztály példányosításakor betöltjük egy privát adatmezőbe a filmek listáját, és azzal dolgozunk a további műveletekben. A példány megszűnésekor (destruktor) a tömböt elmentjük fájlba.
<?php if ( ! defined('TOKEN')) exit('Közvetlenül nem elérhető!'); class FilmAdat { private $filmek; private $fajlnev; public function __construct($fajlnev = '') { if (!$fajlnev) { die('Nincs adatfájl!'); } $this->fajlnev = $fajlnev; $this->filmek = FileIO::fajlbol_betolt($this->fajlnev); } public function __destruct() { FileIO::fajlba_ment($this->fajlnev, $this->filmek); } public function osszes_film() { return $this->filmek; } public function film_beszur($cim, $rendezo, $ev) { $this->filmek[] = array( 'cim' => $cim, 'rendezo' => $rendezo, 'ev' => $ev, ); return true; } }
A listázó főszkript (lista_fajl.php) így változik:
<?php define('TOKEN', 'Védelem'); include('kozos/fileio.php'); include('adat/filmadat.php'); $fajlnev = dirname(__FILE__) . '/filmek.json'; $filmadat = new FilmAdat($fajlnev); $filmek = $filmadat->osszes_film(); include('kimenet/lista_sablon.php');
Az új film felvevésekor a filmadat objektumot csak mentéskor példányosítjuk (ujfilm_fajl.php):
<?php define('TOKEN', 'Védelem'); include('kozos/fileio.php'); include('adat/filmadat.php'); //----------------------------------------------------- $fajlnev = dirname(__FILE__) . '/filmek.json'; $hibak = array(); $cim = ''; $rendezo = ''; $ev = ''; if ($_POST) { $cim = $_POST['cim']; $rendezo = $_POST['rendezo']; $ev = $_POST['ev']; if ($cim == '') { $hibak[] = 'Cím kötelező!'; } if ($rendezo == '') { $hibak[] = 'Rendező kötelező!'; } if (!is_numeric($ev) || strlen($ev) != 4) { $hibak[] = 'Rossz évszám!'; } if (!$hibak) { $filmadat = new FilmAdat($fajlnev); if ($filmadat->film_beszur($cim, $rendezo, $ev)) { header('Location: lista_fajl.php'); }; } } include('kimenet/ujfilm_sablon.php');
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.