Kihagyás

10. fejezet

A fejezet anyaga

Sütik

Az előző fejezetben kimondtuk, hogy egy modern webalkalmazásnak képesnek kell lennie a felhasználó tevékenységeinek nyomonkövetésére. Megismerkedtünk a sessionnel, ami egy szerveroldali technológia volt a menetkövetés megvalósítására. Ebben a részben megnézünk egy kliensoldali megoldást is, a böngészősütik (angolul: cookie-k) használatát.

Valószínűleg mindenki találkozott már azzal az érdekes jelenséggel, hogy amikor nem első alkalommal látogatunk meg egy weboldalt, akkor egy korábbi látogatásunkkal kapcsolatos információk jelenhetnek meg előttünk.

Egy hétköznapi példa - Rendeljünk pizzát!

Tegyük fel, hogy éhesek vagyunk, ezért a kedvenc pizzázónk weboldalán rendelünk magunknak egy pizzát. Pár nappal később szintén elhatározzuk, hogy pizzát szeretnénk rendelni ugyaninnen.

A pizzázó weboldalára visszatérve a következőket tapasztalhatjuk:

  • a főoldalon az ajánlatok között megjelenik a pizza, amit korábban rendeltünk, illetve néhány ahhoz hasonló pizza is felajánlásra kerül
  • a kiszállítással és számlázással kapcsolatos űrlapon nem kell minden adatot újból megadni, a rendszer "emlékszik" a korábbi rendelésünk során megadott információkra
  • a legutóbbi látogatásunk során eszközölt felhasználói beállításainkat (pl. világos helyett sötét téma kiválasztását) szintén eltárolta a weboldal.

Felmerülhet bennünk a kérdés, hogy hogyan is emlékszik a korábbi látogatásunk tevékenységeire a weboldal. Ennek egyszerű magyarázata, hogy a webalkalmazás böngészősütiket használ, amelyek segítségével eltárolja egy korábbi látogatás adatait, műveleteit.

A böngészősütik segítségével kliensoldalon, a böngészőben tárolhatunk el kis mennyiségű adatot, így azt nem kell újból megadni a weboldal egy későbbi felkeresése során.

Mivel a sütik kliensoldalon tárolódnak, ezért a felhasználók egyszerűen elérhetik és manipulálhatják őket. Emiatt fontos, hogy a sütiket NEM SZABAD bizalmas adatok (pl. jelszavak) tárolására használni!

Egy másik fontos probléma, amit érdemes észben tartani, hogy a felhasználó letilthatja a weboldalunkon a sütik használatát. Emiatt a sütik használata mellett célszerű lehet egy alternatív megoldást is kidolgozni.

Sütik működése

Az alábbiakban részletesen is megnézzük, hogy hogyan működnek a böngészősütik.

A süti technológia négy komponensből áll:

  1. egy süti fejléc sorból a HTTP válasz üzenetben
  2. egy süti fejléc sorból a HTTP kérés üzenetben
  3. egy süti fájlból a felhasználó rendszerében, amelyet a böngésző menedzsel
  4. egy szerveroldali adatbázisból.

A teljes folyamat a következőképpen zajlik:

  • a kliens egy hagyományos HTTP kérést küld, amit a szerver fogad
  • a szerver készít egy egyedi azonosítót a felhasználónak, és egy, az azonosítóval ellátott belépési pontot az adatbázisban
  • a szerver egy HTTP válasszal reagál egy kérésre, amely ezúttal tartalmaz egy Set-cookie: <azonosító> fejléc sort is
  • a böngésző megkapja a választ, és a rendszerünkben található süti fájlt kiegészíti egy új sorral, amely tartalmazza a szerver címét és a fejléc sorban található azonosítót
  • ezt követően, ha ugyanezen szerver felé indítunk HTTP kérést, akkor a kérésüzenetben megjelenik egy Cookie: <azonosító> fejléc sor, aminek köszönhetően a szerver követni tudja a felhasználó aktivitását a weboldalon.

A fent leírt folyamatot az alábbi ábra szemlélteti.

Sütik működése

Sütik kezelése PHP-ban

Süti létrehozása, elérése

PHP-ban a setcookie() függvénnyel hozhatunk létre böngészősütit. Ennek a fejléce a következőképpen néz ki:

1
2
3
<?php
  setcookie(string $name, string $value="", int $expires=0, string $path="", string $domain="", bool $secure=FALSE, bool $httponly=FALSE);
?>

