A webfejlesztés világában a felhasználói élmény személyre szabása és az állapotok kezelése kulcsfontosságú. Erre a célra az egyik legrégebbi és leggyakrabban használt technológia a süti, vagy angolul cookie. A sütik apró szöveges fájlok, amelyeket a weboldalak helyeznek el a felhasználó böngészőjében, és lehetővé teszik számukra, hogy emlékezzenek bizonyos információkra a látogatások között. Gondoljunk csak a „Maradj bejelentkezve” opcióra, a kosár tartalmára egy webshopban, vagy a felhasználói felület preferenciáira (pl. sötét téma beállítása).
Azonban a sütiknek van egy alapvető korlátjuk: a méretkorlát. Általában egyetlen süti maximum 4KB adatot tárolhat, és domainenként is van egy korlát a sütik számára (általában 20-50 süti). Mi történik akkor, ha több, egymástól független, de egy felhasználóhoz tartozó információt szeretnénk tárolni egy sütiben, anélkül, hogy több külön sütit hoznánk létre, vagy túllépnénk a méretkorlátot? Itt jön képbe a „több változós süti” titka, ami valójában az adatok szerializálását jelenti egyetlen sütibe. A PHP ebben kínál elegáns és hatékony megoldásokat.
A Probléma: Túl sok süti és a HTTP fejléc terhelése
Kezdetben, amikor csak néhány adatot kellett tárolni, a fejlesztők egyszerűen hoztak létre külön sütiket minden egyes változóhoz:
<?php
setcookie('felhasznalo_neve', 'Példa János', time() + 3600);
setcookie('preferalt_nyelv', 'hu', time() + 3600);
setcookie('tema', 'sotet', time() + 3600);
// ... és így tovább
?>
Ez a megközelítés gyorsan problémássá válhat. Minden egyes setcookie()
hívás egy külön sütit eredményez, ami azt jelenti, hogy a böngészőnek minden egyes HTTP kérésnél (oldalbetöltés, képbetöltés, AJAX kérés stb.) el kell küldenie az összes süti nevét és értékét a szervernek. Ez növeli a HTTP kérés fejlécének méretét. Egy túl nagy fejléc lassíthatja az oldalbetöltést, különösen mobilhálózatokon, és akár szerveroldali hibákat is okozhat, ha a fejléc mérete meghalad egy bizonyos korlátot.
A másik probléma a böngészők által domainenként engedélyezett sütik számának korlátozása. Ha túl sok sütit próbálunk beállítani, a régebbi sütik felülíródhatnak, ami adatvesztéshez vezethet. A megoldás az, hogy ne hozzunk létre tucatnyi apró sütit, hanem egyetlen nagy sütit hozzunk létre, ami az összes szükséges információt tartalmazza strukturált formában.
A Megoldás: Adat szerializálás egyetlen sütibe
A „több változós süti” titka abban rejlik, hogy a több adatot nem külön változókként tároljuk, hanem egyetlen adatstruktúrába (pl. tömbbe vagy objektumba) foglaljuk, ezt az adatstruktúrát szöveggé alakítjuk (szerializáljuk), és ezt a szöveget tároljuk a sütiben. Amikor szükségünk van az adatokra, visszalakítjuk őket az eredeti struktúrába (deszerializáljuk). A PHP többféle beépített funkciót is kínál erre a célra.
1. JSON (JavaScript Object Notation)
A JSON egy ember által is olvasható adatcsere formátum. Rendkívül népszerű a webes alkalmazásokban, mert könnyen kezelhető mind PHP-ben, mind JavaScriptben. Ha a süti tartalmát a kliensoldalon (JavaScripttel) is fel kell dolgozni, a JSON a legkézenfekvőbb választás.
Adatok tárolása JSON-ként:
A PHP json_encode()
függvénye egy PHP tömböt vagy objektumot JSON stringgé alakít:
<?php
// A tárolni kívánt adatok egy asszociatív tömbben
$felhasznaloAdatok = [
'azonosito' => 12345,
'nev' => 'Kovács Anna',
'beallitasok' => [
'tema' => 'vilagos',
'nyelv' => 'en',
'ertesitesek' => true
],
'utolso_latogatas' => time()
];
// Adatok JSON stringgé konvertálása
$jsonAdatok = json_encode($felhasznaloAdatok);
// Fontos: ellenőrizzük a méretkorlátot a süti beállítása előtt!
// A 4096 byte (4KB) a maximális méret, de érdemes kicsit alatta maradni a metaadatok miatt.
if (strlen($jsonAdatok) <= 4000) {
// Süti beállítása: 'user_data' a süti neve, $jsonAdatok az értéke
// time() + (86400 * 30) -> 30 napig érvényes
// '/' -> az egész domainre érvényes
// '' -> domain, üresen hagyva az aktuális domainre vonatkozik
// false -> secure flag (csak HTTPS esetén true)
// true -> httponly flag (megakadályozza a JavaScript hozzáférést)
setcookie('user_data', $jsonAdatok, [
'expires' => time() + (86400 * 30),
'path' => '/',
'secure' => false, // Változtasd true-ra, ha HTTPS-t használsz!
'httponly' => true,
'samesite' => 'Lax' // Fontos biztonsági attribútum
]);
echo "<p>A felhasználói adatok sikeresen elmentve JSON formátumban.</p>";
} else {
echo "<p>Hiba: A JSON adatok túl nagyok a süti méretkorlátjához!</p>";
}
?>
Adatok lekérése JSON-ból:
A PHP json_decode()
függvénye a JSON stringet visszaalakítja PHP tömbbé vagy objektummá. A második paraméter (true
) beállításával asszociatív tömbként kapjuk vissza az adatokat, anélkül objektumként.
<?php
// Ellenőrizzük, hogy létezik-e a süti
if (isset($_COOKIE['user_data'])) {
$jsonAdatokString = $_COOKIE['user_data'];
$felhasznaloAdatok = json_decode($jsonAdatokString, true); // true -> asszociatív tömbként
// Fontos: ellenőrizzük, hogy a dekódolás sikeres volt-e!
if ($felhasznaloAdatok !== null && json_last_error() === JSON_ERROR_NONE) {
echo "<h3>Lekért adatok JSON sütiből:</h3>";
echo "<p>Azonosító: " . htmlspecialchars($felhasznaloAdatok['azonosito']) . "</p>";
echo "<p>Név: " . htmlspecialchars($felhasznaloAdatok['nev']) . "</p>";
echo "<p>Téma: " . htmlspecialchars($felhasznaloAdatok['beallitasok']['tema']) . "</p>";
echo "<p>Utolsó látogatás: " . date('Y-m-d H:i:s', $felhasznaloAdatok['utolso_latogatas']) . "</p>";
} else {
echo "<p>Hiba a JSON adatok dekódolása során: " . json_last_error_msg() . "</p>";
}
} else {
echo "<p>'user_data' süti nem található.</p>";
}
?>
2. PHP serialize()
A PHP serialize()
függvénye egy PHP-specifikus módszer bármilyen PHP érték (tömbök, objektumok, primitív típusok) stringgé alakítására, beleértve az objektumokat is, megőrizve azok típusát és szerkezetét. Ez különösen hasznos, ha komplex PHP objektumokat szeretnénk tárolni, és nincs szükség arra, hogy a kliensoldalon (JavaScripttel) is hozzáférjünk az adatokhoz.
Adatok tárolása serialize()-zal:
A serialize()
függvény bármilyen PHP változót képes szöveggé alakítani:
<?php
// Példa egy komplexebb adattípusra: egy stdClass objektum
$felhasznaloSess = new stdClass();
$felhasznaloSess->felhasznaloId = 54321;
$felhasznaloSess->kosarTartalom = [
['termek_id' => 101, 'db' => 2, 'ar' => 1200],
['termek_id' => 205, 'db' => 1, 'ar' => 5000]
];
$felhasznaloSess->elozoOldalak = ['/fooldal', '/termekek/kategoria-a', '/kosar'];
// Adatok szerializálása
$szerializaltAdatok = serialize($felhasznaloSess);
if (strlen($szerializaltAdatok) <= 4000) {
setcookie('user_session_data', $szerializaltAdatok, [
'expires' => time() + (86400 * 7), // 7 nap
'path' => '/',
'secure' => false,
'httponly' => true,
'samesite' => 'Lax'
]);
echo "<p>A felhasználói munkamenet adatai sikeresen elmentve szerializált formátumban.</p>";
} else {
echo "<p>Hiba: A szerializált adatok túl nagyok a süti méretkorlátjához!</p>";
}
?>
Adatok lekérése serialize()-zal:
Az unserialize()
függvény a szerializált stringet visszaalakítja az eredeti PHP változóvá. Fontos, hogy az unserialize()
függvény hívása után ellenőrizzük a visszatérési értékét, mert hiba esetén false
-t ad vissza.
<?php
if (isset($_COOKIE['user_session_data'])) {
$szerializaltString = $_COOKIE['user_session_data'];
$felhasznaloSess = unserialize($szerializaltString);
// Fontos: ellenőrizzük, hogy a deszerializálás sikeres volt-e!
if ($felhasznaloSess !== false) {
echo "<h3>Lekért adatok szerializált sütiből:</h3>";
echo "<p>Felhasználó ID: " . htmlspecialchars($felhasznaloSess->felhasznaloId) . "</p>";
echo "<p>Kosár elemek száma: " . count($felhasznaloSess->kosarTartalom) . "</p>";
echo "<p>Utoljára látott oldalak: " . implode(', ', array_map('htmlspecialchars', $felhasznaloSess->elozoOldalak)) . "</p>";
} else {
echo "<p>Hiba a szerializált adatok deszerializálása során.</p>";
}
} else {
echo "<p>'user_session_data' süti nem található.</p>";
}
?>
JSON vs. serialize() – Melyiket válasszuk?
- JSON:
- Előnyök: Emberi olvasatú, platformfüggetlen, könnyen kezelhető JavaScriptben is, gyakran kompaktabb egyszerű tömbök és objektumok esetén.
- Hátrányok: Nem képes minden PHP adattípust (pl. objektum példányok metódusokkal, erőforrások) maradéktalanul szerializálni, csak az adatokat.
- Mikor használd: Ha a süti tartalmához kliensoldalon is hozzá kell férni, vagy ha az adatok viszonylag egyszerű struktúrájúak (tömbök, stringek, számok, booleanek).
- serialize():
- Előnyök: Bármilyen PHP adattípust (beleértve a komplex objektumokat, erőforrásokat) képes szerializálni és visszaállítani, megőrizve azok típusát.
- Hátrányok: Nem emberi olvasatú, PHP-specifikus (más nyelven nehezen dekódolható), általában hosszabb stringet eredményez, mint a JSON egyszerű adatok esetén.
- Mikor használd: Ha az adatokat kizárólag PHP-ban dolgozod fel, és komplex objektumokat vagy speciális PHP adattípusokat kell tárolnod.
Fontos szempontok és biztonság
1. Süti méretkorlát ellenőrzése
Mindig ellenőrizd a szerializált/JSON string méretét a strlen()
függvénnyel, mielőtt beállítanád a sütit. A 4KB-os (4096 byte) limitet a legtöbb böngésző tiszteletben tartja, de érdemes egy kis puffert hagyni (pl. max. 4000 byte), mivel a süti neve, a lejárati idő és egyéb metaadatok is beleszámítanak a fejléc méretébe.
2. Biztonsági beállítások a setcookie() függvényben
A setcookie()
függvénynek számos paramétere van, amelyek befolyásolják a süti viselkedését és biztonságát:
name
: A süti neve.value
: A szerializált/JSON kódolt adat.expires
: A lejárati idő Unix timestamp formában. Ha 0 vagy elhagyott, a süti a böngésző bezárásakor törlődik.path
: Az URL elérési útja, amelyre a süti érvényes. A'/'
az egész domainre érvényessé teszi.domain
: Az a domain, amelyre a süti érvényes. Üresen hagyva az aktuális domainre vonatkozik.secure
: Hatrue
, a süti csak HTTPS kapcsolaton keresztül küldhető el. Erősen ajánlott beállítani true-ra éles környezetben!httponly
: Hatrue
, a süti nem érhető el JavaScripten keresztül (pl.document.cookie
). Ez kritikus fontosságú az XSS (Cross-Site Scripting) támadások elleni védelemben. Mindig állítsd true-ra, ha nincs rá feltétlenül szükséged JavaScriptből!samesite
: A SameSite attribútum segít megelőzni a CSRF (Cross-Site Request Forgery) támadásokat. Lehet'Lax'
,'Strict'
vagy'None'
. A'Lax'
egy jó alapértelmezett választás a legtöbb felhasználási esetre, ami megengedi a sütik küldését felső szintű navigáció esetén.
A modern PHP verziók (7.3+) lehetővé teszik a setcookie()
paramétereinek tömbként való átadását, ami átláthatóbbá teszi a beállítást, ahogy a fenti példákban is látható.
3. Érzékeny adatok tárolása
SOHA NE tárolj érzékeny felhasználói adatokat (jelszavak, bankkártyaszámok, személyes azonosítók) a sütikben, még titkosítva sem! A sütik a kliensoldalon vannak, és bárki hozzáférhet hozzájuk, ha tudja, hol keresse. Ha ilyen adatokra van szükséged, használd a szerveroldali munkameneteket (PHP $_SESSION
), amelyek biztonságosabbak, mivel az adatok a szerveren maradnak.
Ha mégis muszáj valamilyen nem kritikus, de privát adatot tárolni, fontold meg a szerializált/JSON string titkosítását (pl. openssl_encrypt()
és openssl_decrypt()
) mielőtt beállítod a sütit, de ez is csak egy extra védelmi réteg, nem tökéletes megoldás.
4. Adatok validálása és tisztítása
A sütikből érkező adatokat soha ne tekintsd megbízhatónak! A felhasználó manipulálhatja őket. Mindig validáld és tisztítsd meg a sütiből lekérdezett adatokat, mielőtt felhasználnád őket az alkalmazásodban (pl. htmlspecialchars()
XSS megelőzésére, vagy szigorú validáció számok, dátumok esetén).
Mikor használjunk több változós sütiket?
- Felhasználói felület preferenciák: Téma (sötét/világos), nyelv, oldalelrendezés, elemek láthatósága.
- Ideiglenes kosár tartalom: Nem regisztrált felhasználók kosarának tárolása (kis mennyiségű termék esetén). Regisztrált felhasználóknál adatbázis vagy munkamenet előnyösebb.
- Utoljára látogatott oldalak/termékek: Egy rövid lista az elmúlt böngészési előzményekről.
- A/B tesztelés flag-ek: Melyik verziót látta a felhasználó egy teszt során.
- Egyszerű követő flag-ek: Pl. „első látogatás”, „látta az üdvözlő üzenetet”.
Konklúzió
A több adat egyetlen sütibe való zsúfolása a szerializálás és deszerializálás technikájával egy rendkívül hasznos módszer a PHP webfejlesztésben. Segít optimalizálni a hálózati forgalmat, csökkenti a HTTP kérés fejlécének méretét, és átláthatóbbá teszi az alkalmazás süti-kezelését.
Akár a JSON-t, akár a PHP serialize()
függvényét választod, a kulcs az, hogy tudatosan dönts a felhasználási esettől függően. Mindig vedd figyelembe a süti méretkorlátját, és ami a legfontosabb, fordíts különös figyelmet a biztonsági beállításokra (secure
, httponly
, samesite
). Ezen elvek betartásával hatékonyan és biztonságosan kezelheted a felhasználói adatokat a sütikben, javítva ezzel a weboldalad felhasználói élményét és teljesítményét.
A sütik erőteljes eszközök, de mint minden ilyen eszköz, felelősségteljesen és a legjobb gyakorlatok betartásával kell használni őket.