Często programiści chcą stworzyć własny mechanizm sesji, jednak nie każdy wie jak się do tego zabrać. W tym artykule postaram się pokazać jak napisać klasę do obsługi sesji.
Zaczniemy od zdefiniowania kilku stałych, które będą nam potrzebne, w sumie będą bardzo podobne do tych wbudowanych w PHP z tym, że my użyjemy tylko kilku i to najbardziej potrzebnych.
define( ‘SESSIONS_PATH’, ‘sessions/’ ); define( ‘SESSION’, ‘sess_’ ); define( ‘SESSION_LIFE_TIME’, 180 ); define( ‘DATE_FORMAT’, “d.m.Y H:i:s” ); |
Pierwsza stała określa ścieżkę dostępu do katalogu, w którym będziemy przechowywać pliki sesji. Druga instrukcja definiuje stałą zawierającą, prefix (czyli przedrostek) dla ciastek, przyda się gdy będziemy zapisywać sessionId w cookie. Nazwa SESSION_LIFE_TIME chyba mówi sama za siebie, jest to czas „życia” sesji określony w sekundach. Ostatnia stała w zasadzie nie ma za dużo wspólnego z sesjami, ona po prostu określa w jakim formacie ma być wyświetlana data.
Zanim przejdziemy do pisania klasy zdefiniujmy jeszcze funkcje z której będziemy często korzystać, naszczęście jest bardzo krótka.
function file_put_contents( $strFile, $strData ) { $resFile = fopen( $strFile, ‘w’ ); fwrite( $resFile, $strData ); fclose( $resFile ); } |
Teraz zdefiniujemy zmienne klasy session, będzie ich kilka i każdą pokrótce postaram się omówić.
class session { var $strSID; var $strIP; var $blnIsCookieSet; var $blnEndSession = false; var $intLastActive; var $strLastActive; var $arrUserConfig = array(); } |
Pierwsze co się rzuca w oczy to, to, że każda zmienna ma przedrostek określający jej typ. Łatwo się domyślić, że str oznacza ciąg znaków, int liczbę całkowitą, bln prawda lub fałsz, a arr tablice. Mam nadzieję, że taki zapis ułatwi wam choć trochę zrozumienie działania poszczególnych metod. Ale wróćmy do naszych zmiennych, SID oznacza sessionId i jest zapisywany jako ciąg 32 znaków, dla każdej sesji jest generowany unikalny strSid. Co oznacza $strIP chyba jest oczywiste: IP klienta. Kolejna zmienna sprawdza czy ciastko jest ustawione jeżeli tak to nie będziemy przesyłać sid-a metodą GET, $intLastActive to data ostatniej modyfikacji pliku z sesją zapisana w formacie liczby całkowitej (tak zwany UNIX timestamp). Następna zmienna jest tą samą datą co poprzednia z tym, że zapisana w formacie dla nas zrozumiałym. Ostatnia zmienna jest tablicą przechowującą wszystkie zmienne zdefiniowane przez użytkownika.
Dopiszmy teraz metodę inicjalizującą naszą klasę, co ciekawe ta funkcja jest zaprojektowana tak aby sama tworzyła klasę wewnątrz której jest zdefiniowana, brzmi to co najmniej dziwnie i może troche odstraszać, ale w rzeczywistości jest to dość proste zobaczmy kod.
function create( &$objSession ) { // 1 $strSidName = SESSION . ‘sid’; if ( !isset( $_GET[‘sid’] ) && !isset( $_COOKIE[$strSidName] ) ) { $objSession = new session; $objSession -> initSession(); } else { // 2 $strSid = ( isset( $_COOKIE[$strSidName] ) ) ? $_COOKIE[$strSidName] : $_GET[‘sid’]; $strFile = $strSid . ‘.ses’; if ( is_file( SESSIONS_PATH . $strFile ) ) { $serialized = file_get_contents( SESSIONS_PATH . $strFile ); // 3 $objSession = unserialize( $serialized ); $objSession -> initSession(); // 4 if ( $objSession -> strIP != $_SERVER[‘REMOTE_ADDR’] || $objSession -> strSID != $strSid ) { unset( $objSession ); $objSession = new session; $objSession -> initSession(); } // 5 } else { $objSession = new session; $objSession -> initSession(); } } return $objSession; } |
Jako zmienną podajemy referencję do zewnętrznej zmiennej z której chcemy „zrobić” obiekt. Obiekt będzie odserializowany lub utworzony nowy w zależności od tego czy kontynuujemy sesje czy dopiero ją zaczynamy. (1) Na początku sprawdzamy czy jest ustawiona jedna ze zmiennych $_GET[‘sid’] lub $_COOKIE[$strSidName] jeżeli nie to wiadomo, że trzeba zrobić nową sesję. (2) W przeciwnym wypadku pobieramy SID-a i jeżeli istnieje plik o takiej nazwie jak id naszej sesji to wyciągamy wszystko z tego pliku i (3) używamy funkcji unserialize() aby odczytać dane sesji. Jeśli wszystko pójdzie dobrze to ze zmiennej $objSession utworzył nam się obiekt, uruchamiamy jego metodę initSession() (opisze ją trochę później) i teraz nasza sesja zawiera wszystkie zapisane wcześniej dane oraz dane, które muszą być pobrane za każdym razem przy tworzeniu sesji, (4) ale to jeszcze nie koniec pracy, musimy sprawdzić czy IP klienta zapisane wewnątrz sesji jest takie samo jak IP klienta, wywołującego skrypt. Jeżeli nie to tworzymy nową „czystą” sesje bez żadnych zmiennych, (5) podobnie, jeśli nie znaleziono pliku przechowującego sesję.
Zajmijmy się teraz metodą z której już skorzystaliśmy, wcześniej, a mianowicie initSession(), jak już wiemy przygotowuje ona obiekt do pracy, jej źródło wygląda tak:
function initSession() { // 1 $strSidName = SESSION . ‘sid’; if ( !isset( $_GET[‘sid’] ) && !isset( $_COOKIE[$strSidName] ) ) { $strSid = md5( rand() ); setcookie( $strSidName, $strSid, time() + SESSION_LIFE_TIME ); } else { // 2 if ( isset( $_COOKIE[$strSidName] ) ) { $strSid = $_COOKIE[$strSidName]; $this -> blnIsCookieSet = true; } else { $strSid = $_GET[‘sid’]; $this -> blnIsCookieSet = false; } } $this -> strSID = $strSid; // 3 $this -> strIP = $_SERVER[‘REMOTE_ADDR’]; $strFile = SESSIONS_PATH . $this -> strSID . ‘.ses’; $this -> intLastActive = @filemtime( $strFile ); return true; |
Podobnie jak poprzednio zaczynamy od odczytania id sesji, jeżeli go nie ma to tworzymy nowy i ustawiamy ciastko o odpowiednich parametrach. Jeżeli mamy id sesji i jest ono ustawione w ciastku zmieniamy $blnIsSetCookie na true. Teraz zostaje już tylko pobrać IP usera i czas ostatniej modyfikacji pliku z sesją.
Nasze pliki z sesjami nie są za duże zazwyczaj nie będą przekraczać rozmiaru jednego kilobajta, jednak dobrze by było się ich pozbyć, gdy będą już niepotrzebne, ale kiedy to nastąpi? Na początku zdefiniowaliśmy stałą SESSION_LIFE_TIME na 3 minuty (180 sekund) i jeżeli jakiś plik nie będzie używany od 3 minut wtedy nasza metoda garbageCollection() uzna go za śmieć i skasuje.
function garbageCollection() { // 1 if ( $resDirectory = opendir( SESSIONS_PATH ) ) { while ( ( $strFile = readdir( $resDirectory ) ) !== false ) { // 2 if ( is_file( SESSIONS_PATH . $strFile ) ) { if ( time() > filemtime( SESSIONS_PATH . $strFile ) + SESSION_LIFE_TIME ) { unlink( SESSIONS_PATH . $strFile ); } } } closedir( $resDirectory ); } } |
Metoda działa tak: (1) otwieramy odpowiedni katalog i sprawdzamy jego zawartość. (2)Jeżeli natrafimy na plik sprawdzamy czy nie jest przestarzały to znaczy czy czas ostatniej modyfikacji lub utworzenia plus czas trwania sesji (+ 3 minuty) jest mniejszy niż aktualny czas, jeśli tak to usuwamy go, po przejrzeniu wszystkiego zamykamy katalog.
Teraz napiszmy dwie krótkie metody, pierwsza będzie dodawać do $arrUserConfig nową zmienną, a druga będzie usuwać zmienne.
function setValue( $mixKey, $mixValue ) { $this -> arrUserConfig[$mixKey] = $mixValue; } function unSetValue( $mixKey = null ) |
Pierwszej funkcji nie ma co tłumaczyć, po prostu podajemy klucz i wartość na tej podstawie do tablicy $arrUserConfig jest dodawana nowa para klucz => wartość. Natomiast w przypadku unSetValue() (2) podajemy klucz i na jego podstawie usuwamy odpowiednią wartość, (1)chyba, że klucz nie został podany wtedy kasujemy całą tablice.
Będziemy jeszcze potrzebować funkcję, która po wykonaniu skryptu, dokona serializacji naszego obiektu, żebyśmy mogli go później odtworzyć na „innej” stronie, tym zadaniem zajmie się metoda unRegister(), jako, że tą metodę będziemy wywoływać przy każdym wywołaniu skryptu to karzemy jej również usuwać śmieci.
function unRegister( $objSession ) { // 1 if ( $this -> blnEndSession == false ) { // 2 $strSerialized = serialize( $objSession ); file_put_contents( SESSIONS_PATH . $this -> strSID . ‘.ses’, $strSerialized ); } |
.(1) Jeżeli zmienna blnEndSession jest ustawiona na false to pomijamy wykonywanie serializacji, (2) w przeciwnym razie serializujemy nasz obiekt, który został podany jako argument i zapisujemy do pliku. (3) Na koniec uruchamiamy metodę garbageCollection().
Na koniec została nam metoda o nazwie destroy(), co będzie robić? Wiadomo, niszczyć sesje, a dokładniej kasować plik w którym jest zapisana.
function destroy() { $strFile = SESSIONS_PATH . $this -> strSID . ‘.ses’; // 1 if ( @unlink( $strFile ) ) { $this -> blnEndSession = true; return true; // 2 |
Funkcja jest krótka, a zasada działania prosta, (1) jeżeli uda się skasować plik przechowujący dane sesji to zmieniamy blnEndSession na true i zwracamy true, (2) w przeciwnym wypadku zwracamy false.
I to już koniec, teraz pokaże jeszcze jak z tego korzystać i już klasa będzie nadawać się do wykorzystania we własnych projektach.
$objMySession = ‘’; // uruchamiamy sesje bez definiowania nowego obiektu session::create( $objMySession ); // teraz już $objMySession zawiera metody i zmienne klasy session // to będzie działać tak jak poprzednia instrukcja // jako argument przekazujemy caly obiekt i zachowujemy go w pliku |
Warto pamiętać, że session::create() i $nazwaObiektu->unRegister muszą być bezwzględnie wykonane za każdym razem gdy chcemy skorzystać z klasy session. Ostatnia rzecz o której trzeba wspomnieć, to ,że takie rozwiązanie nie nadaje się do przechowywania tajnych informacji np. haseł gdyż wszystkie informacje przechowywane są w ogólnodostępnym katalogu i każdy ma do niego dostęp. Jednak nic nie stoi na przeszkodzie aby zmodyfikować klasę session tak aby zawartość pliku była zakodowana, zachęcam do zabawy.
To tyle odnośnie sesji w następnym artykule pokaże jak przy wykorzystaniu tej klasy można zaimplementować koszyk na zakupy, więc ten artykuł jest w zasadzie wstępem do artykułu na temat trochę bardziej skomplikowanego zagadnienia.