Sesje

HTTP jest protokołem bezstanowym, jednak niemal każda aplikacja potrzebuje przechowywać stan między żądaniami, na przykład zawartość koszyka. Właśnie do tego służy sesja. Pokażemy,

  • jak używać sesji
  • jak unikać konfliktów nazw
  • jak ustawić wygaśnięcie

Podczas używania sesji każdy użytkownik otrzymuje unikalny identyfikator zwany ID sesji, który jest przekazywany w ciasteczku. Służy on jako klucz do danych sesji. W przeciwieństwie do ciasteczek, które są przechowywane po stronie przeglądarki, dane w sesji są przechowywane po stronie serwera.

Sesję ustawiamy w konfiguracji, ważny jest zwłaszcza wybór czasu wygaśnięcia.

Zarządzaniem sesją zajmuje się obiekt Nette\Http\Session, do którego dostaniesz się, prosząc o jego przekazanie za pomocą wstrzykiwania zależności. W prezenterach wystarczy tylko wywołać $session = $this->getSession().

Instalacja i wymagania

Uruchomienie sesji

Nette w domyślnych ustawieniach automatycznie rozpoczyna sesję w momencie, gdy zaczynamy z niej czytać lub do niej zapisywać dane. Ręcznie sesję rozpoczyna się za pomocą $session->start().

PHP wysyła przy uruchomieniu sesji nagłówki HTTP wpływające na buforowanie, zobacz session_cache_limiter, a ewentualnie również ciasteczko z ID sesji. Dlatego konieczne jest zawsze uruchomienie sesji jeszcze przed wysłaniem jakiegokolwiek wyjścia do przeglądarki, w przeciwnym razie zostanie zgłoszony wyjątek. Jeśli więc wiesz, że w trakcie renderowania strony będzie używana sesja, uruchom ją ręcznie wcześniej, na przykład w prezenterze.

W trybie deweloperskim sesję uruchamia Tracy, ponieważ używa jej do wyświetlania pasków z przekierowaniami i żądaniami AJAX w Tracy Bar.

Sekcje

W czystym PHP magazyn danych sesji jest realizowany jako tablica dostępna przez zmienną globalną $_SESSION. Problem polega na tym, że aplikacje zwykle składają się z wielu wzajemnie niezależnych części i jeśli wszystkie mają do dyspozycji tylko jedną tablicę, wcześniej czy później dojdzie do kolizji nazw.

Nette Framework rozwiązuje problem, dzieląc całą przestrzeń na sekcje (obiekty Nette\Http\SessionSection). Każda jednostka używa następnie swojej sekcji z unikalną nazwą i do żadnej kolizji już dojść nie może.

Sekcję uzyskujemy z sesji:

$section = $session->getSection('unikalna nazwa');

W prezenterze wystarczy użyć getSession() z parametrem:

// $this to Presenter
$section = $this->getSession('unikalna nazwa');

Sprawdzić istnienie sekcji można metodą $session->hasSection('unikalna nazwa').

Z samą sekcją pracuje się następnie bardzo łatwo za pomocą metod set(), get() i remove():

// zapis zmiennej
$section->set('userName', 'franta');

// odczyt zmiennej, zwraca null, jeśli nie istnieje
echo $section->get('userName');

// usunięcie zmiennej
$section->remove('userName');

Aby uzyskać wszystkie zmienne z sekcji, można użyć pętli foreach:

foreach ($section as $key => $val) {
	echo "$key = $val";
}

Ustawienie wygaśnięcia

Dla poszczególnych sekcji lub nawet poszczególnych zmiennych można ustawić wygaśnięcie. Możemy w ten sposób pozwolić na wygaśnięcie logowania użytkownika po 20 minutach, ale jednocześnie nadal pamiętać zawartość koszyka.

// sekcja wygaśnie po 20 minutach
$section->setExpiration('20 minutes');

Do ustawienia wygaśnięcia poszczególnych zmiennych służy trzeci parametr metody set():