A paraméterek rendre:

  • $name: a süti neve
  • $value: a süti értéke
  • $expires: a lejárati idő, ami után a süti törlésre kerül
    • értékéül általában UNIX időbélyeget adunk meg
    • jellemzően a time() függvény visszatérési értékéhez (az 1970.01.01. óta eltelt másodpercek számához) hozzáadott másodpercek számával adjuk meg
  • $path: a süti elérési útvonala a szerveren
  • $domain: a tartománynév, amin belül a süti elérhető
  • $secure: csak a biztonságos HTTPS protokollon keresztül legyen-e továbbítható a süti
  • $httponly: csak a HTTP protokollon keresztül legyen-e elérhető a süti
    • ennek köszönhetően a szkriptek (pl. JavaScript) nem férnek hozzá.

A fenti paraméterek közül csak az első, $name paraméter értékét kötelező megadni, a többi paraméter opcionális. Ha egy meglévő sütit módosítani szeretnénk, akkor ezt a setcookie() függvény újbóli meghívásával tehetjük meg egyszerűen.

Mivel a sütik a HTTP üzenetek fejlécében közlekednek, ezért itt is fontos, hogy a setcookie() függvényt bármilyen kimenet (pl. HTML tagek, üres sorok, PHP által generált output) elküldése előtt hívjuk meg, hiszen utólag már nincs lehetőség a fejlécek megadására!

PHP-ban a sütiket a $_COOKIE szuperglobális tömb segítségével érhetjük el.

Példa: Egy süti segítségével megszámoljuk, hogy egy felhasználó hányszor látogatta meg a weboldalunkat

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?php
  $latogatasok = 1;                         // hányszor látogattuk meg a weboldalt eddig

  // ha már van egy, az eddigi látogatások számát tároló sütink, akkor betöltjük annak az értékét
  if (isset($_COOKIE["visits"])) {
    $latogatasok = $_COOKIE["visits"] + 1;  // az eddigi látogatások számát megnöveljük 1-gyel
  }

  // egy "visits" nevű süti a látogatásszám tárolására, amelynek élettartama 30 nap
  setcookie("visits", $latogatasok, time() + (60*60*24*30), "/");
?>
<!DOCTYPE>
<html lang="hu">
  <head>
    <title>Sütik</title>
    <meta charset="UTF-8"/>
  </head>
  <body>
    <?php
      if ($latogatasok > 1) {     // ha már korábban járt a felhasználó a weboldalunkon...
        echo "Üdvözöllek ismét! Ez a(z) $latogatasok. látogatásod.";
      } else {                    // ha első alkalommal látogatja meg a weboldalunkat...
        echo "Üdvözöllek a weboldalamon! Ez az 1. látogatásod.";
      }
    ?>
  </body>
</html>

Miután megtekintjük a fenti kód kimenetét a böngészőben, minden egyes böngészőablak frissítés után 1-gyel megnő a látogatás számláló értéke.

A böngészőkben lehetőségünk van a weboldal által használt sütik információinak megtekintésére. Például a Google Chrome böngészőben az F12 billentyűvel megnyithatjuk a fejlesztői konzolt, majd az Application (Alkalmazás) fülre kattintva a Storage (Tárhely) menüpont alatti Cookies (Sütik) opciót kiválasztva megtekinthetjük az oldal által használt böngészősütik adatait.

Sütik megtekintése Google Chrome alatt

Süti törlése

Ha PHP-ban egy meglévő böngészősütit törölni szeretnénk, akkor a setcookie() függvény meghívásakor a lejárati időt (3. paraméter) egy múltbeli dátumra állítjuk.

Példa: Egy meglévő süti törlése

1
2
3
4
<?php
  setcookie("visits", "", time() - 3600);   // a lejárati időt 1 órával ezelőttre állítjuk
  echo "A 'visits' böngészősüti sikeresen törölve!";
?>
Gyakorló feladat

Feladat

Írj egy egyszerű, 2 lapból álló weboldalt a következő szempontok szerint:

  • A home.php fájlban hozz létre egy űrlapot, amelyen a felhasználó egy lenyíló lista segítségével kiválaszthatja a weboldal nyelvét! Az űrlap feldolgozásakor a kiválasztott nyelvet tárold el egy lang nevű, 1 napos lejárati idővel rendelkező böngészősütiben! Az űrlap alatt jeleníts meg egy szöveget a sütiben tárolt nyelven (ha a süti nem létezik, akkor alapértelmezett módon angolul)! Ez alá helyezz el egy "Delete cookies" feliratú hivatkozást, amely a delete_lang.php állományra navigálja a felhasználót!
  • A delete_lang.php fájlban töröld a lang sütit, majd irányítsd vissza a felhasználót a home.php oldalra!
