Seje
HTTP je brezstanje protokol, vendar skoraj vsaka aplikacija potrebuje ohranjati stanje med zahtevami, na primer vsebino nakupovalne košarice. Prav temu služijo seje ali relacije. Pokazali si bomo,
- kako uporabljati seje
- kako preprečiti konflikte imen
- kako nastaviti potek
Pri uporabi sej vsak uporabnik prejme edinstven identifikator, imenovan ID seje, ki se prenaša v piškotku. Ta služi kot ključ do podatkov seje. Za razliko od piškotkov, ki se shranjujejo na strani brskalnika, se podatki v seji shranjujejo na strani strežnika.
Sejo nastavljamo v konfiguraciji, pomembna je zlasti izbira časa poteka.
Upravljanje sej ima na skrbi objekt Nette\Http\Session,
do katerega pridete tako, da si ga pustite predati s pomočjo dependency injection. V presenterjih je dovolj le
poklicati $session = $this->getSession()
.
Zagon seje
Nette v privzeti nastavitvi samodejno zažene sejo samodejno v trenutku, ko iz nje začnemo brati ali vanjo zapisovati
podatke. Ročno se seja zažene s pomočjo $session->start()
.
PHP ob zagonu seje pošlje HTTP glave, ki vplivajo na predpomnjenje, glej session_cache_limiter, in po potrebi tudi piškotek z ID-jem seje. Zato je treba vedno sejo zagnati še pred pošiljanjem kakršnega koli izpisa v brskalnik, sicer pride do sprožitve izjeme. Če torej veste, da se bo med izrisovanjem strani uporabljala seja, jo zaženite ročno prej, na primer v presenterju.
V razvijalskem načinu sejo zažene Tracy, ker jo uporablja za prikazovanje trakov s preusmeritvami in AJAX zahtevami v Tracy Baru.
Sekcije
V čistem PHP je podatkovno skladišče seje realizirano kot polje, dostopno preko globalne spremenljivke
$_SESSION
. Problem je v tem, da se aplikacije običajno sestojijo iz cele vrste medsebojno neodvisnih delov in če
imajo vsi na voljo le eno polje, prej ali slej pride do kolizije imen.
Nette Framework problem rešuje tako, da celoten prostor razdeli na sekcije (objekte Nette\Http\SessionSection). Vsaka enota nato uporablja svojo sekcijo z edinstvenim imenom in do nobene kolizije več ne more priti.
Sekcijo dobimo iz seje:
$section = $session->getSection('unikatno ime');
V presenterju je dovolj uporabiti getSession()
s parametrom:
// $this je Presenter
$section = $this->getSession('unikatno ime');
Preveriti obstoj sekcije je mogoče z metodo $session->hasSection('unikatno ime')
.
S samo sekcijo se nato dela zelo enostavno s pomočjo metod set()
, get()
in
remove()
:
// zapis spremenljivke
$section->set('userName', 'franta');
// branje spremenljivke, vrne null če ne obstaja
echo $section->get('userName');
// preklic spremenljivke
$section->remove('userName');
Za pridobitev vseh spremenljivk iz sekcije je mogoče uporabiti zanko foreach
:
foreach ($section as $key => $val) {
echo "$key = $val";
}
Nastavitev poteka
Za posamezne sekcije ali celo posamezne spremenljivke je mogoče nastaviti potek. Lahko tako pustimo poteči prijavo uporabnika čez 20 minut, vendar si pri tem še naprej zapomnimo vsebino košarice.
// sekcija poteče po 20 minutah
$section->setExpiration('20 minutes');
Za nastavitev poteka posameznih spremenljivk služi tretji parameter metode set()
:
// spremenljivka 'flash' poteče že po 30 sekundah
$section->set('flash', $message, '30 seconds');
Ne pozabite, da mora biti čas poteka celotne seje (glej konfiguracija seje) enak ali daljši od časa, nastavljenega pri posameznih sekcijah ali spremenljivkah.
Preklic prej nastavljenega poteka dosežemo z metodo removeExpiration()
. Takojšen preklic celotne sekcije
zagotovi metoda remove()
.
Dogodka $onStart, $onBeforeWrite
Objekt Nette\Http\Session
ima dogodke
$onStart
in $onBeforeWrite
, lahko torej dodate povratne klice, ki se sprožijo po zagonu seje ali pred
njenim zapisom na disk in posledičnim zaključkom.
$session->onBeforeWrite[] = function () {
// zapišemo podatke v sejo
$this->section->set('basket', $this->basket);
};
Upravljanje sej
Pregled metod razreda Nette\Http\Session
za upravljanje sej:
start(): void
Zažene sejo.
isStarted(): bool
Je seja zagnana?
close(): void
Zaključi sejo. Seja se samodejno zaključi na koncu izvajanja skripta.
destroy(): void
Zaključi in izbriše sejo.
exists(): bool
Ali HTTP zahteva vsebuje piškotek z ID-jem seje?
regenerateId(): void
Generira nov naključni ID seje. Podatki ostanejo ohranjeni.
getId(): string
Vrne ID seje.
Konfiguracija
Sejo nastavljamo v konfiguraciji. Če pišete aplikacijo, ki ne uporablja DI vsebnika, služijo za konfiguracijo te metode. Morajo biti poklicane še pred zagonom seje.
setName (string $name): static
Nastavi ime piškotka, v katerem se prenaša ID seje. Standardno ime je PHPSESSID
. Koristno je v primeru, ko
v okviru enega spletnega mesta poganjate več različnih aplikacij.
getName(): string
Vrača ime piškotka, v katerem se prenaša ID seje.
setOptions (array $options): static
Konfigurira sejo. Lahko nastavljate vse PHP direktive
seje (v formatu camelCase, npr. namesto session.save_path
zapišemo savePath
) in tudi readAndClose.
setExpiration (?string $time): static
Nastavi čas neaktivnosti, po katerem seja poteče.
setCookieParameters (string $path, ?string $domain=null, ?bool $secure=null, ?string $samesite=null): static
Nastavitev parametrov za piškotek. Privzete vrednosti parametrov lahko spremenite v konfiguraciji.
setSavePath (string $path): static
Nastavi imenik, kamor se shranjujejo datoteke s sejo.
setHandler (\SessionHandlerInterface $handler): static
Nastavitev lastnega obravnavalnika, glej dokumentacija PHP.
Varnost na prvem mestu
Strežnik predpostavlja, da komunicira vedno z istim uporabnikom, dokler zahteve spremlja isti ID seje. Naloga varnostnih mehanizmov je zagotoviti, da je temu res tako in da ni mogoče identifikatorja ukrasti ali podtakniti.
Nette Framework zato pravilno konfigurira PHP direktive, da ID seje prenaša samo v piškotku, ga onemogoči JavaScriptu in morebitne identifikatorje v URL-ju ignorira. Poleg tega v kritičnih trenutkih, kot je na primer prijava uporabnika, generira nov ID seje.
Za konfiguracijo PHP se uporablja funkcija ini_set, ki jo na žalost nekateri gostitelji prepovedujejo. Če je to primer tudi vašega gostitelja, se poskusite z njim dogovoriti, da vam funkcijo dovoli ali vsaj strežnik konfigurira.