// zmienna 'flash' wygaśnie już po 30 sekundach
$section->set('flash', $message, '30 seconds');

Pamiętaj, że czas wygaśnięcia całej sesji (zobacz konfiguracja sesji) musi być taki sam lub dłuższy niż czas ustawiony dla poszczególnych sekcji lub zmiennych.

Anulowanie wcześniej ustawionego wygaśnięcia uzyskamy metodą removeExpiration(). Natychmiastowe anulowanie całej sekcji zapewni metoda remove().

Zdarzenia $onStart, $onBeforeWrite

Obiekt Nette\Http\Session ma zdarzenia $onStart i $onBeforeWrite, możesz więc dodać callbacki, które zostaną wywołane po starcie sesji lub przed jej zapisem na dysk i późniejszym zakończeniem.

$session->onBeforeWrite[] = function () {
	// zapisujemy dane do sesji
	$this->section->set('basket', $this->basket);
};

Zarządzanie sesją

Przegląd metod klasy Nette\Http\Session do zarządzania sesją:

start(): void

Rozpoczyna sesję.

isStarted(): bool

Czy sesja jest rozpoczęta?

close(): void

Kończy sesję. Sesja jest automatycznie kończona na końcu działania skryptu.

destroy(): void

Kończy i usuwa sesję.

exists(): bool

Czy żądanie HTTP zawiera ciasteczko z ID sesji?

regenerateId(): void

Generuje nowy losowy ID sesji. Dane pozostają zachowane.

getId(): string

Zwraca ID sesji.

Konfiguracja

Sesję ustawiamy w konfiguracji. Jeśli piszesz aplikację, która nie używa kontenera DI, do konfiguracji służą te metody. Muszą być wywołane jeszcze przed uruchomieniem sesji.

setName (string $name): static

Ustawia nazwę ciasteczka, w którym przekazywane jest ID sesji. Standardowa nazwa to PHPSESSID. Przydatne, gdy w ramach jednej strony internetowej działa kilka różnych aplikacji.

getName(): string

Zwraca nazwę ciasteczka, w którym przekazywane jest ID sesji.

setOptions (array $options)static

Konfiguruje sesję. Można ustawiać wszystkie dyrektywy sesji PHP (w formacie camelCase, np. zamiast session.save_path zapisujemy savePath) oraz readAndClose.

setExpiration (?string $time)static

Ustawia czas nieaktywności, po którym sesja wygaśnie.

setCookieParameters (string $path, ?string $domain=null, ?bool $secure=null, ?string $samesite=null)static

Ustawienie parametrów dla ciasteczka. Domyślne wartości parametrów można zmienić w konfiguracji.

setSavePath (string $path)static

Ustawia katalog, w którym zapisywane są pliki sesji.

setHandler (\SessionHandlerInterface $handler)static

Ustawienie własnego handlera, zobacz dokumentację PHP.

Bezpieczeństwo przede wszystkim

Serwer zakłada, że komunikuje się ciągle z tym samym użytkownikiem, dopóki żądania towarzyszy ten sam ID sesji. Zadaniem mechanizmów bezpieczeństwa jest zapewnienie, aby tak rzeczywiście było i nie było możliwe kradzież lub podstawienie identyfikatora.

Nette Framework dlatego poprawnie konfiguruje dyrektywy PHP, aby ID sesji przekazywał tylko w ciasteczku, uniemożliwił dostęp do niego JavaScriptowi i ignorował ewentualne identyfikatory w URL. Ponadto w krytycznych momentach, jak na przykład logowanie użytkownika, generuje nowy ID sesji.

Do konfiguracji PHP używana jest funkcja ini_set, której niestety niektóre hostingi zabraniają. Jeśli jest to przypadek również Twojego hostingu, spróbuj się z nim dogadać, aby pozwolił Ci na użycie funkcji lub przynajmniej skonfigurował serwer.

wersja: 4.0