Wprowadzenie do klas w języku PHP

  1. Dla kogo przeznaczony jest ten artykuł
  2. Wprowadzenie
  3. Cele kursu
  4. Informacje podstawowe
  5. Wskazówki wstępne
  6. Jak to działa
  7. Kompletny kod
  8. Przydatne adresy

1. Dla kogo przeznaczony jest ten artykuł

Artykuł ten przeznaczony jest dla średniozaawansowanych i zaawansowanych programistów PHP.

2. Wprowadzenie

Dzięki temu artykułowi dowiesz się jak pisać i używać klas w celu stworzenia elastycznego i łatwego w utrzymaniu kodu.

Czytając ten tutorial dowiesz się jak utworzyć prostą klasę, zaopatrzoną w kilka podstawowych funkcji zwiększających bezpieczeństwo: sprawdzanie logowań użytkowników. Mimo że klasa ta odpowiada funkcjonalnością najnowszym modelom bezpieczeństwa, to nie zapewnia go ona w 100%.

Przykład przedstawiony w tym artykule opisuje bardzo prosty ale skuteczny sposób hakowania stron internetowych: ręczne modyfikowanie adresów URL z poziomu okna przeglądarki. Spośród wielu rodzajów ataków na strony WWW, ten jest jednym z najprostszych do przeprowadzenia i zarazem jednym z tych, przed którymi najtrudniej się obronić. Złośliwy użytkownik może na przykład zmienić ceny w koszyku lub uzyskać dostęp do miejsc, które wymagają autoryzacji. Dokonać tego można modyfikując parametry przechowywane w adresie URL lub zapisując stronę na dysku i modyfikując jej ukryte zmienne. Szczególną uwagę należy poświęcić stronom, na których wypełniane są wszelkiego rodzaju zestawy formularzy, następujących po sobie w określonej kolejności, np. sklepy internetowe, formularze zamówienia czy rejestracja użytkowników.

Przykładowa klasa jest odporna na podmianę parametrów adresu URL, dzięki wykorzystaniu funkcji wbudowanych PHP. Pierwszą z nich jest funkcja getenv(), która służy do odczytywania wartości nagłówków HTTP. Odczytany nagłówek jest przechowywany w zmiennej HTTP_REFERER, która przechowuje pełną ścieżkę do strony, z której przyszedł użytkownik. Nasza klasa będzie sprawdzała czy nagłówek posiada odpowiednią wartość dzięki czemu możliwe będzie odróżnienie czy użytkownik wchodzi z właściwej strony, czy też nie.

Opisywana klasa ma budowę modularną, tzn. składa się z kilku funkcji, z których każda ma wykonywać określone zadanie. Pierwsza z nich została już opisana w poprzednim akapicie. Druga funkcja będzie odpowiedzialna za wyświetlanie informacji o nieprawidłowej stronie, z której przyszedł użytkownik oraz zapisywanie adresu IP tego użytkownika do pliku, trzecia zaś, będzie sprawdzać czy i ile razy określony użytkownik został już wcześniej zapisany. Czwarta funkcja będzie odpowiedzialna za kontrolę sesji.

Głównym celem tego artykułu, jak już było wspomniane, jest nauka wykorzystania klas jako podstawy tworzonych aplikacji. Nauczysz się jak dobrze rozplanować pisanie poszczególnych modułów, tak aby kod był elegancki i przejrzysty oraz by można go było zaimplementować do innych projektów PHP. Artykuł ten ukazuje wszechstronność użycia klas, które pozwalają na ponowne wykorzystanie kodu i na wykonywanie nawet bardzo skomplikowanych zadań w sposób prosty i efektywny.

Mimo że przykładowa klasa stworzona w tym tutorialu jest w pełni funkcjonalna, to służy ona tylko jako wstęp do programowania obiektowego, jako że nie pokazuje w pełni dostępnych możliwości. Na przykład, klasy mogą komunikować się i manipulować danymi zawartymi w bazach danych, a także mogą być rozszerzane o nowe funkcje, które nie były początkowo planowane.

