PHP

Czym są sesje? Jaki problem rozwiązują i do czego można ich użyć. Sesje dostępne w PHP oraz alternatywy sesji w PHP.

korn

Czym są sesje?

Co mamy na myśli, mówiąc o sesjach? Nieformalnie, sesja w przypadku przeglądania WWW to czas, w którym dana osoba, siedząc przed jednym komputerem, przegląda określoną liczbę stron, następnie kończy ten proces. Jeżeli prowadzisz witrynę WWW, którą przykładowy użytkownik właśnie przegląda, sesja biegnie od czasu załadowania pierwszej strony, aż do ostatniej. Na przykład witryna hotelu na Karaibach może korzystać z sesji na czas przeglądania pięciu stron, ale użytkownik naprawdę rozpoczął sesję w portalu zajmującym się podróżami, a zakończył ją w czasie rezerwacji miejsca w hotelu konkurencji.

Co stanowi problem?

Dlaczego więc idea sesji jest na tyle skomplikowana? Jest tak dlatego, że protokól HTTP jest bezstanowy. Powoduje to, że twój serwer WWW na mniej pamięci długoterminowej niż twój kod. W rezultacie serwer WWW odpowiada na poszczególne żądania, jakie otrzymuje i nie ma sposobu na ich połączenie, nawet jeżeli żądania te są zapisywane. Jeżeli siądę przed swoim komputerem w Chicago a ty przy swoim, i oboje załadujemy kolejno pierwszą i drugą stronę z witryny hotelu na Karaibach, protokół HTTP nie zaoferuje żadnej pomocy przy rozpoznaniu, że tych dwoje ludzi oglądało dwie kolejne strony. Od strony serwera były to cztery żądania załadowania stron z dodatkowymi danymi związanymi z każdym wywołaniem. Nie tylko taka informacja nie identyfikuje cię (za pomocą nazwiska, adresu e-mail, numeru telefonu lub innego identyfikatora), ale również nie oferuje nic, co może zidetyfikować żądania kolejnych stron jako pochodzące od jednej osoby.

Dlaczego się tym zajmujemy?

Jeżeli twoja witryna jest przeznaczona tylko do dostarczania rozmaitych stron różnym użytkownikom, to w zasadzie nie musisz wiedzieć, kiedy sesja się rozpoczyna, a kiedy kończy. Jednak istnieje wiele sytuacji, kiedy chcemy to wiedzieć.

  • Chcemy dostosowywać się do doświadczenia użytkowników, które zależy od tego, jakie strony (lub ile) już widzieli.
  • Chcemy wyświetlać reklamy, ale nie chcemy wyświetlać jednej reklamy więcej niż raz w czasie sesji.
  • Chcemy zbierać informacje o podejmowanych przez użytkownika działaniach (tak jak w grach przygodowych realizuje się pamiętanie punktów lub w witrynach handlu elektronicznego - wózki na zakupy).
  • Interesuje nas sposób, w jaki ludzie korzystają z witryny - czy przechodzą do wewnętrznych stron korzystając z zakładek, czy przechodzą całą drogę od strony głównej.

Dla wszystkich zastosowań powinniśmy skorelować żądania załadowania stron z kolejnymi częściami sesji; dla niektórych zastosowań wygodnie byłoby zapamiętywać niektóre dane w połączeniu z sesją. Sesje PHP rozwiązują oba te problemy.

Jak działają sesje w PHP?

Dobrze działające sesje zajmują się dwiema rzeczami: śledzeniem sesji (to znaczy wykrywaniem, kiedy dwa osobne wywołania skryptów są częścią tej samej sesji, a kiedy nie) oraz zapamiętywaniem danych związanych z sesją. Musimy mieć pierwszą możliwość, żemy wogóle myśleć o drugiej.

Jeżeli PHP śledzi sesje, można również zachować dane przez "rejestrowanie" określonych zmiennych, w celu wskazania, że ich wartości mają być zapamiętane w połączeniu z identyfikatorem sesji.

Uaktywnianie sesji w PHP

Pierwszym krokiem skryptu, korzystającego z sesji, jest poinstruowanie PHP, że sesja może już trwać, więc trzeba ją przechwycić i odczytać wszystkie związane z nią dane. Realizuje się to przez wywołanie bezargumentowej funkcji session_start(). Jeżeli jest ona wywołana w każdym skrypcie, możesz ją pominąć i w zamian za to ustawić w php.ini zmienną session.auto_start na 1 (jest ona zwykle ustawiona na 0).

