Vissza az előzőleg látogatott oldalra (nem elérhető funkció)Vissza a modul kezdőlapjáraUgrás a tananyag előző oldaláraUgrás a tananyag következő oldaláraFogalom megjelenítés (nem elérhető funckió)Fogalmak listája (nem elérhető funkció)Oldal nyomtatása (nem elérhető funkció)Oldaltérkép megtekintéseSúgó megtekintése

Tanulási útmutató

Összefoglalás

Ebben a leckében azzal ismerkedünk meg, hogy hogyan lehet adatbázisokat elérni szerveroldali környezetből, illetve milyen fogalmak kapcsolódnak ehhez.

Követelmény

A lecke végére képet kaphatunk arról, hogy hogyan lehet szerveroldali környezetünkből az adatbázisokat elérni, és ismerni kell a PHP és MySQL viszonylatában a megfelelő fogalmakat.

Önállóan megoldható feladatok

Adatbázisok elérése webes alkalmazásokból

Adatbázisok elérése szerveroldali környezetből általában

Manapság egyre több alkalmazás jelenik meg a weben. Ezen alkalmazások szinte mindegyike valamilyen formában adatokkal dolgozik: azokat megjeleníti, de legtöbb esetben módosítja is azokat. Az intenzíven adatokkal dolgozó alkalmazásokat adatvezérelt alkalmazásoknak nevezzük jelezvén, hogy maga az alkalmazás felépítése, logikája és megjelenése alapvetően függ a háttérben tárolt adatok szerkezetétől, jellegétől. Ezekben az esetekben a webes megjelenés gyakorlatilag egy kényelmes interfészt biztosít a háttéradatok felhasználóbarát kezeléséhez. Az adatok tárolásának egyik leggyakoribb módja adatbázis-kezelő rendszerekkel történik.

Éppen ezért szinte elkerülhetetlen, hogy egy korszerű webes alkalmazás ne dolgozzon adatokkal, adatbázisokkal, adatbázis-kezelő rendszerekkel. Egy webprogramozónak tehát ismernie kell az ezekhez kapcsolódó technológiákat.

Az adatbázisokhoz való kapcsolódás folyamata igazából független a választott szerveroldali technológiától. Minden esetben az alábbi lépések követik egymást.

Vissza a tartalomjegyzékhez

Biztonsági kérdések

Konkrét szerveroldali technológia választása nélkül beszélhetünk az adatbázisokkal operáló webes alkalmazások biztonságáról. Ez a témakör nagyon sokrétű, hiszen nemcsak programozási, hanem adatbázis-adminisztrációs, üzemeltetési, munkaszervezési vetületei vannak. Az adatbázis kialakításánál általában a tulajdonosnak vagy az adminisztrátornak van hozzáférése az adatbázishoz és a táblához. Ügyeljünk arra, hogy programunkból ne az ő nevükben kapcsolódjunk az adatbázishoz, hanem alakítsunk saját felhasználót vagy speciális felhasználói csoportokat, amelyeknél egyenként tudjuk meghatározni az adatbázishoz és a táblákhoz való hozzáférési jogosultságokat. Ezek minél finomabb megadása nagymértékben csökkenti az illetéktelen vagy véletlen műveletek esélyét. Adatbázis oldalon is használjunk magasabb szintű elemeket, mint a nézetek, tárolt eljárások, triggerek.

Mivel a hálózati forgalom lehallgatható, ezért érdemes nemcsak a böngésző (kliens) és webszerver közötti csatornát, de a webszerver és adatbázis-szerver közötti csatornát is titkosítani.

A legnagyobb hibák azonban az SQL-jeink összeállításánál adódhatnak. Nagyon gyakori ugyanis, hogy egy űrlapon bevitt mező szolgál paraméterként az adatbázis felé kiadott SQL utasításunkban. Éppen ezért kritikus lehet az a mód, ahogy SQL-ünket állítjuk össze. Alapvetően kétféle módszer létezik paraméteres SQL utasítások írására. Az egyik a szövegösszefűzés. Mivel az SQL utasítás egy szöveges információ, így a végeredményt előállíthatjuk szövegműveletek segítségével.

Ennek nagyon nagy hátránya az, hogy a paraméterként szolgáló változó igen sokféle értéket tartalmazhat. Lehet benne például nem engedélyezett, speciális jelentéssel bíró karakter (pl. szöveget lezáró aposztróf), érkezhet az elvárttól más típusú érték, legrosszabb esetben pedig jöhetnek benne további SQL utasítások (SQL beszúrás, SQL injection). Ez utóbbiak is tipikusan a speciális jelentéssel bíró karaktereket használják ki. A végeredmény sokszor adatbázisoldali hibához vagy adatok illetéktelen hozzáféréséhez vezet.