Z założenia głównym przeznaczeniem klas było ich zastosowanie w dużych i skomplikowanych aplikacjach. Mimo to, mogą być one także bardzo użyteczne w mniejszych aplikacjach webowych, w szczególności jeśli planujemy ponowne użycie kodu. Wystarczy poświęcić trochę czasu na utworzenie wyspecjalizowanej klasy, wykonującej jakieś skomplikowane zadanie, aby zaoszczędzić sobie niepotrzebnego pisania tego samego lub podobnego kodu w przyszłości.

3. Cele kursu

Czytając ten tutorial, nauczysz się:

  • Czym są klasy i kiedy ich używać
  • Definiować klasy
  • Definiować i używać funkcji wewnątrz klas
  • Definiować i używać zmiennych wewnątrz klas
  • Definiować i używać konstruktorów wewnątrz klas
  • Tworzyć kompletne i funkcjonalne klasy
  • Implementować instancje klas
  • Ponownego użycia kodu
  • Zmieniać wartość zmiennych klasy
  • Używać następujących funkcji PHP:
  • Używać następujących operatorów PHP
  • Używać następującej zmiennej sesyjnej:
    • PHPSESSID
  • Używać następujących zmiennych serwera
    • HTTP_REFERER
    • REMOTE_ADDR

4. Informacje podstawowe

Co to jest klasa?

Klasa to zbiór powiązanych ze sobą zmiennych i funkcji (zwanych też atrybutami lub metodami). Ich zadaniem jest opisywanie i przetwarzanie danych pochodzących od określonego obiektu (lub klasy). Obiektem może być prawie wszystko co jest częścią kodu. Na przykład, klasa może służyć do opisywania pewnej porcji danych lub wykonywać operacje związane z obsługą systemu pocztowego serwera.

Dobra klasa jest dobrze zaprojektowanym systemem. przewiduje wszystkie możliwe zdarzenia, dzięki czemu dostajemy właściwą odpowiedź bez względu na rodzaj i ilość wprowadzonych danych lub parametrów. Dobre klasy działają jak potężny podzespół poleceń, działający niewidocznie ale wykonujący swoją pracę bezbłędnie.

Jako przykład użycia klasy rozpatrzymy następujący przypadek: system rezerwacji linii lotniczych. System posiada następujące funkcje: zarządzanie lotami, ustalanie rozkładu lotów dla załogi oraz rezerwację biletów na samoloty. Każda funkcja jest częścią klasy. Jako pasażer, nie masz dostępu do samych funkcji, ale możesz zobaczyć ich atrybuty: numer lotu, czas odlotu i przylotu, cena biletu oraz czy są miejsca przy oknie. Wywołanie jednej z funkcji, np. rezerwacja biletu, powoduje zmiany kilku atrybutów klasy: informacja o częstości lotów pasażera, potrzebna ilość posiłków w czasie lotów oraz informacja o tym, czy są jeszcze wolne miejsca.

Mając utworzoną taką klasę, można ją wykorzystać ponownie przy tworzeniu systemu rezerwacji dla innych linii lotniczych. Nie ma potrzeby pisać nowego systemu rezerwacji. Tak właśnie działają klasy w programowaniu. Pozwalają one na tworzenie ogólnych funkcji, których można użyć w różnych aplikacjach. Aby użyć funkcji zdefiniowanych w określonej klasie wystarczy tylko zadeklarować jej instancję ("utworzyć nową kopię").

W pewnym sensie, już od dawna używasz klas programując w PHP, np. tworząc uchwyt do pliku przy jego otwieraniu, tworzysz obiekt. Odbierając dane z bazy danych SQL, otrzymujesz obiekt i zestaw jego atrybutów, np. przy użyciu funkcji numrows. Pomyśl o tym, jak te informacje odnoszą się do tego, co powiedzieliśmy wcześniej, a zrozumiesz jak bardzo klasy ułatwiają ci życie.

5. Wskazówki wstępne

Zawsze bądź zorganizowany. Kod PHP zawsze przechowuj w plikach z rozszerzeniem .php. Klasy przechowuj w plikach o rozszerzeniu .inc i dołączaj je tylko wtedy, gdy są ci potrzebne.

Kod naszej przykładowej klasy został napisany z myślą o dobrym stylu programowania. Klasy i funkcje mają z reguły budowę modularną. Każda funkcja jest krótka i zwięzła oraz wykonuje najwyżej dwa zadania.

