Programowanie obiektowe (ang. Object-Oriented Programming, OOP) to popularny paradygmat programowania, w którym kod organizujemy wokół obiektów – czyli struktur łączących dane oraz funkcje je przetwarzające. Zamiast pisać pojedyncze funkcje operujące na danych rozproszonych po całym programie, w OOP grupujemy powiązane ze sobą dane i operacje w jedną jednostkę zwaną klasą. Klasa pełni rolę szablonu (planu) definiującego obiekt, a każdy obiekt jest konkretną instancją tej klasy – można go traktować jak egzemplarz utworzony na podstawie planu
Taka organizacja ułatwia modelowanie realnych konceptów w kodzie: obiekt może odpowiadać rzeczywistemu bytowi (np. Samochód, Osoba, Produkt), posiadać określone cechy (dane) oraz wykonywać określone działania.
OOP przynosi wiele korzyści, zwłaszcza w większych projektach. Pozwala m.in. na enkapsulację (hermetyzację) danych – obiekty pilnują własnego stanu wewnętrznego i udostępniają do niego kontrolowany dostęp za pomocą metod. Ułatwia to również ponowne wykorzystanie kodu i rozbudowę projektu bez powielania tych samych fragmentów. Poniżej, krok po kroku, omówimy podstawowe pojęcia programowania obiektowego w PHP, takie jak klasy, obiekty, właściwości (atrybuty), metody, a także mechanizmy dziedziczenia oraz interfejsy. Wszystkie przykłady są zgodne ze składnią PHP 8 (i nowszych), co oznacza m.in. wykorzystanie deklaracji typów dla właściwości i parametrów metod.
Paradygmat obiektowy – Klasy i obiekty
Na początek wyjaśnijmy, czym są klasy i obiekty w kontekście OOP. Klasa to definicja pewnego typu obiektów, opisuje ona ich strukturę oraz zachowanie. Można powiedzieć, że klasa jest jak przepis lub plan: określa, jakie właściwości (czyli dane/cechy) oraz metody (czyli funkcje/zachowania) będą mieć obiekty utworzone na jej podstawie. Z kolei obiekt to konkretna instancja klasy, utworzona w trakcie działania programu. Jeżeli klasa jest przepisem, to obiekt jest „potrawą” powstałą według tego przepisu. Klasa jako abstrakcja istnieje w kodzie, a obiekt istnieje w czasie wykonywania programu (np. w pamięci komputera).
Przykład (poza kodem): Wyobraź sobie klasę Samochód – definicję, która mówi, że każdy samochód ma pewne właściwości, np. kolor, markę i prędkość maksymalną, oraz metody, np. jazda() czy hamuj(). Samochód opisany przez klasę to koncept, natomiast obiekt typu Samochód to już konkretny samochód – np. czerwony Ford Mustang, który ma ustawiony kolor na czerwony, markę na Ford, i może wykonać metodę jazda() (czyli np. ruszyć). Możemy utworzyć wiele obiektów na bazie jednej klasy (np. różne samochody o innych parametrach), podobnie jak według jednego planu architektonicznego można zbudować wiele domów.
Podsumowując: klasa definiuje strukturę i zachowanie, a obiekt tę definicję urzeczywistnia jako pojedynczy byt z własnym stanem. Klasa jest pewnym typem danych, który definiujemy sami, a obiekty są wartościami/zmiennymi tego typu.
Tworzenie klas w PHP – składnia, właściwości i metody
Skoro wiemy, czym jest klasa, zobaczmy jak zdefiniować własną klasę w PHP. Podstawowa składnia jest prosta: używamy słowa kluczowego class i nazwy klasy, a jej zawartość umieszczamy w nawiasach klamrowych { }. Wewnątrz klasy definiujemy właściwości (pola) oraz metody (funkcje należące do klasy). Na przykładzie omówimy te elementy:
- Właściwości – to zmienne przechowujące stan obiektu. Każda instancja (obiekt) klasy ma własne kopie tych zmiennych. W PHP definiujemy właściwości podobnie jak zwykłe zmienne, poprzedzając je deklaracją dostępu (np. public) i opcjonalnie określając typ danych oraz wartość domyślną.
- Metody – to funkcje zdefiniowane wewnątrz klasy, które opisują zachowanie obiektu. Metody mogą odczytywać lub modyfikować właściwości (stan) obiektu, wykonywać obliczenia, zwracać wartości, itp. Technicznie definiujemy je jak zwykłe funkcje, ale również poprzedzamy deklaracją dostępu (np. public). W ciele metody możemy odwoływać się do właściwości danej instancji poprzez specjalną zmienną $this (która reprezentuje „bieżący” obiekt).
- Konstruktor – to specjalna metoda o nazwie __construct, która automatycznie wykonuje się w momencie tworzenia nowego obiektu. Służy do początkowej inicjalizacji obiektu (np. ustawienia wartości początkowych właściwości). Konstruktor definiujemy jak zwykłą metodę, ale PHP wywoła go za nas przy użyciu new (o tym za moment). Jeśli nie zdefiniujemy konstruktora, PHP utworzy obiekt bez dodatkowych kroków inicjalizacji, a właściwości można ustawiać „ręcznie” po utworzeniu obiektu.
Przy definiowaniu właściwości i metod używamy tzw. modyfikatorów dostępu, które określają, z jakiego miejsca w programie można się do nich odwołać:
- public – właściwość lub metoda publiczna jest dostępna wszędzie (zarówno z zewnątrz obiektu, jak i wewnątrz klasy oraz w klasach dziedziczących).
- private – element prywatny jest dostępny tylko wewnątrz tej samej klasy. Nie można go odczytać ani zmienić bezpośrednio poza tą klasą (ani w obiektach, ani w podklasach). Służy to ukrywaniu wewnętrznej reprezentacji obiektu przed światem zewnętrznym.
- protected – element chroniony jest dostępny wewnątrz klasy oraz w jej podklasach (dziedziczących), ale nie z zewnątrz (nie można go użyć na obiekcie spoza definicji klasy). Jest to przydatne, gdy chcemy dopuścić użycie np. właściwości w klasach dziedziczących, ale nadal ukryć ją przed resztą programu.
Poniżej zdefiniujemy prostą klasę o nazwie Person (Osoba), która posłuży za przykład. Klasa ta ma dwie właściwości (imię oraz wiek osoby) oraz jedną metodę. Zaimplementujemy także konstruktor, aby wygodnie ustawiać te właściwości przy tworzeniu obiektu:
class Person {
public string $name; // właściwość publiczna przechowująca imię
public int $age; // właściwość publiczna przechowująca wiek
// Konstruktor wywoływany przy tworzeniu nowego obiektu:
public function __construct(string $name, int $age) {
// Słowo $this oznacza "ten konkretny obiekt". Używamy go, aby
// odwołać się do właściwości obiektu i ustawić je:
$this->name = $name;
$this->age = $age;
}
// Przykładowa metoda obiektu:
public function sayHello(): void {
echo "Cześć, mam na imię $this->name.";
}
}
Powyżej utworzyliśmy klasę Person ze wszystkimi wymaganymi elementami:
- Zaczyna się od deklaracji class Person { … }.
- Wewnątrz zadeklarowaliśmy dwie właściwości ($name i $age) poprzedzone modyfikatorem public i oznaczeniem typów (string i int). To oznacza, że każdy obiekt Person będzie przechowywał własne name i age, dostępne publicznie.
- Zdefiniowaliśmy konstruktor __construct, który przyjmuje dwa parametry (też ze zdefiniowanymi typami) i ustawia właściwości obiektu na przekazane wartości. Dzięki temu od razu przy tworzeniu obiektu będziemy mogli podać imię i wiek.
- Dodaliśmy metodę sayHello(), która wypisuje przywitanie z imieniem. Zauważ, że korzysta ona z $this->name, aby odczytać właściwość obiektu.
Uwaga – skrócona inicjalizacja właściwości (PHP 8)
W PHP 8 wprowadzono wygodną skróconą składnię inicjalizowania właściwości w konstruktorze (tzw. constructor property promotion). Pozwala ona zadeklarować właściwości bezpośrednio w definicji parametrów konstruktora, co upraszcza kod. Powyższą klasę Person moglibyśmy zdefiniować równoważnie krócej:
class Person {
public function __construct(
public string $name,
public int $age
) {
// ciało może być puste, PHP sam przypisze przekazane wartości
// do odpowiednich właściwości na podstawie powyższej deklaracji
}
public function sayHello(): void {
echo "Cześć, mam na imię $this->name.";
}
}
Takie zapisy skracają kod, ale mechanicznie działają tak samo jak poprzednia definicja – tworzą publiczne właściwości $name i $age oraz przypisują im wartości przekazane do konstruktora. W dalszych przykładach będziemy już używać zwykłej, pełnej formy dla przejrzystości, jednak warto wiedzieć o istnieniu tej nowej składni.
Instancjonowanie obiektów i korzystanie z metod i właściwości
Mając zdefiniowaną klasę, możemy teraz utworzyć obiekt tej klasy, czyli tzw. zainstancjować klasę. W PHP dokonujemy tego za pomocą operatora new. Wywołanie new NazwaKlasy() powoduje utworzenie nowego obiektu danego typu. Jeśli klasa posiada konstruktor, w nawiasach podajemy argumenty, które zostaną przekazane do __construct.
Kontynuując nasz przykład, utwórzmy dwa obiekty klasy Person i zobaczmy, jak korzystać z ich właściwości i metod:
$person1 = new Person("Jan", 20); // tworzymy obiekt Person, imię="Jan", wiek=20
$person2 = new Person("Anna", 22); // tworzymy inny obiekt Person, imię="Anna", wiek=22
// Dostęp do właściwości obiektu za pomocą operatora "->":
echo $person1->name; // wyświetli: Jan
$person2->age = 23; // zmieniamy wartość właściwości age obiektu person2 (Anna ma teraz 23 lata)
// Wywoływanie metody obiektu:
$person1->sayHello(); // wypisze: Cześć, mam na imię Jan.
$person2->sayHello(); // wypisze: Cześć, mam na imię Anna.
W powyższym kodzie widać kilka ważnych rzeczy:
- Obiekty tworzymy poprzez new Klasa(…). W naszym przypadku new Person(„Jan”, 20) wywołuje konstruktor klasy Person z argumentami „Jan” i 20, co skutkuje utworzeniem obiektu z ustawionym name = „Jan” i age = 20. Zmienna $person1 przechowuje referencję do tego obiektu. Analogicznie tworzymy $person2.
- Do właściwości obiektu odwołujemy się przez składnię $obiekt->właściwość. Np. $person1->name zwraca wartość właściwości name obiektu $person1. Możemy też przypisywać wartości: $person2->age = 23 zmienia wiek drugiej osoby. (Uwaga: w naszym przypadku właściwości są publiczne, więc mamy do nich dostęp bezpośrednio. Gdyby były prywatne, taka operacja spoza klasy byłaby niedozwolona i wymagałaby użycia metod get/set – na początek jednak trzymamy się publicznych dla prostoty).
- Metody obiektu wywołujemy podobnie, używając $obiekt->nazwaMetody(…). Przykładowo, $person1->sayHello() wykonuje metodę sayHello() na obiekcie $person1. W efekcie zostanie wypisany komunikat zawierający imię tej osoby. Każdy obiekt zachowuje się zgodnie ze swoją klasą – wywołanie sayHello() na $person2 działa, choć $person2 to inna instancja, bo klasa Person definiuje tę metodę i obie instancje ją odziedziczyły.
Warto zauważyć, że każdy obiekt ma własny stan – w naszym przykładzie $person1->name to „Jan”, a $person2->name to „Anna”. Obiekty te są niezależne, zmiana właściwości w jednym nie wpływa na drugi. OOP pozwala więc tworzyć wiele instancji z jednego schematu (klasy), każda z własnymi danymi.
Tip: Obiekt w PHP jest przekazywany przez referencję, co oznacza, że jeśli przekażemy obiekt do funkcji lub przypiszemy do innej zmiennej, nadal będzie to ten sam obiekt (dwie zmienne mogą referować ten sam obiekt). Dla początkujących wystarczy jednak pamiętać, że zmienna z obiektem to coś więcej niż prosta wartość – zawiera ona dostęp do struktury złożonej (danych i funkcji).
Dziedziczenie – klasy bazowe i pochodne
Jedną z najważniejszych cech paradygmatu obiektowego jest dziedziczenie. Dziedziczenie pozwala tworzyć nową klasę w oparciu o już istniejącą klasę, przejmując jej właściwości i metody. Nowa klasa nazywa się klasą pochodną (podklasą), zaś klasa oryginalna to klasa bazowa (lub rodzic). W PHP do wskazania dziedziczenia używamy słowa kluczowego extends przy definicji klasy pochodnej.
Dzięki dziedziczeniu możemy kod wspólny umieścić w klasie bazowej, a klasy pochodne rozszerzają jej funkcjonalność (dodają nowe właściwości/metody lub zmieniają te odziedziczone). Jest to przydatne przy modelowaniu hierarchii obiektów – np. klasa bazowa Osoba może zawierać cechy wspólne wszystkim osobom, a klasy pochodne Student czy Nauczyciel dodadzą specyficzne atrybuty lub zachowania.
Jak to działa? Klasa dziedzicząca automatycznie otrzymuje wszystkie publiczne i chronione właściwości oraz metody z klasy bazowej
. Nie musi ich ponownie definiować – są one dostępne jak część definicji podklasy. To oznacza, że np. jeśli Student dziedziczy po Person, to Student ma już właściwości $name i $age oraz metodę sayHello() tak jak Person. Klasa pochodna może jednak nadpisać wybrane metody klasy bazowej, definiując własną implementację o tej samej nazwie (i sygnaturze). Może też dodać nowe właściwości i metody, których nie ma w klasie bazowej.
Uwaga: Klasa pochodna nie ma dostępu do elementów zadeklarowanych jako private w klasie bazowej (są one ukryte). Dostaje tylko to, co jest public lub protected. Dlatego, jeśli chcemy by podklasy mogły korzystać z pewnej właściwości/metody, a równocześnie by była ona niewidoczna na zewnątrz obiektów, powinniśmy użyć modyfikatora protected w klasie bazowej.
Stwórzmy teraz klasę Student dziedziczącą po naszej klasie Person. Student będzie mieć te same właściwości co Person (imię, wiek) oraz dodamy mu nową właściwość major (kierunek studiów). Zaimplementujemy też własną wersję metody powitania, która uwzględni dodatkową informację:
class Student extends Person {
public string $major; // nowa właściwość dla Student (np. kierunek studiów)
// Konstruktor Student przyjmuje także kierunek studiów.
// Wykorzystujemy konstruktor Person do ustawienia imienia i wieku:
public function __construct(string $name, int $age, string $major) {
parent::__construct($name, $age); // wywołanie konstruktora klasy bazowej Person
$this->major = $major; // ustawienie nowej właściwości
}
// Nadpisujemy metodę sayHello odziedziczoną z Person:
public function sayHello(): void {
echo "Cześć, mam na imię $this->name i studiuję $this->major.";
}
}
Zwróć uwagę na kilka rzeczy w tej definicji:
- Użyliśmy extends Person – to oznacza, że Student dziedziczy po klasie Person. Dzięki temu Student automatycznie ma właściwości $name, $age oraz metodę sayHello() (choć tę akurat zaraz nadpisujemy).
- Dodaliśmy nową właściwość $major (kierunek). Student ma więc wszystko to, co Person, plus $major.
- Zdefiniowaliśmy konstruktor w klasie Student przyjmujący trzy parametry. Wewnątrz konstruktora wywołujemy parent::__construct($name, $age). Słowo kluczowe parent odnosi się do klasy bazowej, a wywołanie parent::__construct(…) uruchamia konstruktor z klasy Person. To pozwala nam wykorzystać logikę inicjalizacji z bazowej klasy (ustawienie imienia i wieku), a następnie dopisać własną (ustawienie $major). Gdybyśmy tego nie zrobili, musielibyśmy sami ustawić $this->name i $this->age – co oznacza powielenie kodu już istniejącego w Person. Wywołanie konstruktora bazowego eliminuje duplikację.
- Nadpisaliśmy metodę sayHello(). W klasie Person ta metoda wypisuje komunikat z imieniem; w klasie Student chcemy, by zawierała też informację o kierunku studiów. Dlatego definiujemy metodę o identycznej sygnaturze (sayHello(): void) w klasie pochodnej. Ta definicja zastąpi odziedziczoną wersję. Teraz dla obiektów Student wywołanie sayHello() będzie wykonywać nowy kod (zawierający także $this->major). W naszym przypadku nie użyliśmy parent::sayHello(), lecz mogliśmy – np. moglibyśmy najpierw wywołać kod z Person, a potem dopisać coś od siebie. Jeśli jednak całkowicie zmieniamy działanie metody, nie ma takiej potrzeby.
Teraz użyjmy klasy Student w praktyce:
$student = new Student("Jan", 20, "Informatyka"); // tworzymy obiekt Student (imię, wiek, kierunek)
$student->sayHello(); // wypisze: Cześć, mam na imię Jan i studiuję Informatyka.
echo $student->age; // odziedziczona właściwość age, wynik: 20
W powyższym kodzie widać, że obiekt klasy Student zachowuje się podobnie do obiektu Person, ale ma dodatkowe cechy. Wywołanie $student->sayHello() skorzystało z nadpisanej metody i wyświetliło komunikat uwzględniający kierunek studiów. Właściwość $student->age jest dostępna tak samo jak w Person (odziedziczona jako publiczna). Gdybyśmy wywołali $student->sayHello() bez nadpisywania tej metody, zostałaby wykonana wersja z klasy bazowej Person (odziedziczona, bo każda klasa dziedziczy publiczne/protected metody bazowe, dopóki ich nie nadpisze).
Dziedziczenie podsumowując: Pozwala tworzyć hierarchię klas od ogólnych do szczegółowych. Klasa pochodna rozszerza (ang. extends) klasę bazową, co sprzyja ponownemu użyciu kodu i organizacji. W PHP każda klasa może dziedziczyć tylko po jednej innej klasie (brak wielokrotnego dziedziczenia klas). Jeśli potrzebujemy rozdzielić wspólne cechy między różne gałęzie hierarchii, do akcji wkraczają interfejsy, które omówimy za chwilę.
Interfejsy – definicja i implementacja
Oprócz dziedziczenia klas, PHP oferuje mechanizm interfejsów. Interfejs można rozumieć jako zbiór metod (sygnatur metod) bez ich implementacji. Definiując interfejs, określamy co dana klasa musi robić (jakie metody udostępniać), ale nie jak to robi. Następnie różne klasy mogą implementować ten interfejs – czyli zobowiązać się do napisania tych konkretnych metod. Dzięki interfejsom możemy zaprojektować kod w formie kontraktów: jeśli klasa implementuje interfejs, to gwarantuje, że pewne metody w niej istnieją, co umożliwia traktowanie różnych obiektów w ten sam sposób, o ile spełniają ten kontrakt.
Interfejsy są przydatne, gdy chcemy zapewnić wspólne metody dla różnych, niezależnych klas. Pozwalają określić, jakie metody klasa powinna zaimplementować
, i ułatwiają używanie różnych klas w taki sam sposób. Innymi słowy, jeśli kilka klas implementuje ten sam interfejs, ich obiekty można będzie wykorzystać wymiennie tam, gdzie oczekujemy obiektu „mającego” dany interfejs – nawet jeśli te klasy nie są ze sobą powiązane przez dziedziczenie. (Jest to forma realizacji polimorfizmu, choć nie zagłębiamy się tutaj w to pojęcie.)
Definiowanie interfejsu: W PHP interfejs definiujemy podobnie jak klasę, z tą różnicą, że używamy słowa kluczowego interface zamiast class. Wewnątrz interfejsu umieszczamy nagłówki metod bez implementacji (czyli same deklaracje, zakończone średnikiem ;, bez ciała). Nie definiujemy w interfejsie właściwości (można jedynie stałe). Wszystkie metody zadeklarowane w interfejsie są domyślnie publiczne (nie podajemy modyfikatora dostępu – inny i tak nie jest dozwolony).
Implementacja interfejsu w klasie: Klasa implementuje interfejs używając słowa kluczowego implements w swojej definicji. Może implementować wiele interfejsów (oddzielamy ich nazwy przecinkami). Taka klasa musi zdefiniować wszystkie metody zadeklarowane w interfejsie (z dokładnie takimi samymi sygnaturami). Jeśli którejś zabraknie lub różni się sygnaturą, PHP zgłosi błąd. W jednej klasie możemy jednocześnie dziedziczyć po innej klasie (extends) i implementować interfejs(y) (implements). Implementacja interfejsu to po prostu zapewnienie ciał tych metod w klasie.
Przyjrzyjmy się przykładom, by to wyjaśnić. Zdefiniujmy interfejs Instrument zawierający metodę play() (graj). Następnie stworzymy dwie klasy: Gitara oraz Perkusja, które ten interfejs zaimplementują na różne sposoby:
interface Instrument {
public function play(): void; // każda klasa implementująca musi mieć tę metodę
}
class Gitara implements Instrument {
public function play(): void {
echo "Gitara: brzdęk!";
}
}
class Perkusja implements Instrument {
public function play(): void {
echo "Perkusja: bum!";
}
}
Wyjaśnienie:
- Interfejs Instrument definiuje jedną metodę play(). Nie ma ciała metody – to tylko „obietnica”, że klasa, która zaimplementuje ten interfejs, dostarczy własny kod tej metody.
- Klasa Gitara deklaruje implements Instrument, co zobowiązuje ją do napisania metody play(). Widzimy implementację: wypisuje tekst „Gitara: brzdęk!”.
- Klasa Perkusja również implementuje Instrument i zapewnia własną wersję metody play() – w tym przypadku wypisując „Perkusja: bum!”.
- Obie klasy mogą oczywiście mieć też inne metody czy właściwości (specyficzne dla siebie), ale kluczowe jest to, że spełniają kontrakt narzucony przez Instrument – czyli mają metodę play().
Teraz zobaczmy użycie takiego interfejsu w praktyce:
$guitar = new Gitara();
$drums = new Perkusja();
$guitar->play(); // wyświetli: Gitara: brzdęk!
$drums->play(); // wyświetli: Perkusja: bum!
Na pierwszy rzut oka może się to nie różnić od zwykłych klas – po prostu wywołujemy metodę na obiekcie danej klasy. Siła interfejsów ujawnia się jednak, gdy zauważymy, że nie musimy wiedzieć, z jaką konkretnie klasą mamy do czynienia, by móc wywołać play(). Wystarczy nam informacja, że obiekt implementuje interfejs Instrument. Możemy np. napisać funkcję:
function zagrajKoncert(Instrument $instr) {
// parametr $instr może być dowolnym obiektem implementującym Instrument
$instr->play();
}
Funkcja zagrajKoncert(Instrument $instr) przyjmie dowolny obiekt typu Instrument (czyli dowolny, który implementuje ten interfejs). W jej ciele wywołujemy $instr->play(), a dzięki interfejsowi PHP zapewnia, że taka metoda na pewno istnieje w obiekcie. W efekcie możemy przekazać do tej funkcji zarówno new Gitara(), jak i new Perkusja(), a nawet obiekty innych klas implementujących Instrument (gdyby istniały), i za każdym razem zadziała poprawnie wywołując odpowiednią implementację play(). To samo tyczy się np. przechowywania obiektów różnych klas we wspólnej tablicy Instrumentów i iterowania po nich.
Po co są interfejsy? Uogólniając, interfejsy pozwalają definiować role lub zachowania, które różne obiekty mogą przyjąć. Przykładowo interfejs JsonSerializable (wbudowany w PHP) wymaga metody jsonSerialize() – różne klasy mogą go implementować, aby ich obiekty dało się zamienić na JSON. Interfejsy przydają się też jako zamiennik wielokrotnego dziedziczenia: w PHP klasa może dziedziczyć tylko po jednej klasie, ale może implementować wiele interfejsów, więc może spełniać wiele „kontraktów”. Ułatwiają również pisanie czystego, modularnego kodu – można najpierw zdefiniować interfejsy (API), a potem tworzyć różne realizacje (klasy) tego API.
Warto pamiętać, że interfejsu nie da się instancjonować – nie możemy zrobić new Instrument(), bo to nie klasa z pełną implementacją, a jedynie zbiór metod do zaimplementowania. Interfejs służy wyłącznie jako opis wymagań dla klas.
Podsumowanie
- Klasa w PHP to definicja obiektu: określa zestaw właściwości (danych) i metod (funkcji) wspólnych dla wszystkich obiektów tego typu. Klasa jest jak szablon lub przepis, na podstawie którego tworzymy obiekty.
- Obiekt (instancja klasy) to konkretny egzemplarz utworzonej klasy, posiadający własny stan. Obiekty tworzymy za pomocą new Klasa(…). Możemy utworzyć wiele obiektów z jednej klasy, każdy z własnymi wartościami właściwości.
- Właściwości i metody definiujemy wewnątrz klasy. Właściwości przechowują stan obiektu, metody definiują jego zachowanie. Modyfikatory dostępu (public/private/protected) pozwalają kontrolować, kto ma dostęp do tych składowych (np. tylko sama klasa czy także jej podklasy, czy cały program).
- Konstruktor (__construct) to metoda uruchamiana automatycznie przy tworzeniu obiektu (new), służąca do inicjalizacji obiektu (np. ustawienia początkowych wartości właściwości).
- Dziedziczenie (extends) umożliwia tworzenie nowych klas na bazie już istniejących. Klasa pochodna dziedziczy publiczne i chronione właściwości/metody klasy bazowej
, dzięki czemu możemy ponownie wykorzystać kod. Podklasa może dodawać własne elementy oraz nadpisywać metody bazowe, aby zmienić lub rozszerzyć ich działanie. PHP wspiera dziedziczenie pojedyncze (jedna klasa bazowa).
- Interfejsy (interface … implements) pozwalają definiować zestaw metod bez implementacji – swego rodzaju kontrakt. Klasy implementujące interfejs muszą dostarczyć kod tych metod. Interfejsy umożliwiają stosowanie wspólnego zestawu metod w różnych klasach (nawet niezwiązanych dziedziczeniem) i traktowanie obiektów tych klas jednolicie, poprzez typ interfejsu
. Klasa może implementować wiele interfejsów, dzięki czemu łączy różne role. Interfejsów nie można instancjonować (nie tworzymy obiektów interfejsu bezpośrednio).
OOP to potężny paradygmat – na początku wymaga zrozumienia nowych pojęć, ale w zamian oferuje przejrzystą organizację kodu i skalowalność.