Wynik działania session_start() zależy od tego, czy PHP odszukał poprzedni identyfikator sesji, przekazany przez argumenty HTTP lub cookie. Jeżeli został znaleziony, poprzednio związane z nim dane sesji będą dostępne w aktualnym wywołaniu skryptu. Jeżeli PHP nie znajdzie poprzedniego identyfikatora sesji, wywołanie session_start() utworzy nową sesję. Podstawowym efektem jest utworzenie nowego identyfikatora, który może być używany do rejestrowania nowych danych.

Zapisywanie danych w sesji

Wywołanie session_start() zajmuje się zainicjalizowaniem sesji, tzn odczytaniem lub wygenerowaniem klucza identyfikacji sesji, i odczytaniem danych z sesji lub zainicjalizowaniem pustego miejsca, jeśli sesja jest nowa.

Załóżmy, że dostaliśmy w parameterze $_GET["city"] wartość "Warszawa". Chcielibyśmy ją zapisać w sesji, tak by można było ją odczytać w kolejnych wywołaniach skryptu.

$action = $_GET["action"];

session_start();
if ($action === 'store') {
    $_SESSION["session_city"] = $_GET["city"];
    echo "Zapisano wartość w sesji";
}
if ($action === 'read') {
    echo "Wartość z sesji to " . $_SESSION["session_city"];
}

Wywołanie session_start() zainicjalizuje sesje, a znaczy to że dane zapisane w $_SESSION staną się dostępne w kolejnym wykonaniu skryptu, który korzysta z tej samej sesji.

  • Jeśli wywołamy teraz skrypt w taki sposób:
    plik.php?action=store&city=Warszawa
    
    zobaczymy tekst Zapisano wartość w sesji
  • Jeśli wywołamy go ponownie...
    plik.php?action=read
    
    zobaczymy tym razem tekst Wartośc z sesji to Warszawa.

Oczywiście podając inne wartości parametru city zobaczymy inny wynik, mimo że w drugim żądaniu nigdzie nie przekazaliśmy wartości "Warszawa" (ani w parametrze, ani ciastkach ani żadnych innych nagłówkach), to mogliśmy ją odczytać, dlatego że została zapisana w sesji podczas poprzedniego żądania. Zależnie od Twojej przeglądarki, sesja będzie się utrzymywać albo do czasu kiedy zamkniesz kartę, lub przeglądarkę, lub nawet do czasu wyłączenia komputera - zależnie od tego jak długo przeglądarka będzie nagłówek PHPSESSID.

Gdzie są przechowywane dane?

Mechanizm sesji bazuje na identyfikatorze sesji i związanych z nim zmiennych.

Jak już wiemy, identyfikator sesji jest przechowywany przez przeglądarke (w pamięci, na dysku, lub w ogóle, zależnie od ustawień) i przekazywany do kolejnych żądań jako nagłówek HTTP, ciastko lub jest wbudowany w argumenty GET, POST, przekazywane razem z żądaniem pobrania strony. Server co jakiś czasu zmienia indetyfikator, i odsyła go z powrotem do klienta (przeglądarki). A więc orzeglądarka i serwer przerzucają się tą krytyczną dla nas informacją jak "gorącym kartoflem". Jeżeli któraś ze stron zgubi identyfikator, nasza sesja się kończy.

Domyślnie zawartość zmiennych sesji jest przechowywana w specjalnych plikach na serwerze, po jednym dla każdej sesji (można przechowywać identyfikator sesji jako cookie w komputerze klienta, ale byłoby grubiaństwem przechowywanie wartości zmiennych sesji na jego dysku). Zapamiętanie danych w ten sposób wymaga kodu sesji, realizującego serializację danych. Termin ten oznacza zamianę danych w liniową sekwencję bajtów, którą można zapisywać na dysku i odczytywać z niego w celu odtworzenia danych.

Możliwe jest takie skonfigurowanie PHP, aby program przechowywał zmienne sesji w bazie danych, zamiast w plikach.

Funkcje obsługi sesji