W miarę jak aplikacje rozrastają się i kod staje się coraz bardziej skomplikowany, klasy pomogają utrzymać porządek i uniknąć dublowania nazw zmiennych i funkcji. Dzięki klasom unikniesz błędów związanych z nazewnictwem, jeśli pracujesz w zespole. Zmienne i funkcje w klasach nie są widoczne poza nimi, dzięki czemu nie ma problemów z nadawaniem im nazw.

Klasy są bardzo dobrym narzędziem służacym do optymalizowania czasu pracy. Solidnie napisana klasa pozwala na wielokrotne wykorzystanie kodu, dzięki czemu oszczedzasz na czasie i pracy.

6. Jak to działa

Każdy krok ilustrowany jest odpowiednim przykładem. Skrypty PHP umieszczamy pomiędzy znacznikami <?php oraz ?>

Definiowanie klasy

Poniższy przykład pokazuje sposób definiowania klasy poprzez nadanie jej nazwy. Zauważ, że cała treść klasy powinna znajdować się pomiędzy nawiasami klamrowymi { oraz }.

Definiowanie klasy przy pomocy deklaracji jej nazwy.

class Security {
    ...
    }

Nazwą klasy może być każde słowo za wyjątkiem słów kluczowych PHP.

Weryfikacja adresu strony

  • Deklaracja funkcji klasy o nazwie verifyReferer
  • Akceptacja wartości zmiennej $ref, która wskazuje na stronę, z której powinien wejść odwiedzający
  • Sprawdzenie rzeczywistej strony, z której nastąpiło wejście, przy pomocy odczytania jej nagłówka (HTTP_REFERER).
  • Jeśli wejście nastąpiło z niewłaściwej strony, nastąpi wywołanie funkcji printbadRef (funkcja zdefiniowana jest w dalszej części kodu).

class Security {
...
    function verifyReferer ($ref) {
       $headerref = getenv("HTTP_REFERER");
       if ($headerref == $ref) {
              return true;
       } else {
         $this -> printbadRef ();
       }
   }
}

Nagłówek HTTP_REFERER może zawierać więcej informacji niż znajduje się w zmiennej $ref, dzięki czemu porównywane wartości mogą nie być identyczne. Nie oznacza to wcale, że użytkownik, który wszedł na stronę jest na pewno hakerem. Sposób radzenia sobie z tym problemem został przedstawiony na końcu artykułu. Tutaj skupiliśmy się tylko na najważniejszych sprawach w celu zachowania przejrzystości.

Funkcje w klasach deklaruje się dokładnie tak samo jak poza nimi. Różnica między funkcją klasy i zwykłą funkcją polega na tym, że ta pierwsza może zostać wywołana tylko przy pomocy instancji danej klasy.

Wewnątrz klasy, zapis w postaci $this -> oznacza "ten obiekt", czyli egzemplarz klasy, z której został on wywołany. Pozwala to funkcjom obiektu na dostęp do jego zmiennych i innych funkcji.

Wywoływanie funkcji wewnątrz klasy dokonuje się przy pomocy następującej składni $this->printbadRef();.

Zauważ, że przy pomocy jednej metody (verifyReferer)można wywołać inną metodę tej samej klasy (printbadRef), zanim ta druga została zdefiniowana. Zasada ta odnosi się także do zwykłych funkcji w PHP 4, ale dla tych, którzy korzystali do tej pory z PHP 3 może wydawać się nieco dziwna.

Mechanizm obsługi nieprawidłowych adresów

Struktura klasy

  • Deklaracja metody klasy o nazwie printbadRef.
  • Wyświetlenie strony z informacją o błędzie (jeśli wejście nastąpiło z niewłaściwej strony).
  • Odczytanie adresu IP odwiedzającego (REMOTE_ADDR) przy pomocy nagłówka wysyłanego razem z żądaniem wyświetlenia strony.
  • Zapisanie do pliku "badips.txt" adresów IP użytkowników, którzy weszli na stronę w niewłaściwy sposób, w celu wykrycia powtarzających się ataków.

  
    class security {
    ...
    function printbadRef {
      include ("badref.html");
      $myfile = fopen ("badips.txt", "a");
      fputs($myfile, getenv("REMOTE_ADDR")."\n");
      fclose($myfile);
      exit;
    }
  }
	
  