Az alábbi példában egy beléptető oldal utáni lekérdezés látható, amelyben szeretnénk megnézni, hogy a megadott felhasználónév és jelszó hány találatot eredményez a users táblánkban:

<?php
$sql = "select count(1) from users where userid='" . $user . "' and passwd='" . $pwd . "';";
//vagy
$sql = "select count(1) from users where userid='{$user}' and passwd='{$pwd}';";
?>

Ha a felületről “megfelelő” adatok érkeznek, pl. $user="probauser" és $pwd="jelszo", akkor a futtatandó lekérdezés a következő: "select count(1) from users where userid='probauser' and passwd='jelszo';". Ha azonban $user="" és $pwd="' or '1'='1", akkor a lekérdezés így alakul: "select count(1) from users where userid='' and passwd='' or '1'='1';". A jelszó megadást ezzel kikerültük, a lekérdezés nem üres sorral tér vissza, így a szkript továbbengedi a rosszindulatú támadót mint azonosított felhasználót.

Ennek elkerülése érdekében sosem szabad ellenőrizetlenül felhasználni a klienstől érkező értékeket. Minden esetben meg kell vizsgálni a tartalmát, és csak olyan karaktereket szabad engedélyezni, amely az adott környezetben elfogadható. Továbbá ha lehet, expliciten konvertáljuk a megfelelő típusra az adatokat. Ebben számos PHP nyelvi elem és függvény van a segítségünkre (típuskényszerítés, settype() függvény). MySQL adatbázis használata esetén a mysql_real_escape_string() függvény használata javasolt, mely minden MySQL specifikus karaktert kiescape-el. Az explicit szövegösszefűzés helyett használjuk az sprintf() függvényt, amelynek első paramétereként egy paraméteres szövegsablont, további paramétereiként pedig a szövegsablonba illeszkedő kifejezéseket adhatjuk meg. A sablonban jelölhetjük, hogy az adott helyen szöveget vagy számot szeretnénk megjeleníteni. Ennek használata is csökkenti a típusból eredő hibák előfordulását.

A szövegösszefűzés mellett egy másik lehetőség az ún. adatkötés használata (binding). Ebben az esetben az SQL utasításunkban nem konkrét értéket, hanem csak egy paramétert tüntetünk fel. Ezt a sablont magával az adatbázis-kezelővel dolgoztatjuk fel, készíttetjük fel végrehajtásra. A konkrét végrehajtáskor pedig meg kell adni, hogy az egyes paramétereknek mi az értéke. Mivel itt egy dedikált erőforrást kapunk az adatok átadására, így ezt megtehetjük típusosan, és elkerülhetjük a speciális karakterekből fakadó hibákat. PHP és MySQL esetében az adatkötést az előkészített SQL utasítások használatával lehet elérni (ld. később).

Tárolt eljárások használatával (természetesen adatkötve a paraméterekez) tovább bővíthetjük az adatbázis oldali biztonságunkat. Ezek által a kérések logolhatók egy külön táblába, az adatbázis több funkciójához férünk hozzá, összetett kifejezések optimalizációja is elérhető.

Vissza a tartalomjegyzékhez

Általános fogalmak az adatbázisszerver és -kliens kapcsolatában

Konkrét adatbázisszerver és alkalmazáslogikai megoldás választása nélkül beszélhetünk néhány olyan fogalomról, amely az adatbázis elérésével kapcsolatos.

Állandó adatbázis-kapcsolatok

Az üzleti logika végrehajtása során a kliens nyit egy adatbázis-kapcsolatot, használja azt, majd bezárja. A kapcsolat megnyitása és karbantartása minden felhasználó vagy szkript számára nagyon idő- és erőforrásigényes folyamat, különösen igaz ez egy adatvezérelt webes alkalmazásban, ahol gyakran kérésenként kell ezt végrehajtani. Ha az adatbázis-kezelő támogatja, akkor ehelyett lehetőség van állandó adatbázis-kapcsolatok kialakítására. Ebben az esetben a kapcsolat az adatbázissal a folyamat végén nem zárul be, hanem egy átmeneti tárolóba kerül, a következő kapcsolódásnál innen kerül újra felhasználásra időt és erőforrást spórolva meg azzal, hogy nem kell felépítenie a kapcsolatot az adatbázissal. A kapcsolat újrahasznosítása általában bizonyos kapcsolati paraméterek alapján történik: a szervernek, portnak, felhasználónak, kiválasztott adatbázisnak meg kell egyeznie az átmeneti tárolóban lévőkkel. Ha minden kapcsolat foglalt, akkor új kapcsolat nyílik, és az is az átmeneti tárolóba kerül. Az átmeneti tárolót kapcsolati tárolónak, angolul connection pool-nak nevezik. Az átmeneti tároló az üzleti réteg számára láthatatlan. Így annak kódjában továbbra is nyitni kell a kapcsolatot és zárni, legfeljebb a kapcsolatot a kapcsolati tárolóból kapja meg, illetve annak adja vissza.