Tabela poniżej zawiera najbardziej istotne funkcje związane z obsługą sesji:

  • session_start() - Powoduje, że PHP odczytuje identyfikator sesji przekazany do strony (dzięki cookie lub GET/POST). Jeżeli nie może go odszukać, tworzy nowy identyfikator sesji. Jeżeli odnaleziony został stary identyfikator sesji, PHP odtwarza przypisania do wszystkich zarejestrowanych zmiennych i udostępnia te wartości jako zwykłe zmienne globalne.

  • session_destroy() - Wywołanie tej funkcji usuwa wszystkie zapamiętane dane o sesji. Najbardziej popularne zastosowanie tej funkcji, to gdy użytkownik chce się wylogować. Nie musimy wtedy pamiętać żadnych danych z jego sesji, więc możemy ją zniszczyć, aby np. umożliwić użytkownikowi zalogowanie się na inne konto.

  • session_name() - Pozwala nadać oraz odczytać identyfikator sesji. Funkcja wywołana bez parametrów zwraca nazwę bieżącej sesji. Wywołana z parametrem string ustawia nazwę identyfikatora sesji. Jest to zwykle 'PHPSESSID'. Wywołana z jedym argumentem session_name() ustawia nazwę bieżącej sesji na podany ciąg. Nazwa ta jest używana jako klucz do odszukania identyfikatora sesji w cookie lub argumentach GET, POST. Aby odnalezienie sesji się udało, nazwa sesji musi być identyczna jak nazwa sesji, w trakcie której poddano zmienne serializacji. Zauważ, że nie ma powodu do zmiany nazwy sesji, chyba, że chcesz rozróżniać różne typy sesji obsługiwanych jednocześnie przez serwer WWW (np. w przypadku wielu witryn korzystających z sesji). Nazwa sesji jest zmienna na domyślną za każdym uruchomieniem skryptu, więc zmiana nazwy sesji musi zostać wykonana w każdym skrypcie przez wywołaniem innej funkcji obsługi sesji.

  • session_module_name() - Pozwala zmienić sposób w jaki sesja jest przechowywana na serwerze. Jeżeli nie zostaną podane żadne argumenty, funkcja zwraca nazwę modułu odpowiedzialnego za obsługę danych sesji. Domyślny moduł to 'files', co oznacza, że dane sesji po serializacji są zapisywane do plików w katalogu ustawionym przez funkcję session_save_path(). Jeżeli podany zostanie ciąg jako argument, nazwa modułu jest zmienna na ten ciąg (np. 'user' dla zdefiniowanej przez użytkownika bazy danych sesji; ale nie powinieneś zmieniać tego parametru, jeżeli nie wiesz dokładnie co robisz).

  • session_save_path() Zwraca (lub ustawia w przypadku podania argumentu) ścieżkę do katalogu, w którym zapisywane są wartości zmiennych sesji (zwykle jest to katalog /tmp w systemach Unix). Katalog ten musi mieć odpowiednio ustawione uprawniwnia, aby PHP mógł tam zapisywać pliki.

  • session_id() - Zwraca klucz określający sesje. Jeżeli zostanie przekazany argument, funkcja ta ustawi identyfikator sesji na tę wartość. W normalnych warunkach nie ma powodu żeby używaj tej funkcji, skorzystaj raczej z session_regenerate_id().

  • session_encode()/session_decode() - Pozwalają serializować i deserializować stan sesji. Funkcja session_encode() zwraca string (zawierający zserializowany stan sesji). Ciąg ten może zostać użyty do zapamiętania sesji w celu odtworzenia jej za jakiś czas (np. zapisanie zakodowanego ciągu w pliku lub w bazie danych) używając session_decode(). Po użyciu session_decode() można znów używać $_SESSION.

Zastosowania sesji

Najczęściej sesje wykorzystuje się do zapisania aktualnego użytkownika - jesli zalogujemy się na stronie, spodziewamy się że po zmianie strony na podstronę, nadal będziemy zalogowani.

Sesję utrzymuje się również w miejscach gdzie logowanie nie jest konieczne, np funkcja "Koszyk" w sklepach internetowych.

Alternatywy implementacji sesji w PHP

Zanim zajmiemy się podejściem PHP do sesji, wymieńmy kilka alternatywnych sposobów rozwiązania problemu. Jak się później przekonamy, podejście PHP stanowi kombinację kilku z tych technik:

Ukryte zmienne

Każde wywołanie HTTP jest obsługiwane niezależnie, ale za każdym razem gdy użytkownik przejdzie do innej strony witryny, jest zwykle realizowane przez łącze lub przetworzenie formularza. Jeżeli pierwsza strona, którą użytkownik odwiedza, może wygenerować identyfikator tej wizyty, a następne przejścia ze strony do strony mogą go przenosić.

Poniższy przykładowy fragment kodu można zamieścić na początku strony:

if (!isSet($GET['my_session_key']))  {
   $session_id = generate_my_session_id(); // hipotetyczna funkcja
}