Wskazówki

Zwróć uwagę na modularną budowę klasy. Funkcja verifyReferrer sprawdza, czy wejście nastąpiło z właściwej strony. Jeśli tak, to wywoływana jest funkcja printbadRef, której zadaniem jest wyświetlenie informacji o błędzie oraz zapisanie adresu IP użytkownika do pliku. Następnie funkcja verifyIP sprawdza czy określony adres IP sie nie powtórzył. Każda z tych funkcji jest krótka i wykonuje tylko jedno zadanie.

Sprawdzanie adresu strony, z której nastąpiło wejście

Mamy już zadeklarowaną klasę Security. Aby skorzystać z jej funkcjonalności, musimy utworzyć jej nowy obiekt. Kod w poniższym przykładzie sprawdza czy dany użytkownik wszedł z odpowiedniej strony page1.phtml.

Zadania do wykonania

  • Utworzenie egzemplarza klasy o nazwieSecurity
  • Blokowanie użytkowników wchodzących ze stron innych niż page1.phtml
    $sec = new Security;
    $sec->verifyReferer("http://www.here.com/page1.phtm");

Wskazówki

Aby utworzyć nowy egzemplarz klasy, należy użyć konstruktora new przy zastosowaniu następującej składni:

    $sec = new Security;

Powyższy przykład tworzy nowy egzemplarz (obiekt) klasy o nazwie Security.

Obiektami operuje się w taki sam sposób jak zwykłymi zmiennymi przechowującymi liczby lub ciągi znaków. Dzięki temu można bez problemu modyfikować jeden obiekt klasy bez obawy, że naruszymy inne jej obiekty. W tym samym czasie może być aktywnych wiele różnych egzemplarzy jednej lub wielu klas. Oczywiście nazwy obiektów muszą być unikalne.

W celu wywołania funkcji obiektu klasy, należy zastosować następującą składnię:

    $sec->verifyReferer();

Tej samej składni użyliśmy do wywoływania funkcji wewnątrz klasy w przykładzie powyżej:

    $this->printbadRef();

A więc, aby odwołać się do funkcji wewnątrz klasy, należy zastosować składnię:
"obiekt->funkcja"

Można także zastosować alternatywną metodę wywoływania funkcji w postaci: Class::function($vars). Przy zastosowaniu tej metody zmienna $this nie jest deklarowana. Zapis $vars oznacza parametry funkcji.

Wykrywanie powracających złośliwych użytkowników

Poniższy kod przedstawia funkcję o nazwie verifyIP, która sprawdza, czy dany użytkownik próbował już wcześniej wejść na stronę w niewłaściwy sposób. Jeśli adres IP takiego użytkownika figuruje już na "czarnej" liście, to następuje wyświetlenie strony z błędem nawet gdy wchodzi on tym razem z dobrej lokalizacji.

Jest to dość brutalne traktowanie użytkownika, który mógł popełnić po prostu błąd lub powziąć postanowienie poprawy. Na samym końcu tego artykułu znajduje się trochę mniej radykalna wersja kodu. Tutaj opisane są tylko najważniejsze części funkcji.

Struktura

  • Deklaracja funkcji o nazwie verifyIP
  • Odczytanie adresu IP użytkownika
  • Jeżeli w pliku badips.txtznajdują się jakieś zapisy, to jego zawartość zostaje załadowana jako tablica do zmiennej $myfile
  • Sprawdzanie czy dany adres IP figuruje już w bazie danych
  • Jeśli adres użytkownika znajduje się na "czarnej" liście to następuje wywołanie funkcji printbadRef
    class Security {
      ...
      function verifyIP () {
        $IP = getenv("REMOTE_ADDR");
        if (file_exists("badips.txt")) {
          $myfile = file("badips.txt");
        }
        for ($index = 0; $index < count($myfile); $index++) {
          if ($IP == chop($myfile[$index])) {
            $this->printbadRef();
          }
        }
      }
    ...
    }

Wskazówki

Zmienna, np. $IP, która jest zdefiniowana wewnątrz funkcji klasy, dostępna jest w każdym miejscu tej funkcji. Zachowanie to jest podobne do zwykłych funkcji.