Előkészített utasítások

Az előkészített vagy paraméteres utasítások alternatívát jelentenek a nyers, szöveges SQL kód írására. Az előkészített utasítások alapvetően olyan sablonok, amelyekben a beillesztendő értékek helyét paraméterekkel (helyettesítő karakterrel) jelöljük. Az előkészített utasításokat az adatbázis-kezelővel előzetesen fel kell dolgoztatni, melynek során a paraméterek helyét típusosan előkészíti, majd ezeket a paramétereket más és más értékekkel újra és újra meg lehet hívni.

Az előkészített utasítások használata legalább két lépésből áll. Először a paraméteres SQL utasításunkat előkészíttetjük az adatbázisszerverrel, majd az így előállt paraméterekhez értékeket rendelünk. Lekérdezéseknél a futtatás után harmadik lépésként az adatok kinyerésekor lehetőség van az eredmények változókhoz kötésére is.

Az előkészített utasítások használatának van előnye és hátránya is. Előnyei:

Hátrányai:

A biztonság, a hatékonyság és az átláthatóbb kód miatt megéri az adatkötés használata.

Pufferelt lekérdezések

Végül még egy fogalommal szükséges megismerkednünk a konkrét környezet áttekintése előtt. Lekérdezéskor két lehetőségünk van az eredményhalmaz tárolására. Az egyik esetben a lekérdezés eredményét az adatbázisszerver tárolja, és a kliens (pl. a PHP) soronként kérheti le az eredményeket. A kliens szempontjából ezt nem pufferelt lekérdezésnek hívják. Ez a módszer az adatbázisszervertől kíván nagyobb erőforrást, kliens oldalon viszont kevés memóriára van szükség. A pufferelt lekérdezés ennek épp a fordítottja. Ebben az esetben a kliens lekéri az egész eredményhalmazt az adatbázisból, eltárolja, és mikor az egész megérkezik, akkor kezdődhet a feldolgozás. Bár kliens oldalon nagyobb erőforrásigény jelentkezik ebben az esetben, mivel az adatok kliens oldalon vannak, sokkal rugalmasabb feldolgozási módok közül választhatunk. Főleg feladat és hardverkonfiguráció kérdése, hogy melyiket választjuk.

Vissza a tartalomjegyzékhez

PHP és MySQL

Bővítmények és könyvtárak

Az általános tudnivalóknál említettük, hogy az adatbázis elérése erősen függ a választott szerverkörnyezettől. A továbbiakban tehát nézzük meg részleteiben ezt a kapcsolatot a tananyagban konkrét fejlesztési környezetnek választott PHP és MySQL esetében.

PHP-ban is különböző függvénycsoportok állnak rendelkezésre a különböző típusú adatbázis-kezelő rendszerek kezelésére. Ezek használatát a PHP telepítésekor kell jeleznünk vagy azáltal, hogy a megfelelő bővítményt egyszerűen engedélyezzük a php.ini fájlban, vagy fordításkor kell az adott bővítményt belefordíttatnunk a PHP-ba. A PHP nagyon sokféle adatbázis-kezelővel képes együtt dolgozni. Néhány a gyakrabban használtak közül:

PHP-ból a MySQL adatbázis-kezelőt manapság háromféle bővítményen, alkalmazásprogramozási felületen (API-n) keresztül lehet elérni. Ezek a

Történetileg haladva eleinte a mysql bővítmény létezett. A PHP és MySQL futtatókörnyezet szoros viszonyát jelzi, hogy ekkor még a PHP integráltan tartalmazta a MySQL adatbázis-kezelőhöz tartozó függvényeket. (A PHP5 megjelenésével azonban ezek is külön modulban érhetőek el, külön szükséges őket engedélyezni.) A mysql bővítmény a PHP 2-es verziójától felfelé érhető el, és mind a mai napig használható. Kompatibilis a MySQL 4.1-es verziójától felfelé. Bár az alapvető adatbázis-műveletek rajta keresztül gond nélkül elvégezhetőek, hiányoznak belőle azok a funkciók, amelyek kihasználják az újabb MySQL verziók további újdonságait, vagy biztonságosabb kapcsolódást tesznek lehetővé. A korszerű programozási mintáknak is nehezen tesz eleget azáltal, hogy csak procedurális felületet ad a függvények hívására, az objektum-orientált programozási mintákat nem támogatja. Gyakorlatilag új projektekhez ennek használata nem ajánlott.