Egy lehetséges megoldás

A home.php fájl:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<?php
  if (isset($_POST["submit-btn"])) {
    if (isset($_POST["lang"]) && trim($_POST["lang"]) !== "") {
      $lang = $_POST["lang"];                     // az űrlapon megadott nyelv lementése egy változóba
      setcookie("lang", $lang, time() + 86400);   // 1 napos lejárati idővel rendelkező, "lang" nevű süti létrehozása
      header("Location: home.php");               // weboldal újratöltése
    }
  }
?>

<!DOCTYPE html>
<html>
<head>
  <title>Cookies</title>
  <meta charset="UTF-8"/>
</head>
<body>
  <form action="home.php" method="POST">
    <label for="language">Language:</label>
    <select id="language" name="lang">
      <option value="en" <?php if (isset($_COOKIE["lang"]) && $_COOKIE["lang"] === "en") echo "selected"; ?>>English</option>
      <option value="de" <?php if (isset($_COOKIE["lang"]) && $_COOKIE["lang"] === "de") echo "selected"; ?>>Deutsch</option>
      <option value="hu" <?php if (isset($_COOKIE["lang"]) && $_COOKIE["lang"] === "hu") echo "selected"; ?>>Magyar</option>
    </select>
    <input type="submit" name="submit-btn" value="OK">
  </form>

  <?php
    // kiírunk egy szöveget a "lang" süti értékéül beállított nyelven

    if (isset($_COOKIE["lang"]) && $_COOKIE["lang"] === "hu") {
      echo "<h1>Üdvözöllek a weboldalamon!</h1>";
    } elseif (isset($_COOKIE["lang"]) && $_COOKIE["lang"] === "de") {
      echo "<h1>Willkommen auf meiner Seite!</h1>";
    } else {
      echo "<h1>Welcome to my website!</h1>";
    }
  ?>

  <a href="delete_lang.php">Delete cookies</a>
</body>
</html>

A delete_lang.php fájl:

1
2
3
4
<?php
  setcookie("lang", "", time() - 3600);         // a "lang" nevű süti törlése
  header("Location: home.php");                 // átirányítás a home.php oldalra
?>

Fájlfeltöltés

A PHP segítségével egyszerűen tölthetünk fel különféle fájlokat a szerverre egy űrlapról.

A fájlfeltöltésre alkalmas HTML űrlap

Ha azt szeretnénk, hogy egy HTML űrlapon lehetőség legyen fájlok feltöltésére, akkor az alábbi 3 feltételnek kell teljesülnie a HTML kódra vonatkozóan:

  • a <form> tag a method="POST" attribútummal kell, hogy ellátva legyen
  • a <form> tagnek rendelkeznie kell egy enctype="multipart/form-data" attribútummal
  • az űrlapon el kell helyezni egy <input type="file"/> elemet a fájlfeltöltésre.

Az <input type="file"/> űrlapelem accept attribútumával megadhatjuk a feltölteni kívánt fájl elvárt kiterjesztését. Ekkor a fájlok tallózására szolgáló dialógusablakban a felhasználó csak a megadott kiterjesztésű állományokat látja. Ez inkább egy kényelmi funkció, és nem egy megbízható validálási eszköz. A fájl kiterjesztését szerveroldalon is ellenőrizni kell!

Példa: Egy HTML űrlap, fájlfeltöltéssel

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
<!DOCTYPE html>
<html lang="hu">
  <head>
    <title>Fájlfeltöltés</title>
    <meta charset="UTF-8"/>
    <style>
      input { margin-bottom: 10px; }
    </style>
  </head>
  <body>
    <form action="process.php" method="POST" enctype="multipart/form-data">
      <label for="file-upload">Profilkép:</label>
      <!-- Csak képfájlokat szeretnénk engedélyezni a feltöltés során -->
      <input type="file" id="file-upload" name="profile-pic" accept="image/*"/> <br/>
      <input type="submit" name="upload-btn" value="Feltöltés"/>
    </form>
  </body>
</html>

A feltöltött fájlok adatainak elérése PHP-ban

PHP-ban a feltöltött fájlok adatait a $_FILES szuperglobális tömb tartalmazza. Ennek az elemei a feltöltött fájlok adatait tartalmazó asszociatív tömbök, amelyek a következő kulcsokkal rendelkeznek:

  • name: a feltöltött fájl neve
  • tmp_name: ideiglenes név, amivel a fájl feltöltésre kerül (az elérési útvonalat is tartalmazza)
  • size: a feltöltött fájl mérete (bájtokban)
  • type: a feltöltött fájl típusa
  • error: hibakód (0, ha nem történt hiba a fájlfeltöltés során).