Sprawdzanie linku referencyjnego oraz wykrywanie powracających "złośliwców".

Rozszerzymy teraz trochę funkcjonalność naszego poprzedniego kodu. Poza sprawdzaniem czy wejście nastąpiło z odpowiedniej strony, będziemy wykrywać czy dany użytkownik próbował już dokonać tego wcześniej czy nie.

Struktura

  • Zdefiniowanie nowej instancji klasy Security
  • Zezwolenie użytkownikowi na odwiedzenie strony tylko w przypadku gdy wejście nastąpiło ze strony o nazwie page1.phtml
  • Sprawdzenie czy złośliwy użytkownik już kiedyś wchodził na stronę
    $sec = new Security;
    $sec -> verifyReferer("http://www.here.com/page1.phtml");
    $sec->verifyIP();

Wskazówki

Należy być bardzo ostrożnym implementując taką funkcję do sprawdzania adresu IP. Może się zdarzyć, że niesłusznie zabronimy dostępu niektórym użytkownikom, którzy na stronę weszli przypadkowo (np. stary adres z ulubionych). Może się też zdarzyć, że przy dynamicznym przydzielaniu adresów, adres zapisany na "czarnej liście" należy już do innej osoby. Rozwiązaniem mogłoby być określenie jakiegoś przedziału czasu, w którym użytkownik z pod danego adresu IP nie może wejść na stronę.

Funkcje sesji

Nasza następna funkcja, służąca do obsługi sesji, będzie zezwalała na poruszanie się po stronie tylko tym użytkownikom, których sesja jest aktywna. W wielu przypadkach ważne jest, aby nie blokować użytkownikom dostępu do jakichkolwiek podstron, ale zezwalać im na poruszanie się po stronie tylko gdy aktywna jest aktualna sesja. Taka kontrola sesji może być użyteczna w aplikacjach służących do odbioru poczty email. Dzięki niej odchodząc od monitora na dłuższy czas, użytkownik nie musi się obawiać, że ktoś przeczyta jego korespondencję ponieważ sesja wygasa po określonym czasie.

Struktura

  • Deklaracja funkcji verifySession
  • Sprawdzanie czy istnieje już domyślna zmienna sesji $PHPSESSID
  • Wyświetlanie strony z błędem jeśli nie istnieje aktywna sesja

    class Security {
    ...
      function verifySession() {
        if (!isset($PHPSESSID))  {
          $this->printbadRef();
        }
      }
    ...
    }
  

Wskazówki

Funkcja verifySession może być wywołana tylko po uprzednim rozpoczęciu lub zarejestrowaniu sesji. Dodatkowo, chcąc zmienić nazwę sesji, należy dokonać także zmian w kodzie funkcji.

Podsumowanie funkcji bezpieczeństwa

Na koniec utworzymy funkcję, w której zsumowane będą możliwości wszystkich funkcji bezpieczeństwa. Użytkownik, który nie przeszedł nawet tylko jednego testu, nie będzie mógł obejrzeć strony.

Struktura:

  • Deklaracja funkcji verifySecurity
  • Sprawdzanie czy użytkownik wchodzi z właściwego adresu
  • Sprawdzanie czy istnieje aktywna sesja
  • Sprawdzanie czy użytkownik ma już na swoim koncie jakieś nielegalne działania
    class Security {
    ...
      function verifySecurity($expectedpage) {
        $this->verifyReferrer($expectedpage);
        $this->verifySession();
        $this->verifyIP();
      }
    ...
    }

Wskazówki

Zauważ w jaki sposób funkcja verifySecurity przekazuje parametr zawierający właściwy link referencyjny ($expectedpage)). Parametr przekazywany jest bezpośrednio do funkcji verifyReferrer.

Konstruktory

Inną właściwością klaskonstruktory. Konstruktory to funkcje, które wywoływane są podczas tworzenia nowych obiektów klasy. Konstruktor może służyć do określania domyślnych wartości i atrybutów. Dobrym przykładem użycia konstruktorów w klasie Security byłoby zezwolenie projektantowi na stworzenie jego własnej wersji strony badref.html. Dzięki temu możliwe jest stworzenie różnych stron dla każdego działu witryny, zamiast tworzenia jednego, bardzo dużego pliku dla wszystkich.