Ten fragment kodu kontroluje, czy zmienna $session_id została wcześniej zainicjowana. Jeżeli tak, zakładamy, że została przekazana do bieżącej strony i jesteśmy w środku sesji. Jeżeli nie, oznacza to, że jesteśmy na pierszej stronie nowej sesji i wywołujemy hipotetyczną funkcję, nazwaną generate_my_session_id(), w celu utworzenia identyfikatora danej sesji.

Założyliśmy, że po włączeniu poprzedniego kodu identyfikator sesji został stworzony i teraz wystarczy przekazać go do innych połączonych stron.

Jeśli każde łącze będzie zawierać $session_id jako parameter zapytania (ang. query parameter) żądania GET, a formularz będzie zawierać ukryty argument POST, będziemy mogli utrzymać sesję dla użytkownika, ponieważ na podstawie tego parametru będziemy mogli go odróżnić od innych (jeśli będziemy mogli wygenerować ID sesji).

  • Parametry GET
    <a href="next.php?session_id=$session_id">Następna strona</a> 
    
  • Ukryte pola w formularzu POST
    <form method="POST">
        <input type="hidden" name="session_id" value="$session_id"> 
    </form>
    

Ta technika jest prostym sposobem odróżniania sesji (dopóki możesz generować identyfikatory). Jeśli mamy identyfikatory sesji, możemy zastosować wiele sposobów dołączania danych do każdej sesji. Jednym z nich jest użycie identyfikatora sesji jako klucza tabeli bazy danych. Jednak to wyjście jest kłopotliwe - musisz upewnić się, że każde łącze i obsługa formularza prawidłowo przenoszą opisane informacje. Jeżeli przesyłasz informacje jako argument GET, maszyna śledzenia sesji będzie widoczna w pasku adresu przeglądarki - argument może zostać łatwo zmieniony lub przechwycony przez użytkownika.

Cookies

Innym sposobem śledzenia sesji jest użycie takiego samego identyfikatora sesji, jak opisaliśmy przed chwilą, ale do jego przekazywania pomiędzy stronami stosuje się cookie.

Cookie jest nagłówkiem odpowiedzi HTTP, który umównie jest odsyłany przy nastęnym żądaniu w nagłówku Set-Cookie. Przeglądarki mogą, ale nie muszą zapisać wartości z nagłówka Cookie w pamięci lub na dysku, w taki sposób że ciasteczko (ang. cookie) zostanie odesłane nawet za kilka godzin lub po restarcie komputera. Jednak to czy cookie zostanie zapisane na dysku lub odesłane w ponownym żądaniu leży w gestii przeglądarki (zwykle jednak tak się dzieje).

Zamiast sprawdzać zmienną przekazaną za pomocą metody POST lub GET (i przypisywać nową, jeżeli nie została odnaleziona), skrypt może sprawdzić, czy przeglądarka odesłała ciasteczko i zapisać do niego nowy identyfikator, jeżeli nie został odnaleziony. Motoda ta posiada pewne zalety w porównaniu do ukrytych zmiennych:

Zalety ciasteczek nad parametrami żądań:

  • mechanizm ten nie ingeruje w logikę Twojej aplikacji - przekazując parametry w metodach POST i GET mieszamy parametry na jakich na prawdę nam zależy, z naszym mechanizmem sesji. Takie mieszanie parametrów może sprawić że aplikacja stanie się trudna w utrzymaniu.
  • Ciasteczka obecnie istnieją w dwóch formach: tzw. "zwykłe ciastko" (ang. standard cookie) oraz ciasteczko tylko do HTTP (ang. Http-Only Cookie). Jest to taki rodzaj ciasteczka, który jest pobierany przy odpowiedzi i odsyłany przy żądaniu, ale nie można się do niego dostać z kodu klienta (z JavaScript'u), przez co jest to bardziej odporne np. na ataki XSS. Na tym polega np autentykacja JWT.

Podobieństwa do przekazania parametrów przez GET/POST, a ciasteczka.

  • kod, który sprawdza i ustawia cookie może być scentralizowany (zamaiast występować w każdym łączu) w obu przypadkach
  • formularze i nagłówki są mniej skomplikowane (przez nie dołączanie parametrów), ale jeśli nasz widok jest generowany przez wybraną bibliotekę UI, to być może nie będzie to stawnoiło problemu

Adres IP

Serwery WWW znają albo nazwę, albo adres IP komputera, z którego przyszło żądanie załadowania strony. W większości konfiguracji, PHP udostępnia te dane aplikacji w formie superglobalnych zmiennych, odpowiednio $_SERVER['REMOTE_HOST'] i $_SERVER['REMOTE_ADDR']. Wydaje się, że identyfikacja komputera jest jednoznaczna z użytkownikiem, co najmniej w krótkim czasie. Jeżeli w krótkim okresie dostaniesz dwa żądania z tego samego adresu IP, kod może stwierdzić, że zostały one wysłane przez tę samą osobę, która przeszła z jednej strony witryny na drugą.

Niestety adres IP Twojej przeglądarki nie musi pasować do komputera, z którego korzysta użytkownik. Zarówno AOL, jak i inni dostawcy internetu często stosują serwery proxy, które działają jako pośrednicy. Jeżeli przeglądarka zażąda załadowania URL, wysyła on to żądanie do docelowego serwera i zwraca stronę użytkownikowi. Taka konfiguracja powoduje, że wielu użytkowników może jednocześnie przeglądać twoją witrynę pozornie z jednego adresu IP. Dodatkowo, w zależnie od konfiguracji sieci firmowej, domowej lub publicznej, różne komputery w tej sieci mogą być widoczne poza siecią pod jednym adresem IP. Adresy IP nie są więc wystarczająco jednoznaczne, aby stanowiś podstawę do śledzenia sesji.

Nowoczesne podejście do sesji

Podczas gdy implementacja sesji w PHP pozostawia wiele do życzenia, i może się okazać przestarzała; tak sama idea sesji jest w zasadzie nieśmiertela. Zawsze będzie potrzeba przechowywania danych po stronie serwera bez udziału klienta (ze względu na wydajność, bezpieczeństwo, funkcjonalności). Tak więc nowoczesne frameworki, takie jak Laravel, Ouzo, Symfony i masa innych - wszystkie korzystają z tej idei, ale oczywiście mają już inne interfejsy.

Np, w bibliotece laravel dostęp do sesji wygląda podobnie do:

if ($request->session()->has('users')) {
    //
}
if ($request->session()->exists('users')) {
    //
}

Podsumowanie

Sesje są użyteczne w sytuacjach, gdy chcesz śledzić działania użytkownika w przypadku interakcji trwającej dłużej niż wykonanie jednego skryptu. Jeżeli prezentowana zawartość strony zależy od działań podejmowanych na poprzedniej, kod powinien zapamiętywać i przenosić te informacje w sposób, który umożliwia odróżnienie poszczególnych użytkowników. Ponieważ protokół HTTP jest protokołem bezstanowym, prowadzi to do zastosowania technik zastępczych - zwykle albo ukrytych zmiennych (które są niestety kłopotliwe), albo cookies (niektóre przeglądarki klientów ich nie obsługują).

Implementacja sesji w PHP hermetyzuje te skomplikowane zagadnienia. Identyfikatory sesji są tworzone i rozprowadzone w sposób automatyczny, a mechanizm rejestracji pozwala programiście dołączać dowolne dane PHP do sesji. Poza początkowym dołączeniem się do sesji i zarejestrowaniem zmiennych, które powinny zostać rozpropagowane poza bieżącą stronę, użycie sesji jest dla programisty przejrzyste.


Autor: Bartosz (webcity.pl)

PHP

9 komentarzy

Poprawiłem artykuł, dopasowałem formatowanie, wywaliłem session_register(), tak że kod używa predefiniowanych superglobali.

Artykul jest stary i powinien zostać zmieniony. Kto teraz w sesjach nie uzywa tablic predefiniowanych się pytam?

... dokładnie tak jak mówi Twardy..... artykuł przestarzały!

2 godziny sie pieprzyłęm z tymi sesjami, aż natrafiłęm na tę strone, tak czy siak za pierwszym razem nawet ten kurs nie zadzial, ale w końcu sie udało ... :D

Dobrze, ze wrzuciles ten artykul tutaj, bo webcity.pl od jakiegos czasu chyba przestalo istniec... ;)

Artykul ten jest dostepny na webcity.pl: http://webcity.pl/artykuly.php/t/3
Jako autor podany jest Tim Converse.

Najpierw do Adama:

Nie wiem skad to jest, wiem tylko ze zessalem to z www.webcity.pl (chyba) i wwalilem tutaj zeby ludziochy sie uczyly.

A teraz do LofiXa:

siegac to siegam z wielu stron ale raczej sie nie podpisuje pod cudzymi materialami, chyba ze gdzies zobaczyles taka sytuacje, jesli tak to mi daj cynk bo moze wwalilem to jeszcze jak bylem mlody i glupi :-)

korn siega rowniez po materialy ze stron
ygreg.com i innych :>
nieladnie

Otrzymalem informacje, ze artykul jest plagiatem z ksiazki PHP 4. Biblia.