A mysql bővítményben lévő hiányosságok orvoslására jelent meg a mysqli interfész, mely a fent felsorolt problémákat orvosolja. A PHP 5-ös verziójától érhető el, és a MySQL adatbázis-kezelőt a 4.1-es verziótól felfelé támogatja annak minden korszerű funkciójával együtt, pl. támogatja az előkészített utasításokat, tárolt eljárásokat, a procedurális programozói felület mellett objektum-orientált programozást is biztosít. Ha tehetjük, akkor manapság ennek az interfésznek a használata ajánlott. (http://developers.sun.com/databases/articles/mysql_php1.html)

Ugyancsak a PHP 5-ös verziójában mutatták be a PHP Data Objects (PDO) nevű interfészt, amely egy adatbázis-elérési absztrakciós réteget biztosít: egy olyan programozói felületet, mely utasításkészletében nem függ a mögötte álló adatbázis-kezelőtől. Vele lehetőség nyílik olyan alkalmazások írására, mely mögött az adatbázis-kezelő változhat. A PDO önmagában csak egy egységes programozói felületet biztosít, a MySQL eléréséhez a PDO_MYSQL vezérlő használata szükséges. Ez forgatja át az absztrakt utasításokat konkrét MySQL-specifikus hívásokká. A PDO előnye a hordozhatósága, és az egységes és egyszerű programozói felület, hátránya az, hogy nem használhatja ki az egyes adatbázis-kezelők és azok speciális függvényei által nyújtott többletszolgáltatásokat, így például MySQL esetében a többszörös lekérdezéseket, stb. A PDO-ról későbbi fejezetekben még bővebben lesz szó.

A fenti három bővítmény biztosítja azt a programozói felületet, amelyen keresztül az adatbázis elérhető. Az ezekben található függvények azonban nem közvetlenül érik el az adatbázis-kezelőt, hanem egy függvénykönyvtáron keresztül (library). Ezek biztosítják az alacsony szintű kommunikációt a MySQL szerverrel. Kétféle függvénykönyvtár létezik, a libmysql és a mysqlnd. Történetileg először a libmysql (MySQL client library) könyvtár létezett. Ez nem a PHP része, hanem a PHP fordításához szükséges külön könyvtár, amely azt is jelenti, hogy ha a PHP és a MySQL nem ugyanazon a szerveren helyezkedik el, akkor a webszerverre egy MySQL kliens telepítése volt szükséges, hogy a PHP ennek a függvénykönyvtárán keresztül érhesse el a MySQL szervert. A PHP5-ös verziójában egy újabb függvénykönyvtárat vezettek be, amely megszünteti ezt a függőséget, és így a PHP fordítható MySQL kliens nélkül is. Ez lett a mysqlnd, azaz MySQL Native Driver. A kétféle könyvtárat összehasonlítva azt lehet mondani, hogy teljesítményük közel azonos, a támogatott funkciók részben eltérnek (vannak függvények, amelyek csak mysqlnd-ben érhetők el), valamint a mysqlnd bővebb statisztikai adatokat szolgáltat a teljesítményről, amely a performancia javításánál jöhet jól. Az ígéretes továbbfejlesztési lehetőségek miatt mindenképpen ez utóbbi használata javasolt (ha tehetjük).

A függvénykönyvtárakról nem is érdemes többet tudnunk, hiszen a programozás során ezekkel nem találkozunk, egyelőre elég annyi, hogy egy korszerű fejlesztői környezetben a mysqlnd-re épülő mysqli bővítmény használata a tanácsos, vagy PDO használata ugyancsak mysqlnd-vel.

A következő fejezetekben azt nézzük meg, hogy az adatbázis és kliens kapcsolatában már az általános résznél említett fogalmak hogyan valósulnak meg PHP és MySQL esetén.

Állandó adatbázis kapcsolatok

A MySQL adatbázis-kezelő is támogatja a kapcsolatok átmeneti tárolását, és ezt PHP-ban is kihasználhatjuk. Minden PHP folyamat saját kapcsolati tárolót használ. A PHP telepítésének jellegétől függ, hogy így ki tudja-e használni a kapcsolati tárolót. Ha a PHP CGI szkriptként fut, akkor minden kéréskor új PHP folyamat indul a szerveren, így új kapcsolati tároló jön létre, viszont a kérés befejeztével a kapcsolati tároló is megszűnik. Ebben az esetben tehát nincs különbség az állandó és a normális adatbázis-kapcsolat között. Ha viszont a PHP modulként van feltelepítve, akkor a PHP egy folyamatként fut, és a kérések csak újabb gyerekfolyamatokat, ún. szálakat indítanak el. Ekkor már több kérés osztozhat a tárolt adatbázis-kapcsolatokon.

Az állandó kapcsolatok használatát a php.ini fájlban kell jelezni a mysqli.allow_persistent PHP direktíva beállításával. Itt még egyéb, ezzel kapcsolatos paraméterek is beállíthatók (ld. a dokumentációt). A kapcsolódás a mysqli::connect() paranccsal történik.

Előkészített utasítások

A mysqli bővítménynél említettük, hogy ezen keresztül lehetőségünk van az ún. előkészített utasítások használatára, valamint a biztonság témakörnél is kiemeltük, hogy ezek segítségével tudjuk a sokkal biztonságosabb adatkötést megvalósítani PHP és MySQL esetében, használatuk tehát mindenféleképpen javasolt.

Előkészített utasítások használatának a lépései megegyeznek az általános bemutatásnál írtakkal. A paraméteres SQL utasítást szövegesen elő kell állítanunk. A paraméterek jelölését vagy a ? karakter jelzi, vagy nevesített jelölőket használunk, ahol kettőspont után kell megadni a paraméter nevét. Egy tipikus előkészített lekérdezés a következőképpen néz ki (vö. a biztonság témakörnél összeállított lekérdezéssel):

<?php
$sql = "select count(1) from users where userid=? and passwd=?;";
//vagy
$sql = "select count(1) from users where userid=:user and passwd=:pwd;";
?>

Az így előállt utasítássablont első lépésként el kell küldeni a MySQL szervernek (mysqli::prepare). A szerver megkapja, és validálja a sablont, előkészíti, és eltárolja egy speciális pufferben. Majd visszaad egy speciális értéket, amin keresztül az előkészített utasítást kezelni lehet. Második lépésként a paraméterekhez értékeket kell hozzárendelni (mysqli_stmt::bind_param), végül a lekérdezést végrehajtjuk (mysqli_stmt::execute).

A fentiek előnye, hogy ha egy lekérdezésben csak az adatok változnak, akkor nem közlekedik minden kéréskor az egész SQL utasítás, csupán az adatok. Ráadásul az SQL utasítás feldolgozásának legerőforrásigényesebb feladata, a validálás és előzetes feldolgozás is csak egyszer kell, hogy megtörténjen. Az adatokat nem kell a mysql_real_escape_string() függvénnyel megvédeni az SQL befecskendezéstől, mert ezt a kliens és a szerver kommunikációja során automatikusan elvégzi.

Az eredmény feldolgozásakor adatkötött eredmények (mysqli_stmt::bind_result) esetén az eredményhalmaz egyes mezőit köthetjük változókhoz. Minden egyes sor lekérésekor a megfelelő mezőket automatikusan a kötött változókon keresztül érhetjük el.

Pufferelt lekérdezések

A PHP és MySQL kapcsolatában is lehetőség van a pufferelt vagy nem pufferelt lekérdezésekre. Amit az általános részen kívül itt megemlíthetünk, hogy mindegyik bővítmény lehetővé teszi mindkettőnek a használatát.

A mysqli bővítmény osztályai

A mysqli bővítményben a gyakran használt utasítások három különböző funkciójú osztályba vannak sorolva. A műveleteket a mysqli osztály példányán kezdjük el végrehajtani. Lekérdezéskor az eredményhalmaz sorai a mysqli_result osztály példányán keresztül érhetőek el. Előkészített utasítások esetén az előkészítés során (mysqli::prepare()) a mysqli_stmt osztály egy példányát kapjuk vissza, amin keresztül végrehajtjuk a lekérdezést, és ugyanezen keresztül kérdezzük le az eredményt. A mysqli_stmt::get_result() függvénnyel mysqli_result példányt kaphatunk meg, ám ez csak mysqlnd könyvtárt használva működik. Összességében tehát az alábbi négyféle osztálysorrenddel találkozhatunk adatbázis-műveletek során (vö. a következő fejezet tipikus utasításcsoportjaival):

Vissza a tartalomjegyzékhez

Fel a lap tetejére
Új Széchenyi terv
A projekt az Európai Unió támogatásával, az Európai Szociális Alap társfinanszirozásával valósul meg.