Aby utworzyć konstruktor, który uruchamia się automatycznie podczas tworzenia klasy, należy nadać tę samą nazwę zarówno funkcji jak i klasie. W poniższym przykładzie, za każdym razem kiedy tworzona jest nowa instancja klasy Security, automatycznie uruchamiany jest konstruktor o tej samej nazwie, który nadaje domyślną wartość zmiennej $badref.

Struktura

  • Deklaracja zmiennej $badref.
  • Deklaracja konstruktora Security.
  • Nadanie domyślnej wartośći zmiennej $badref.
    class Security {
    ...
      var $badref;
      function Security {
        $this -> badref = "badref.html";
      }
    ...
    }

Wskazówki

Wszystkie zmienne wykorzystywane przez klasę muszą być zadeklarowane przed jakąkolwiek funkcją lub konstruktorem.

Aby odwołać się do zmiennej $badref wewnątrz kodu klasy należy użyć składni: $this->badref

Podobnie odnosimy się do zmiennej klasowej $badref wewnątrz kodu aplikacji (gdzie zdeklarowana jest instancja klasy o nazwie sec). Składnia jest następująca: $sec ->badref.

Jak widać powyżej, nazwa zmiennej badref nie występuje samodzielnie i nie ma przed nią znaku $. Dzieje się tak dlatego, że aby skorzystać ze zmiennej należy posłużyć się składnią "$obiekt->nazwazmiennej", a jak wiadmomo klasa jest obiektem. Identyfikatorem zmiennej jest zapis w postaci "obiekt->nazwazmiennej" z poprzedzającym znakiem "$". W naszym przykładzie nazwa zmiennej to obiekt->badref a nie samo badref. Dzieje się tak dlatego, że zmienna badref jest własnością obiektu (zmienną klasy Security).

Należy zauważyć, że zapis "$object->nazwazmiennej" to nie jest to samo co
"$object ->$nazwazmiennej". Drugi zapis spowoduje podstawienie zawartości zmiennej $nazwazmiennej w jej miejsce a jej nazwa zostanie zmieniona na "$object->zawartosc zmiennej $nazwazmiennej".

W języku PHP 4 nie ma destruktorów, które znane są programistom C++, ponieważ PHP automatycznie zwalnia nieużywaną pamięć. Więcej informacji na ten temat można przeczytać w artykule Reference Counting, którego autorem jest Andy Gutmans.

Zmiana strony nieprawidłowego odwołania

Funkcja o nazwie set_badref pozwala aplikacji na zmianę wartości zmiennej $badref przy pomocy klasy Security. To znaczy, zmienia ona stronę docelową w przypadku gdy użytkownik okaże się potencjalnie niebezpieczny.

Struktura

class Security {
...
  function set_badref($badref_page) {
    $this ->badref = $badref_page;
  }
...
}

Wskazówki

Pamiętaj: Elastyczność i możliwość wprowadzania zmian wartości domyślnych są najważniejszymi cechami wyróżniającymi dobrze napisaną klasę. Klasa powinna nie tylko być prosta w użyciu, ale także pozwalać na dokonywanie zmian według indywidualnych potrzeb użytkowników.

Zmiana strony docelowej dla użytkowników wchodzących z nieprawidłowego adresu

Aby zmienić lokację $badref w skrypcie, należy wywołać set_badref natychmiast po utworzeniu nowej instancji klasy. Wtedy odwiedzający zostanie przekierowany do nowej strony, zamiast do domyślnej badref.html. Lokalizację można zmieniać wielokrotnie, w zależności od tego, co zrobił użytkownik.

Struktura

$sec = new Security;
$sec->set_badref("5yearsand50000dollars.html");
$sec->verifyReferer("http://www.here.com/page1.phtml");

Wskazówki

Nie ma potrzeby korzystać z wszystkich możliwości klasy. Nie wywołuj set_badref jeśli nie ma ku temu wyraźnej potrzeby.

Jeśli sytuacja wymaga użycia różnych wartości zmiennej $badref, to ich zmiany należy dokonywać przy użyciu set_badref.

7. Kompletny kod

