Uvod
Nedugo posle početnog upoznavanja sa web dizajnom i backend programiranjem, programeri se prirodno susretnu sa potrebom za lokalnim skladištenjem podataka koje browseri koriste za prikaz sajtova.
Od browsera se očekuje da "upamte" da je korisnik prijavljen na određeni sajt, da sačuvaju prethodni sadržaj korpe pri online kupovini, boju pozadine i veličinu fonta koju je korisnik izabrao, kao i mnogo šta još.
Ovakve stvari se svakako podrazumevaju (godinama unazad), ali - ne dešavaju se "same od sebe".
Da bi sajtovi koje kreiramo mogli da koriste opcije koje smo prethodno naveli (kao i druge srodne opcije), potrebno je da im omogućimo da podatke smeštaju na računarima klijenta, preko kukija i lokalnih skladišta podataka.
Razlike između HTTP kukija i lokalnog skladištenja
Verujemo da je čitaocima pojam HTTP "kolačića" ili kukija (engl. - cookie) dobro poznat iz svakodnevne prakse i u pitanju je način za lokalno skladištenje podataka koje posećeni sajtovi čuvaju za svoje potrebe.
Sam po sebi, kuki predstavlja nisku (string, tekstualni podatak) koja se kreira na serveru, šalje browseru i skladišti lokalno, međutim, u daljoj obradi podataka, kuki postaje deo HTTP zahteva koji se šalju serveru (prosto rečeno, svaki put kada se sajt obraća serveru, šalju se i kukiji koje je sajt prethodno od servera primio).
Iako su (u očima mnogih pojedinaca) kukiji sinonim za lokalno skladištenje podataka o sajtovima u opštem smislu, vidimo da postoji komunikacija sa serverom, videćemo i da procedura čitanja i zapisa kukija nije skroz trivijalna, tako da - kada se sve uzme u obzir - rad sa kukijima već na prvi pogled deluje relativno "zvanično".
Za "običnije" zahteve - možemo koristiti lokalna skladišta browsera kojima se može pristupiti preko Javascript-a (pri čemu nema komunikacije sa serverom).
Operacije sa lokalnim skladištem browsera preko Javascript-a
Za sam početak, upoznaćemo se sa korišćenjem lokalnih skladišta na računaru klijenta, za šta se u Javascript-u koriste objekti sessionStorage
i localStorage
.
Pomenuta dva objekta imaju mnogo sličnosti i gotovo identičnu sintaksu za pozivanje komandi, ali naravno postoje razlike (i razlozi zašto ćemo u različitim okolnostima koristiti jednu ili drugu opciju):
sessionStorage
- podaci su vezani za sesiju i brišu se pri zatvaranju browseralocalStorage
- podaci ostaju trajno zapisani (odnosno, dok se ne obrišu ručno)
Objekat sessionStorage
možemo koristiti za (recimo) pamćenje sadržaja tekstualnih polja u situacijama kada ne želimo da se posle osvežavanja stranice sadržaj izgubi, dok localStorage
možemo koristiti za pamćenje veličina fonta, boja elemenata koje je korisnik izabrao (i slično).
Skladištenje podataka
Skladištenje (upis) podataka obavlja se pozivom komande setItem
, kojoj se kao argumenti predaju ključ (identifikator preko koga ćemo nadalje obraćati podatku) i vrednost.
// Za upis u skladište koje je vezano za sesiju:
sessionStorage.setItem("boja_pozadine", "#2244ee");
// Za upis u lokalno skladište browsera:
localStorage.setItem("boja_pozadine", "#2244ee");
Čitanje podataka
Za čitanje podatka koji je već upisan, koristi se funkcija getItem
, kojoj se kao argument predaje ključ nekog od podataka:
// Za čitanje podataka iz skladišta koje je vezano za sesiju:
var bojaPozadine = sessionStorage.getItem("boja_pozadine");
// Za čitanje podataka iz lokalnog skladišta browsera:
var bojPozadine = localStorage.getItem("boja_pozadine");
// U oba slučaja, promenljiva bojaPozadine
// dobija vrednost "#2244ee"
Uklanjanje podataka
Za uklanjanje podataka, koristi se funkcija removeItem
(uz predavanje ključa) - ukoliko želimo da uklonimo određeni (pojedinačni) podatak, ili clearAll
- ukoliko želimo da ukonimo sve podatke iz lokalnog skladišta.
// Za uklanjanje podataka iz skladišta koje je vezano za sesiju:
sessionStorage.removeItem("boja_pozadine");
// Za čitanje podataka iz lokalnog skladišta browsera:
localStorage.removeItem("boja_pozadine");
// U oba slučaja, podatak sa ključem
// bojaPozadine biće uklonjen.
// Ukoliko želimo da ispraznimo skladišta
// podataka u potpunosti, koristićemo sledeći kod:
sessionStorage.clearAll();
// ... ili ....
localStorage.clearAll();
Podaci smešteni u sessionStorage
ističu po zatvaranju browsera, dok oni smešteni u localStorage
(osim ako se sami za to ne "pobrinemo") ne ističu nikad.
Rad sa kukijima je nešto složeniji.
Pored ključeva i vrednosti, možemo (ili, u određenim okolnostima, pogotovo kada nas na to navode sigurnosni razlozi - moramo), navesti i trenutak isteka, kao i to da li je u pitanju kuki koji se može čitati samo na serveru (a takođe navodimo i domen i putanju za koje kuki važi).
U nastavku, pozabavićemo se obradom kukija u PHP-u i Javascript-u.
Operacije sa kukijima u PHP-u
PHP, kao serverski jezik, nudi dobru podršku za kukije i raspolaže svim neophodnim funkcijama za manipulaciju ovim resursima.
Krenimo redom ...
Kreiranje i ažuriranje kukija
Za kreiranje kukija preko PHP-a, koristi se funkcija setcookie
:
<?php
setcookie(naziv, vrednost, trenutak_isteka, domen);
?>
Da bismo bolje razumeli šta navedeni argumenti označavaju, pogledaćemo primer (objašnjenja u komentarima):
<?php
$boja_pozadine = "#44ee11";
setcookie("boja_pozadine", $boja_pozadine, time() + 86400 * 30, "/");
// Ako želimo, možemo to uraditi i ovako:
//
// $boja_pozadine = "#44ee11";
// $naziv_kukija = "boja_pozadine";
// setcookie($naziv_kukija, $boja_pozadine, time() + 86400 * 30, "/");
//
// ARGUMENTI SU:
//
// naziv: "boja_pozadine" - naziv po kome ćemo (nadalje)
// prepoznavati dati kuki
//
// vrednost: $boja_pozadine - vrednost koju upisujemo u kuki
// (najvažniji argument) biće vrednost promenljive
// $boja_pozadine ("#44ee11")
//
// trenutak_isteka: time() + 86400 * 30 - označava trenutak u kome
// kuki ističe i zadaje se u sekundama;
// Preko funkcije time(), dobijamo UNIX Timestamp
// koji odgovara trenutnom vremenu,
// dok je 86400 broj sekundi u jednom danu:
// 86400 = 24 * 60 * 60
// .... što znači da naš kuki ističe za 30 dana
// od trenutka kada pozivamo funkciju setcookie()
//
// domen: "/" - Ovako deklarisan kuki važiti za ceo domen
//
?>
Za ažuriranje kukija (na primer, ako korisnik promeni boju pozadine), jednostavno ćemo ponovo pozvati funkciju setcookie
i predati druge vrednosti:
<?php
$boja_pozadine = "#332aff";
setcookie("boja_pozadine", $boja_pozadine, time() + 86400 * 30, "/");
// Sve je "isto", ali, ovoga puta predajemo vrednost:
// "#332aff"
?>
Sada je kuki dostupan širom domena (svim stranicama sajta) i možemo mu lako pristupiti.
Pristup kukijima
Za pristup kukijima, koristićemo superglobalnu promenljivu $_COOKIE
, sa odgovarajućim indeksima koji (kao što verovatno pretpostavljate) odgovaraju nazivima kukija koje smo zadavali kada smo pozivali funkciju setcookie
.
Naravno, da bi sve imalo pravog smisla, pre nego što pristupimo kukiju, prvo treba proveriti da li kuki (uopšte) postoji, za šta možemo koristiti sada već dobro poznatu funkciju isset
:
<?php
// Podrazumevana boja pozadine je bela:
$boja_pozadine = "#fff";
$poruka = "";
if (isset($_COOKIE['boja_pozadine'])) {
$boja_pozadine = $_COOKIE['boja_pozadine'];
}
echo "<font color='" . $boja_pozadine . "'>Ispis</font>";
?>
Uklanjanje kukija
Kukiji se uklanjaju na jednostavan (ali pomalo idiosinkratičan) način - pozivanjem funkcije setcookie
, pri čemu se za trenutak isteka zadaje trenutak u prošlosti:
<?php
setcookie("boja_pozadine", "", 1, "/");
// naziv: "boja_pozadine" - Preko prvog argumenta,
// određujemo koji je kuki predviđen za brisanje
//
// vrednost: "" - Prilično je svejedno, ali, pri brisanju
// kukija tipično se zadaje vrednost koja dodatno
// sugeriše da je u pitanju uklanjanje
//
// trenutak_isteka: 1 - UNIX Timestamp za 20.03.2021 u 10h
// (vreme je ovaj članak kreiran), koji smo dobili
// preko funkcije time(), ima vrednost 1616230800,
// što veoma jasno pokazuje da timestamp sa
// vrednošću 1 označava "trenutak u prošlosti"
//
// domen: Kuki važi za ceo domen
?>
Za kraj, ostaje nešto što ćemo u praksi raditi na samom početku - da proverimo da li browser uopšte dozvoljava kreiranje kukija.
Provera podržanosti kukija u browseru
Iako PHP nema specijalizovanu funkciju za proveru podržanosti kukija u browseru, provera se može jednostavno obaviti u dva koraka:
- pokušajem kreiranja kukija određenog naziva
- naknadnom proverom da li kuki iz prethodnog koraka postoji
<?php
setcookie("proba", "proba kukija", time() + 3600, "/");
if (isset($_COOKIE['proba'])) {
echo "Čuvanje kukija je omogućeno.";
// U praksi, u slučaju da je čuvanje kukija omogućeno,
// nećemo (nepotrebno) o tome obaveštavati korisnika
}
else {
echo "Čuvanje kukija nije dozvoljeno!";
// U praksi, u slučaju da je čuvanje kukija nije omogućeno,
// moramo ipak naći elegantniji način da o tome
// obavestimo korisnika
}
?>
Takođe, u prethodnoj skripti, možemo koristiti i drugačiji uslov ....
<?php
// ....
if (count($_COOKIE) > 0)
// ....
?>
.... što navodimo (iako je prvi kod više nego adekvatan), pre svega iz razloga što verujemo da će naši čitaoci naći bolje i kreativnije načine da iskoriste mogućnost prebrojavanja postojećih kukija.
Pošto smo se kroz primere u PHP-u upoznali sa opštim principima čuvanja i obrade kukija, pogledaćemo kako sve navedene operacije možemo obavljati u Javascript-u.
Operacije sa kukijima u Javascript-u
Javascript kao front-end jezik takođe nudi dobru podršku za rad sa kukijima, ali, mora se priznati da se oko nekih stvari ipak moramo malo više potruditi nego što je to slučaj sa PHP-om.
Najbolje je ipak da to shvatimo kao priliku da se dodatno upoznamo sa Javascript-om i utvrdimo ono što smo do sada naučili (pogotovo kada je u pitanju rad sa nizovima).
Kreiranje kukija
Za kreiranje kukija preko Javascript-a, pozivaćemo kod po sledećem obrascu:
document.cookie = "naziv=vrednost; expires=datum; path=/";
Iako je kod naizgled drugačiji, prepoznajemo princip sa kojim smo se upoznali u odeljku o kukijima u PHP-u: predajemo naziv kukija, vrednost, vreme isteka i domen.
Deluje jednostavno, međutim, kada je u pitanju unos datuma, potrebno je koristiti sledeći format zapisa ....
Thu, 01 Jan 1970 00:00:00 UTC
.... što ni iz daleka nije intuitivno.
Da bismo olakšali sebi posao dodavanja novog kukija, kreiraćemo pomoćnu funkciju koja će kreirati nisku za dodavanje kukija - onako kako to od nas zahteva jezik Javascript:
function formatiranjeKukija(naziv, vrednost, dani, sati, domen) {
let datum = new Date();
datum.setTime(datum.getTime() + (dani * 86400 + sati * 3600) * 1000);
return naziv + "=" + "vrednost; " +
"expires=" + datum.toUTCString() +
((domen != "")? "; path=" + domen : "");
//*/
}
Princip je skoro isti kao u prethodnom slučaju (kada smo koristili PHP):
- objekat klase
Date
(datum) beleži datum - metoda
getTime
upisuje trenutni datum u objekat datum
Na vreme koje zada prethodna metoda dodaćemo:
dani * 86400
(broj dana * broj sekundi u jednom danu)sati * 3600
(broj sati * broj sekundi u jednom satu)
Zbir prethodne dve vrednosti pomnožićemo sa 1000, budući da metoda setTime
operiše sa milisekundama, a preko metode toUTCString
kreiraćemo format koji je neophodno koristiti pri kreiranju kukija u JS-u
Ako sada pozovemo sledeći kod:
document.cookie = formatiranjeKukija("boja_pozadine", "#2244ee", 30, 0, "/");
.... novi kuki će biti postavljen.
Pristup kukijima
Pristup kukijima je (reklo bi se) najmanje jednostavna operacija kada su u pitanju kukiji u Javascript-u.
Za pristup kukijima, JS nam nudi samo da preuzmemo sve kukije u obliku niske, preko sledećeg koda:
var kukiji = document.cookie;
.... pri čemu se (shodno primerima koje smo koristili) dobija niska sledećeg formata:
"korisnik=darth_vader"; id=411; boja_pozadine=#2244ee"
Međutim, budući da su delovi niske razdvojeni znakom tačka-zarez, preko komande split
se niska lako može pretvoriti u niz čijim elementima se može pristupati pojedinačno:
kukiji = kukiji.split(";");
.... posle čega promenljiva kukiji
više nije obična niska, već niz sledećeg sadržaja:
[
"korisnik=darth_vader",
" id=411",
" boja_pozadine=#2244ee"
]
U pitanju je niz niski, u kome svaka pojedinačna niska sadrži dva podatka (naziv i vrednost), koji su razdvojeni znakom jednakosti.
Pri svakom obraćanju "pojedinačnim niskama" možemo, ponovo preko komande split
, kreirati pomoćnu promenljivu red
- niz koji sadrži naziv kukija i pripadajuću vrednost:
let red = kukiji[0].split("=");
// red = ["korisnik", "darth_vader"];
red = kukiji[1].split("=");
// red = [" id", "411"];
red = kukiji[2].split("=");
// red = [" boja_pozadine", "#2244ee"];
Primećujemo da, u drugom i trećem nizu, prva niska sadrži razmak na početku, što je neizbežno, ali i lako rešivo (u daljoj obradi) preko komande trim
.
Pošto su nam poznati svi detalji, možemo napraviti jednostavnu funkciju koja će pronaći sadržaj određenog kukija (ili ustanoviti da traženi kuki ne postoji):
function pronalazenjeKukija(naziv) {
let kukiji = document.cookie;
kukiji = kukiji.split(";");
// kukiji = [
// "naziv_1=vrednost_1",
// " naziv_2=vrednost_2",
// " naziv_3=vrednost_3",
// ....
// " naziv_n=vrednost_n",
// ];
for (let i = 0; i < kukiji.length; i++) {
// kukiji[i] = "naziv_i=vrednost_i";
let red = kukiji[i].trim().split("=");
// red = ["naziv_i", "vrednost_i"];
if (red[0] == naziv) {
return red[1];
}
}
return false;
}
Čitaocima ostavljamo da gornju funkciju (koja je krajnje adekvatna za svakodnevnu upotrebu), dodatno optimizuju.
Uklanjanje kukija
Za uklanjanje kukija koristi se poznati (pomalo čudni) princip: pozivamo metodu za dodavanje/ažuriranje kukija u kojoj poništavamo vrednost i takođe, kao vreme isteka navodimo "trenutak u prošlosti".
Ako pozovemo sledeći kod:
document.cookie = "boja_pozadine=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;";
.... kuki će biti uklonjen.
Provera da li browser podržava kukije
Za razliku od prethodnih operacija, provera podržanosti kukija preko Javascript-a je krajnje jednostavna:
if (navigator.cookieEnabled) {
alert("Čuvanje kukija je omogućeno.");
// U praksi, u slučaju da je čuvanje kukija omogućeno,
// nećemo (nepotrebno) o tome obaveštavati korisnika
}
else {
alert("Čuvanje kukija nije dozvoljeno!");
// U praksi, u slučaju da je čuvanje kukija nije omogućeno,
// naći ćemo elegantniji način da o tome obavestimo korisnika
}
Sledeći koraci ....
Na kraju, pošto smo se upoznali sa mehanizmima za "trajno" čuvajne podataka iz browsera na računaru klijenta, možemo preći na sledeći (reklo bi se "prirodan") korak, to jest, primer koji se tipično vezuje za upotrebu lokalnih skladišta browsera.
U pitanju je autentifikacija (čime smo se već, u velikoj meri, pozabavili u tutorijalu za kreiranje forme za prijavu) i autorizacija korisnika, što će biti tema jednog od narednih članaka ....