Példa: Egy feltöltött fájl adatainak kiíratása PHP-ban

1
2
3
4
5
6
7
8
9
<?php
  if (isset($_FILES["profile-pic"])) {
    echo "A fájl neve: " . $_FILES["profile-pic"]["name"] . "<br/>";
    echo "A fájl ideiglenes neve: " . $_FILES["profile-pic"]["tmp_name"] . "<br/>";
    echo "A fájl mérete (bájtokban): " . $_FILES["profile-pic"]["size"] . "<br/>";
    echo "A fájl típusa: " . $_FILES["profile-pic"]["type"] . "<br/>";
    echo "Hibakód: " . $_FILES["profile-pic"]["error"] . "<br/>";
  }
?>

A fenti kód kimenete egy kép feltöltését követően

A fájl neve: profile.jpg A fájl ideiglenes neve: C:\xampp\tmp\php356.tmp A fájl mérete (bájtokban): 53351 A fájl típusa: image/jpeg Hibakód: 0

Kiterjesztés lekérdezése

PHP-ban a feltöltött fájlok kiterjesztését szerveroldalon is lekérdezhetjük.

Az egyik módszer a kiterjesztés lekérdezésére, hogy beépített függvények használatával lekérjük a fájl nevében az utolsó . (pont) karakter utáni szövegrészt:

  1. Először az explode() függvénnyel feldaraboljuk a fájlnevet pont karakterek mentén. Ez egy tömbben adja vissza a feldarabolás után kapott elemeket.
  2. Az így kapott tömbnek vesszük az utolsó elemét az end() függvénnyel.
  3. Végezetül, hogy a kiterjesztés vizsgálata ne legyen érzékeny a kis- és nagybetűkre, csupa kisbetűssé alakítjuk a kiterjesztést az strtolower() függvénnyel (persze csupa nagybetűssé is alakíthattuk volna az strtoupper() használatával).

Példa: Fájl kiterjesztésének lekérdezése (1. módszer)

1
2
3
4
5
6
7
<?php
  $fajlnev = $_FILES["profile-pic"]["name"];
  $darabok = explode(".", $fajlnev);            // fájlnév feldarabolása pont karakterek mentén
  $kiterjesztes = strtolower(end($darabok));    // a feldarabolás után kapott értékek közül az utolsó lesz a kiterjesztés

  echo "A fájl kiterjesztése: $kiterjesztes <br/>";   // "A fájl kiterjesztése: jpg"
?>

Egy másik módszer a pathinfo($path, PATHINFO_EXTENSION) beépített függvény használata. Itt a $path helyén a feltöltött fájl nevét (name) adjuk meg.

Ahhoz, hogy a fájl kiterjesztése ne legyen kis- és nagybetűérzékeny, itt is érdemes csupa kisbetűssé (vagy ízlés szerint csupa nagybetűssé) alakítani azt.

Példa: Fájl kiterjesztésének lekérdezése (2. módszer)

1
2
3
4
5
6
<?php
  $fajlnev = $_FILES["profile-pic"]["name"];
  $kiterjesztes = strtolower(pathinfo($fajlnev, PATHINFO_EXTENSION));

  echo "A fájl kiterjesztése: $kiterjesztes <br/>";   // "A fájl kiterjesztése: jpg"
?>
Fájl átmozgatása

A feltöltött fájlok a feltöltést követően egy ideiglenes mappába kerülnek. A mi feladatunk, hogy innen átmásoljuk őket egy állandó helyre, különben egy idő után törlődnek.

A feltöltött fájlok átmozgatására a move_uploaded_file($t_name, $dest) beépített függvény szolgál, amivel a $t_name ideiglenes névvel (tmp_name) rendelkező fájlt áthelyezhetjük az ideiglenes mappából a $dest célhelyre. A függvény TRUE-t ad vissza, ha sikerült átmozgatni a fájlt, FALSE-ot egyébként.

Ha a célhelyen létezik már az átmozgatott fájllal megegyező nevű állomány, akkor az alapértelmezett módon felül lesz írva. Amennyiben ezt nem szeretnénk, akkor a file_exists() függvénnyel ellenőrizhetjük, hogy a megadott elérési útvonalú fájl létezik-e.