Poniżej znajduje się pełny kod aplikacji, opatrzony wieloma komentarzami. Klasa security została wzbogacona o dodatkowe linijki kodu, aby podnieść jej niezawodność.

Ważne:

Przykładowy kod umieszczony jest w dwóch plikach (.inc oraz .php), tak jak radziłem we "Wskazówkach wstępnych".

Komentarze poprzedzone są znakami "//". Kod PHP zawarty jest pomiędzy znacznikami "<?php" i "?>".

Plik 1: Klasa Security

<?php

// Deklaracja klasy poprzez nadanie jej nazwy

class Security {

    // zmienne klasy muszą zsotać zadeklarowane przed funkcjami
    var $badref;
	
    // Ten konstruktor klasy ma taką samą nazwę jak sama klasa,
    // dzięki czemu jest on wywoływany automatycznie za każdym razem,
    // gdy tworzona jest nowa instancja klasy. Konstruktor ten ustawia
    // wartość domyślną dla "strony nieprawdiłowego odwołania".
    function Security() {
        $this->badref = "badref.html";
    }
    
    
    // Funkcja ta sprawdza skąd przyszedł dany użytkownik,
    // przy pomocy funkcji wbudowanej PHP "getenv" oraz
    // zmiennej HTML HTTP_REFERER
    function verifyReferer($ref) {
        $headerref = getenv("HTTP_REFERER");
        
        // Funkcja wbudowana PHP "ereg" przeszukuje łańcuchy znaków
        // w poszukiwaniu wyrażeń regularnych. W tym przypadku wyrażeń,
        // w których znakiem rozdzielającym jest znak "?".
        if(ereg("\?" ,$headerref)) {
            
            // Funkcja wbudowana PHP "split" rozbija łańcuch na dwie
            // części. W tym przypadku podział następuje w miejscu, gdzie
            // pojawia się pierwszy znak "?". Pierwsza część
            // rozbitego łańcucha przechowywana jest jako parametr
            // operatora "list", a druga część jako jego drugi
            // parametr. Strona, z której nastąpiło wejście, lub jej
            // adres, jest reprezentowana przez tę część łańcucha
            // HTTP_REFERER, która znajduje się przed pierwszym "?".
            list($url,$getstuff) = split("\?",$headerref);
            $headerref = $url;
        }
        if ($headerref==$ref) {
            return true;
        } else {
            
            // Użytkownik wszedł z niewłaściwej strony,
            // wyświetli się więc strona błędu
            $this->printbadRef();
        }
    }
    
    // Poniższa funkcja sprawdza, czy adres IP danego użytkownika nie
    // znajduje się na liście użytkowników wchodzących z niewłaściwej
    // lokalizacji, nawet jeżeli tym razem wchodzi on ze strony
    // prawidłowej.
	
    function verifyIP () {
        $count = 0;
        
        // Sprawdzanie IP użytkownika przy pomocy funkcji PHP "getenv"
        // oraz zmiennej HTML REMOTE_ADDR
        $IP = getenv("REMOTE_ADDR");
        if (file_exists("badips.txt"));
        
            // Wczytaj zawartość pliku do tablicy, której każdy element
            // zawiera jedną linijkę zawartości pliku
            // (linijki oddzielone są od siebie znakiem nowego wiersza)
            $myfile = file("badips.txt");
        }
        for ($index=0; $index < count($myfile); $index++) {
            
            // Użycie funkcji "chop" w celu usunięcia znaku nowej linii
            if ($IP == chop($myfile[$index])) {
                $count++;
            }
        }
        
        // reaguj tylko w sytuacji, gdy dany adres IP powtórzył się
        // conajmniej trzy razy, zamiast używania stałej liczby (3).
        // Przydatne mogłyby się okazać następujące kroki:
        // 1) deklaracja zmiennej klasy, np. $badcount;
        // 2) ustawienie wartości domyślnej w
        // konstruktorze Security, np. 3
        // napisanie funkcji klasy, np. set_badcount, aby
        // umożliwić zmianę zmiennej $badcount przez aplikację.
        // Podobną procedurę zastosowano w przypadku zmiennej $badref
        if($count > 3) {
            $this->printbadRef();
        }
    }
	
    // Poniższa funkcja sprawdza czy nie ma aktualnie otwartej sesji
    // sprawdzając zawartość zmiennej PHP $PHPSESSID
	
    function verifySession() {
        if (!isset($PHPSESSID)) {
            $this->printbadRef();
        }
    }
    
    // Jedynym zadaniem tej funkcji jest połączenie funkcjonalności
    // wszystkich trzech funkcji, które wywołuje. Parametr zawierający
    // właściwy adres odwołania ($expectedpage) jest przekazywany wprost
    // do funkcji verifyReferer.
	
    function verifySecurity($expectedpage) {
        $this->verifyReferer($expectedpage);
        $this->verifySession();
        $this->verifyIP();
    }
    
    // Funkcja ta służy do wyświetlania strony błędu użytkownikowi,
    // który zrobił coś niedozwolonego.
    function set_badref($badref_page) {
        $this->badref = $badref_page;

    // Poniższa funkcja jest wywoływana,
    // gdy użytkownik wchodzi z niewłaściwej strony
    function printbadRef() {
    
        // Użycie funkcji PHP "include" do wyświetlenia
        // użytkownikowi strony błędu
        include($this->badref);
		
        // Wczytanie zawartości pliku do tablicy, której każdy
        // element (oddzielony znakiem nowej linii)
        // zawiera jedną linijkę zawartości pliku
        $myfile = fopen("badips.txt", "a");
		
        // Dodawanie adresu IP użytkownika do listy użytkowników
        // wchodzących z niewłaściwej lokalizacji
        fputs($myfile, getenv("REMOTE_ADDR")."\n");
        fclose($myfile);
        exit;
    }
}

Plik 2: Segmenty skryptu PHP, który wykorzystuje funkcjonalność klasy Security

<?php
...
// Deklaracja nowej instancji ("sec") klasy Security.
// Do każdej funkcji w klasie (wewnątrz instancji "sec"),
// należy odwoływać się przy pomocy następującej składni:
// "$sec->function()"
$sec = new Security;

// Zmiana strony nieprawidłowego odwołania.
// Funkcja ta jest opcjonalna, jako że strona nieprawidłowego odwołania
// ma ustawioną wartośćdomyślną, który automatycznie jest ustawiana
// przez konstruktor dla każdej nowej instancji klasy Security
$sec->set_badref("5yearsand50000dollars.html");

// Porównaj stronę, z której wszedł użytkownik z adresem
// podanym do funkcji jako parametr
$sec->verifyReferer("http://www.here.com/page1.phtml");

// Sprawdź czy istnieje aktywna sesja. Należy pamiętać, że funkcja
// verifySession może być użyta tylko w przypadku, gdy została
// przed jej wywołaniem uruchomiona lub zarejestrowana sesja.
// Dodatkowo, należy wnieść poprawki do kodu jeśli zmienia się
// nazwę sesji (używając session_name).
$sec->verifySession();

// Sprawdź czy adres IP danego użytkownika znajduje się
// na liście użytkowników wykonujących nielegalne działania,
// nawet jeśli tym razem nie robi nic podejrzanego
$sec->verifyIP();

...

// Jedynym zadaniem tej funkcji jest wywołanie funkcji:
// verifyReferer, verifySession oraz verifyIP. Link strony,
// z której wchodzi użytkownik porównywany jest z adresem
// podanym jako parametr funkcji.
$sec->verifySecurity("http://www.here.com/page1.phtml");
...
?>

8. Przydatne adresy

Informacje na temat klas w PHP
http://www.zend.com/manual/language.oop.php

Informacje na temat funkcji PHP "getenv()"
http://www.zend.com/manual/function.getenv.php

Informacje na temat zmiennych CGI/Header
http://hoohoo.ncsa.uiuc.edu/cgi/env.html

"Klasy i PHP"
http://www.phpbuilder.com/columns/rod19990601.php3

"Dostęp do baz danych przy zastosowaniu klas"
http://www.devshed.com/Server_Side/PHP/Class/


Autor: Duncan Lamb przy współpracy z Zend.
Data: 28.03.2000
Tłumaczenie: Łukasz Piwko
Oryginalna wersja artykułu znajduje się pod adresem http://www.zend.com/zend/tut/class-intro.php