Példa: Egy feltöltött fájl átmozgatása az images mappába

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<?php
  // összeállítjuk a cél útvonalat: ez most az images/<fájl neve> útvonal lesz
  $cel_utvonal = "images/" . $_FILES["profile-pic"]["name"];

  // megpróbáljuk átmozgatni a fájlt a cél útvonalra (az azonos nevű fájlt ekkor felülírjuk!)
  if (move_uploaded_file($_FILES["profile-pic"]["tmp_name"], $cel_utvonal)) {
    echo "A fájl sikeresen átmozgatásra került!";
  } else {
    echo "Hiba történt a fájl átmozgatása során!";
  }
?>
A teljes fájlfeldolgozást végző PHP program

Írjuk meg a process.php-ban a az anyagrész elején található profilkép feltöltő űrlap feldolgozását az alábbi szempontok alapján!

  • Csak JPG, JPEG és PNG kiterjesztésű fájlokat lehessen feltölteni a szerverre!
  • A feltöltött fájl mérete ne lehessen több 30 MB-nál!
  • Ha a feltöltés sikeres, és a kép megfelel a fenti két elvárásnak, akkor mozgassuk át az images mappába! Amennyiben már létezik ebben a mappában egy ilyen nevű kép, akkor írassunk ki figyelmeztetést az eredeti kép felülírásáról!
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<?php
  if (isset($_FILES["profile-pic"])) {
    // csak JPG, JPEG és PNG kiterjesztésű képeket szeretnénk engedélyezni a feltöltéskor
    $engedelyezett_kiterjesztesek = ["jpg", "jpeg", "png"];

    // a feltöltött fájl kiterjesztésének lekérdezése
    $kiterjesztes = strtolower(pathinfo($_FILES["profile-pic"]["name"], PATHINFO_EXTENSION));

    // ha a fájl kiterjesztése szerepel az engedélyezett kiterjesztések között...
    if (in_array($kiterjesztes, $engedelyezett_kiterjesztesek)) {
      // ha a fájl feltöltése sikeresen megtörtént...
      if ($_FILES["profile-pic"]["error"] === 0) {
        // ha a fájlméret nem nagyobb 30 MB-nál...
        if ($_FILES["profile-pic"]["size"] <= 31457280) {
          // a cél útvonal összeállítása
          $cel = "images/" . $_FILES["profile-pic"]["name"];

          // ha már létezik ilyen nevű fájl a cél útvonalon, figyelmeztetést írunk ki
          if (file_exists($cel)) {
            echo "<strong>Figyelem:</strong> A régebbi fájl felülírásra kerül! <br/>";
          }

          // a fájl átmozgatása a cél útvonalra
          if (move_uploaded_file($_FILES["profile-pic"]["tmp_name"], $cel)) {
            echo "Sikeres fájlfeltöltés! <br/>";
          } else {
            echo "<strong>Hiba:</strong> A fájl átmozgatása nem sikerült! <br/>";
          }
        } else {
          echo "<strong>Hiba:</strong> A fájl mérete túl nagy! <br/>";
        }
      } else {
        echo "<strong>Hiba:</strong> A fájlfeltöltés nem sikerült! <br/>";
      }
    } else {
      echo "<strong>Hiba:</strong> A fájl kiterjesztése nem megfelelő! <br/>";
    }
  }
?>
További gyakorló feladat

Feladat

Egészítsd ki az előző fejezetben megírt webes projektet a következő funkciókkal:

  • A regisztráció során legyen lehetőség profilkép feltöltésére is! A profilkép feltöltése ne legyen kötelező!
  • A "Profilom" oldalon jelenítsd meg a felhasználó profilképét! Amennyiben a felhasználó még nem töltött fel profilképet, akkor egy alapértelmezett kép jelenjen meg!
  • A "Profilom" oldalon legyen lehetőség a profilkép megváltoztatására is!

A felhasználók profilképeit tároljuk egy images mappában! A feltöltött képek neve a felhasznalonev.kiterjesztes formátumot kövesse! Az engedélyezett kiterjesztések: PNG, JPG, JPEG.

Tippek a feladat megoldásához:

  • Mivel két fájltöltésünk is lesz (egy a regisztrációnál, egy pedig a profil oldalon), ezért a fájlfeltöltést végző kódot érdemes kiszervezni a kozos.php állományba egy függvényként.
  • A profilkép módosításakor érdemes először a már meglévő profilképet törölni. Ezt az unlink($path) függvénnyel tehetjük meg (ahol $path a törlendő fájl elérési útvonala).

Megoldás

A feladat egy lehetséges megoldása letölthető ide kattintva.

Felhasznált irodalom

Számítógép Hálózatok gyakorlati jegyzet


Utolsó frissítés: 2021-03-14 16:34:48