Lekcja 23 (OOP 3): Konstruktory, Destruktory i Metody Magiczne
Witaj w trzeciej lekcji poświęconej Programowaniu Obiektowemu w PHP! W poprzedniej lekcji nauczyliśmy się, jak definiować klasy, deklarować ich właściwości oraz tworzyć metody. Dowiedzieliśmy się również, jak tworzyć instancje klas (obiekty) i odwoływać się do ich składowych. Teraz pójdziemy o krok dalej i poznamy specjalne funkcje w klasach, które są automatycznie wywoływane w określonych momentach cyklu życia obiektu – mowa o konstruktorach i destruktorach. Dodatkowo, przyjrzymy się innym metodom magicznym w PHP, które pozwalają na dostosowanie zachowania obiektów w specyficznych sytuacjach.
Zrozumienie konstruktorów jest kluczowe dla prawidłowej inicjalizacji obiektów, czyli nadawania im początkowego stanu zaraz po utworzeniu. Destruktory pozwalają na wykonanie operacji porządkujących przed zniszczeniem obiektu. Metody magiczne natomiast otwierają drzwi do bardziej zaawansowanych technik programowania obiektowego, umożliwiając np. przeciążanie właściwości i metod czy definiowanie, jak obiekt ma być traktowany w kontekście rzutowania na string.
Konstruktory (Constructors)
Konstruktor to specjalna metoda w klasie, która jest automatycznie wywoływana w momencie tworzenia nowego obiektu (instancji) tej klasy za pomocą słowa kluczowego new
. Głównym zadaniem konstruktora jest inicjalizacja obiektu, czyli ustawienie początkowych wartości jego właściwości, wykonanie niezbędnych operacji konfiguracyjnych lub przygotowanie zasobów, których obiekt będzie potrzebował do działania.
W PHP konstruktor definiuje się jako metodę o specjalnej nazwie __construct
(dwa znaki podkreślenia na początku). Nazwa ta jest zarezerwowana i PHP automatycznie rozpoznaje ją jako konstruktor.
Składnia definicji konstruktora:
<?php
class MojaKlasa
{
public function __construct(/* argumenty */)
{
// Kod inicjalizujący obiekt
}
}
?>
Konstruktor, jak każda inna metoda, może przyjmować argumenty. Argumenty te są przekazywane do konstruktora podczas tworzenia obiektu, w nawiasach po nazwie klasy.
Przykład klasy Uzytkownik
z konstruktorem:
<?php
class Uzytkownik
{
public string $nazwaUzytkownika;
public string $email;
public ?DateTimeImmutable $dataRejestracji; // Może być DateTimeImmutable lub null
// Konstruktor przyjmujący nazwę użytkownika i email
public function __construct(string $nazwa, string $email)
{
echo "Tworzenie nowego obiektu Uzytkownik...<br>";
$this->nazwaUzytkownika = $nazwa;
$this->email = $email;
$this->dataRejestracji = new DateTimeImmutable(); // Ustawienie aktualnej daty i czasu
echo "Użytkownik '" . $this->nazwaUzytkownika . "' został utworzony.<br>";
}
public function wyswietlDane(): string
{
return sprintf(
"Użytkownik: %s, Email: %s, Data rejestracji: %s",
$this->nazwaUzytkownika,
$this->email,
$this->dataRejestracji->format('Y-m-d H:i:s')
);
}
}
// Tworzenie obiektu z przekazaniem argumentów do konstruktora
$user1 = new Uzytkownik("JanKowalski", "jan.kowalski@example.com");
echo $user1->wyswietlDane() . "<br>";
$user2 = new Uzytkownik("AnnaNowak", "anna.nowak@example.com");
echo $user2->wyswietlDane() . "<br>";
/* Przykładowy wynik:
Tworzenie nowego obiektu Uzytkownik...
Użytkownik 'JanKowalski' został utworzony.
Użytkownik: JanKowalski, Email: jan.kowalski@example.com, Data rejestracji: [aktualna data i czas]
Tworzenie nowego obiektu Uzytkownik...
Użytkownik 'AnnaNowak' został utworzony.
Użytkownik: AnnaNowak, Email: anna.nowak@example.com, Data rejestracji: [aktualna data i czas]
*/
?>
W powyższym przykładzie, za każdym razem, gdy tworzymy nowy obiekt Uzytkownik
(new Uzytkownik(...)
), metoda __construct
jest automatycznie wywoływana. Przekazane argumenty (nazwa użytkownika i email) są używane do ustawienia odpowiednich właściwości obiektu, a data rejestracji jest inicjalizowana na bieżący moment.
Konstruktor a dziedziczenie
Jeśli klasa potomna definiuje własny konstruktor, konstruktor klasy bazowej (rodzica) nie jest automatycznie wywoływany. Aby wywołać konstruktor rodzica z wnętrza konstruktora klasy potomnej, należy użyć składni parent::__construct(argumenty_rodzica)
.
<?php
class Osoba
{
public string $imie;
public function __construct(string $imie)
{
echo "Konstruktor Osoba: Ustawiam imię na '" . $imie . "'.<br>";
$this->imie = $imie;
}
}
class Pracownik extends Osoba
{
public string $stanowisko;
public function __construct(string $imie, string $stanowisko)
{
echo "Konstruktor Pracownik: Zaczynam...<br>";
// Wywołanie konstruktora klasy rodzica (Osoba)
parent::__construct($imie);
$this->stanowisko = $stanowisko;
echo "Konstruktor Pracownik: Ustawiono stanowisko na '" . $this->stanowisko . "'.<br>";
}
public function przedstawSie(): string
{
return "Nazywam się " . $this->imie . " i pracuję jako " . $this->stanowisko . ".";
}
}
$pracownik = new Pracownik("Alicja", "Programistka");
echo $pracownik->przedstawSie() . "<br>";
/* Wynik:
Konstruktor Pracownik: Zaczynam...
Konstruktor Osoba: Ustawiam imię na 'Alicja'.
Konstruktor Pracownik: Ustawiono stanowisko na 'Programistka'.
Nazywam się Alicja i pracuję jako Programistka.
*/
?>
Jeśli klasa potomna nie definiuje własnego konstruktora, a klasa rodzica go posiada, to konstruktor rodzica zostanie automatycznie odziedziczony i wywołany podczas tworzenia obiektu klasy potomnej (o ile jest kompatybilny z przekazanymi argumentami).
Promocja Właściwości w Konstruktorze (Constructor Property Promotion)
PHP 8.0 wprowadził bardzo użyteczną składnię zwaną "promocją właściwości w konstruktorze". Pozwala ona na zdefiniowanie właściwości klasy i przypisanie im wartości bezpośrednio w sygnaturze konstruktora, co znacznie skraca kod, szczególnie w przypadku klas z wieloma właściwościami inicjalizowanymi przez konstruktor.
Aby użyć promocji właściwości, należy w liście argumentów konstruktora poprzedzić nazwę argumentu modyfikatorem dostępu (public
, protected
lub private
). PHP automatycznie utworzy właściwość o tej samej nazwie i przypisze jej wartość przekazanego argumentu.
Przykład klasy Punkt
z promocją właściwości:
<?php
// PHP 8.0+
class Punkt
{
// Właściwości $x i $y są automatycznie tworzone i inicjalizowane
public function __construct(
public float $x,
public float $y,
protected string $etykieta = "Punkt"
) {
echo "Utworzono punkt '" . $this->etykieta . "' o współrzędnych (" . $this->x . ", " . $this->y . ").<br>";
}
public function pobierzEtykiete(): string
{
return $this->etykieta;
}
}
$p1 = new Punkt(10.5, 20.3);
$p2 = new Punkt(0, 0, "Początek układu");
echo "Etykieta p1: " . $p1->pobierzEtykiete() . "<br>"; // Działa, bo etykieta jest protected, ale mamy getter
// echo $p1->etykieta; // Błąd, jeśli próbujemy uzyskać dostęp spoza klasy (lub klas potomnych)
/* Wynik:
Utworzono punkt 'Punkt' o współrzędnych (10.5, 20.3).
Utworzono punkt 'Początek układu' o współrzędnych (0, 0).
Etykieta p1: Punkt
*/
?>
Promocja właściwości jest bardzo wygodna i prowadzi do bardziej zwięzłego kodu. Można jej używać razem z typowaniem.
Destruktory (Destructors)
Destruktor to specjalna metoda w klasie, która jest automatycznie wywoływana tuż przed tym, jak obiekt ma zostać zniszczony (usunięty z pamięci). Obiekt jest niszczony, gdy nie ma już do niego żadnych odwołań (np. zmienna przechowująca obiekt wychodzi poza zasięg lub jest jej przypisywana inna wartość) lub gdy skrypt PHP kończy swoje działanie.
Głównym zadaniem destruktora jest wykonanie operacji "porządkujących", takich jak:
- Zwalnianie zasobów zewnętrznych (np. zamykanie otwartych plików, zamykanie połączeń z bazą danych).
- Zapisywanie stanu obiektu przed jego zniszczeniem.
- Wykonywanie innych operacji czyszczących.
W PHP destruktor definiuje się jako metodę o specjalnej nazwie __destruct
(dwa znaki podkreślenia na początku). Destruktor nie może przyjmować żadnych argumentów i nie powinien niczego zwracać.
Składnia definicji destruktora:
<?php
class MojaKlasaZDestruktorem
{
public function __construct()
{
echo "Obiekt utworzony.<br>";
}
public function __destruct()
{
// Kod wykonywany przed zniszczeniem obiektu
echo "Obiekt jest niszczony! Wykonuję operacje porządkujące...<br>";
}
}
// Przykład użycia
function testDestruktora()
{
echo "Rozpoczynam funkcję testDestruktora.<br>";
$obj = new MojaKlasaZDestruktorem();
echo "Wewnątrz funkcji testDestruktora, przed jej zakończeniem.<br>";
// Gdy funkcja się zakończy, $obj wyjdzie poza zasięg i destruktor zostanie wywołany
}
testDestruktora();
echo "Poza funkcją testDestruktora.<br>";
$innyObj = new MojaKlasaZDestruktorem();
// Destruktor dla $innyObj zostanie wywołany na końcu skryptu
// lub gdy $innyObj zostanie jawnie usunięty (np. $innyObj = null; lub unset($innyObj);)
/* Przykładowy wynik:
Rozpoczynam funkcję testDestruktora.
Obiekt utworzony.
Wewnątrz funkcji testDestruktora, przed jej zakończeniem.
Obiekt jest niszczony! Wykonuję operacje porządkujące...
Poza funkcją testDestruktora.
Obiekt utworzony.
(na końcu skryptu pojawi się komunikat z destruktora dla $innyObj)
Obiekt jest niszczony! Wykonuję operacje porządkujące...
*/
?>
Destruktory są wywoływane w odwrotnej kolejności niż konstruktory (jeśli mamy wiele obiektów). PHP próbuje wywołać destruktor nawet jeśli skrypt kończy się z powodu instrukcji exit()
lub die()
. Jednak w przypadku błędów krytycznych (fatal errors), destruktory mogą nie zostać wykonane.
Warto zauważyć, że w PHP, ze względu na automatyczne zarządzanie pamięcią (Garbage Collection), jawne użycie destruktorów do zwalniania pamięci zajmowanej przez sam obiekt PHP nie jest zazwyczaj konieczne. Skupiają się one bardziej na zwalnianiu zasobów zewnętrznych.
Metody Magiczne (Magic Methods)
W PHP istnieje zestaw specjalnych metod, nazywanych metodami magicznymi, które są wywoływane automatycznie w odpowiedzi na pewne zdarzenia lub operacje wykonywane na obiektach. Nazwy tych metod zawsze zaczynają się od podwójnego znaku podkreślenia (__
). Konstruktor (__construct
) i destruktor (__destruct
) są przykładami metod magicznych.
Metody magiczne pozwalają na "przeciążanie" (overloading) pewnych zachowań obiektów lub na implementację specyficznych interakcji. Oto niektóre z ważniejszych metod magicznych (poza __construct
i __destruct
):
1. __call(string $name, array $arguments): mixed
Wywoływana, gdy próbujemy wywołać niedostępną (prywatną, chronioną spoza dozwolonego kontekstu lub nieistniejącą) metodę obiektu w kontekście publicznym. Pozwala to na dynamiczne tworzenie metod lub obsługę wywołań nieistniejących metod.
<?php
class MagicznaKlasa
{
public function __call(string $nazwaMetody, array $argumenty)
{
echo "Próbowano wywołać niedostępną metodę: '" . $nazwaMetody . "' z argumentami: " . implode(', ', $argumenty) . "<br>";
// Można tu np. delegować wywołanie do innej metody lub rzucić wyjątek
return null; // lub inna wartość
}
}
$obj = new MagicznaKlasa();
$obj->nieistniejacaMetoda(10, "test");
// Wynik: Próbowano wywołać niedostępną metodę: 'nieistniejacaMetoda' z argumentami: 10, test
?>
2. __callStatic(string $name, array $arguments): mixed
Podobna do __call
, ale wywoływana, gdy próbujemy wywołać niedostępną metodę statyczną klasy.
3. __get(string $name): mixed
Wywoływana, gdy próbujemy odczytać wartość niedostępnej (prywatnej, chronionej spoza dozwolonego kontekstu lub nieistniejącej) właściwości obiektu w kontekście publicznym. Umożliwia implementację "magicznych" właściwości.
<?php
class DaneUzytkownika
{
private array $dane = [
"imie" => "Anna",
"wiek" => 30
];
public function __get(string $nazwaWlasciwosci)
{
echo "Próba odczytu magicznej właściwości: '" . $nazwaWlasciwosci . "'.<br>";
if (array_key_exists($nazwaWlasciwosci, $this->dane)) {
return $this->dane[$nazwaWlasciwosci];
} else {
// Można zwrócić null, rzucić wyjątek lub wygenerować ostrzeżenie
trigger_error("Niezdefiniowana właściwość: " . $nazwaWlasciwosci, E_USER_NOTICE);
return null;
}
}
}
$user = new DaneUzytkownika();
echo "Imię: " . $user->imie . "<br>"; // Wywoła __get('imie')
echo "Wiek: " . $user->wiek . "<br>"; // Wywoła __get('wiek')
echo "Nazwisko: " . $user->nazwisko . "<br>"; // Wywoła __get('nazwisko'), zwróci null i wygeneruje Notice
?>
4. __set(string $name, mixed $value): void
Wywoływana, gdy próbujemy przypisać wartość do niedostępnej właściwości obiektu w kontekście publicznym.
<?php
// Kontynuacja klasy DaneUzytkownika
class DaneUzytkownika
{
private array $dane = [
"imie" => "Anna",
"wiek" => 30
];
// ... metoda __get z poprzedniego przykładu ...
public function __get(string $nazwaWlasciwosci) { /* ... */ return null;}
public function __set(string $nazwaWlasciwosci, $wartosc): void
{
echo "Próba zapisu do magicznej właściwości: '" . $nazwaWlasciwosci . "' wartości: '" . $wartosc . "'.<br>";
$this->dane[$nazwaWlasciwosci] = $wartosc;
}
public function wyswietlWszystkieDane(): void
{
print_r($this->dane);
}
}
$user = new DaneUzytkownika();
$user->imie = "Beata"; // Wywoła __set('imie', 'Beata')
$user->zawod = "Lekarka"; // Wywoła __set('zawod', 'Lekarka') - utworzy nową "magiczną" właściwość
echo "Imię po zmianie: " . $user->imie . "<br>";
$user->wyswietlWszystkieDane();
?>
5. __isset(string $name): bool
Wywoływana, gdy na niedostępnej właściwości obiektu użyjemy funkcji isset()
lub empty()
.
6. __unset(string $name): void
Wywoływana, gdy na niedostępnej właściwości obiektu użyjemy funkcji unset()
.
7. __toString(): string
Pozwala zdefiniować, jak obiekt ma być reprezentowany jako ciąg znaków, gdy jest używany w kontekście, który wymaga stringa (np. przy użyciu echo $obiekt;
lub konkatenacji z innym stringiem). Metoda ta musi zwracać string.
<?php
class Kolor
{
public function __construct(public int $r, public int $g, public int $b) {}
public function __toString(): string
{
return "rgb(" . $this->r . ", " . $this->g . ", " . $this->b . ")";
}
}
$czerwony = new Kolor(255, 0, 0);
echo "Mój ulubiony kolor to: " . $czerwony . "<br>"; // Automatycznie wywoła __toString()
// Wynik: Mój ulubiony kolor to: rgb(255, 0, 0)
?>
8. __invoke(...$arguments): mixed
Pozwala traktować obiekt jak funkcję, tzn. wywoływać go za pomocą nawiasów $obiekt(argumenty)
.
9. __clone(): void
Wywoływana, gdy obiekt jest klonowany za pomocą słowa kluczowego clone
. Pozwala na dostosowanie procesu klonowania (np. głębokie kopiowanie obiektów zagnieżdżonych).
10. __debugInfo(): array
Pozwala kontrolować, jakie właściwości obiektu są wyświetlane przez funkcję var_dump()
.
Metody magiczne są potężnym narzędziem, ale należy ich używać z rozwagą. Nadmierne ich stosowanie może prowadzić do kodu, który jest trudny do zrozumienia i debugowania. Najczęściej używane i najbardziej przydatne w codziennej pracy są __construct
, __destruct
, __toString
, a także __get
, __set
, __call
w specyficznych przypadkach (np. przy tworzeniu klas typu "wrapper" lub implementacji wzorca ActiveRecord).
Podsumowanie Lekcji
W tej lekcji zgłębiliśmy temat konstruktorów, destruktorów oraz innych metod magicznych w PHP. Zrozumieliśmy, że konstruktor (__construct
) jest kluczowy do inicjalizacji obiektów, pozwalając na ustawienie ich początkowego stanu zaraz po utworzeniu. Poznaliśmy również wygodną składnię promocji właściwości w konstruktorze (PHP 8.0+), która skraca kod. Destruktor (__destruct
) pozwala na wykonanie operacji porządkujących przed zniszczeniem obiektu, co jest szczególnie przydatne przy zarządzaniu zasobami zewnętrznymi.
Omówiliśmy również szereg innych metod magicznych (__call
, __get
, __set
, __toString
itd.), które dają programiście większą kontrolę nad zachowaniem obiektów w różnych sytuacjach, takich jak próba dostępu do niedostępnych składowych czy rzutowanie obiektu na string.
Te mechanizmy są fundamentalne dla zaawansowanego programowania obiektowego w PHP. W następnej lekcji skupimy się bardziej szczegółowo na modyfikatorach dostępu (public
, protected
, private
) i koncepcji hermetyzacji, która jest jednym z filarów OOP.
Zadanie praktyczne
Zmodyfikuj klasę Samochod
z zadania do samodzielnego wykonania z poprzedniej lekcji w następujący sposób:
- Dodaj konstruktor
__construct(string $marka, string $model, int $rokProdukcji)
, który będzie inicjalizował odpowiednie właściwości obiektu przy jego tworzeniu. Użyj promocji właściwości, jeśli pracujesz w PHP 8.0+ (jeśli nie, zadeklaruj właściwości normalnie i przypisz je w ciele konstruktora). Właściwość$predkoscAktualna
powinna być nadal inicjalizowana na 0 (może być jako wartość domyślna właściwości lub ustawiona w konstruktorze). - Dodaj metodę magiczną
__toString(): string
, która będzie zwracać sformatowany ciąg znaków z informacjami o samochodzie (marka, model, rok produkcji), np. "Samochód: Ford Mustang (2022)". - (Opcjonalnie) Dodaj destruktor
__destruct()
, który wyświetli komunikat, np. "Samochód [marka] [model] został zezłomowany.<br>". - Przetestuj działanie: utwórz kilka obiektów klasy
Samochod
, przekazując dane do konstruktora. Wyświetl obiekty za pomocąecho
(aby przetestować__toString
). Sprawdź, czy destruktor (jeśli go dodałeś) jest wywoływany.
Kliknij, aby zobaczyć przykładowe rozwiązanie (PHP 8.0+ z promocją właściwości)
<?php
class Samochod
{
public int $predkoscAktualna = 0;
private const PREDKOSC_MAX = 200;
public function __construct(
public string $marka,
public string $model,
public int $rokProdukcji
) {
echo "Utworzono nowy samochód: " . $this->marka . " " . $this->model . " (" . $this->rokProdukcji . ").<br>";
}
public function przyspiesz(int $wartosc): void
{
if ($wartosc < 0) return;
$this->predkoscAktualna += $wartosc;
if ($this->predkoscAktualna > self::PREDKOSC_MAX) {
$this->predkoscAktualna = self::PREDKOSC_MAX;
}
echo $this->marka . " " . $this->model . " przyspieszył do " . $this->predkoscAktualna . " km/h.<br>";
}
public function hamuj(int $wartosc): void
{
if ($wartosc < 0) return;
$this->predkoscAktualna -= $wartosc;
if ($this->predkoscAktualna < 0) {
$this->predkoscAktualna = 0;
}
echo $this->marka . " " . $this->model . " zwolnił do " . $this->predkoscAktualna . " km/h.<br>";
}
public function wyswietlStatus(): string
{
return "Status: " . $this->marka . " " . $this->model . " (" . $this->rokProdukcji . "), prędkość: " . $this->predkoscAktualna . " km/h.";
}
public function __toString(): string
{
return "Samochód: " . $this->marka . " " . $this->model . " (" . $this->rokProdukcji . ")";
}
public function __destruct()
{
echo "Samochód " . $this->marka . " " . $this->model . " został zezłomowany.<br>";
}
}
// Testowanie
$auto1 = new Samochod("Ford", "Mustang", 2022);
$auto2 = new Samochod("Toyota", "Corolla", 2021);
echo $auto1 . "<br>"; // Test __toString
echo $auto2 . "<br>";
$auto1->przyspiesz(50);
echo $auto1->wyswietlStatus() . "<br>";
// Destruktory zostaną wywołane na końcu skryptu lub gdy obiekty zostaną usunięte
// unset($auto1); // Można jawnie usunąć obiekt, aby wywołać destruktor wcześniej
?>
Zadanie do samodzielnego wykonania
Stwórz klasę Logger
, która będzie miała następujące cechy:
- Prywatną właściwość
$logFilePath
(string), która będzie przechowywać ścieżkę do pliku logu. Ścieżka powinna być przekazywana do konstruktora. - Konstruktor
__construct(string $filePath)
, który inicjalizuje właściwość$logFilePath
. Konstruktor powinien również sprawdzić, czy plik logu istnieje i jest zapisywalny. Jeśli nie, powinien spróbować go utworzyć. Jeśli to się nie uda, powinien wyświetlić błąd. - Publiczną metodę
log(string $message): void
, która dopisuje do pliku logu wiadomość w formacie:[YYYY-MM-DD HH:MM:SS] - Wiadomość
. Użyj funkcjidate()
do sformatowania daty ifile_put_contents()
z flagąFILE_APPEND
do dopisywania. - Metodę magiczną
__call(string $methodName, array $arguments): void
. Jeśli wywołana metoda to np.logInfo
,logWarning
,logError
, to metoda__call
powinna wywołać metodęlog()
, dodając do wiadomości odpowiedni prefix (np. "INFO: ", "WARNING: ", "ERROR: "). Dla innych nazw metod, powinna wyświetlić komunikat o nieznanej metodzie. - Destruktor
__destruct()
, który dopisze do pliku logu wiadomość "Zakończono logowanie." - Przetestuj klasę: utwórz obiekt
Logger
, zapisz kilka wiadomości za pomocą metodylog()
oraz za pomocą "magicznych" metod (np.$logger->logInfo("To jest informacja.")
). Sprawdź zawartość pliku logu.
FAQ - Konstruktory, Destruktory, Metody Magiczne
Czy klasa musi mieć konstruktor?
Nie, klasa nie musi mieć jawnie zdefiniowanego konstruktora. Jeśli go nie zdefiniujemy, PHP użyje domyślnego, pustego konstruktora. Konstruktor definiujemy wtedy, gdy potrzebujemy wykonać jakieś operacje inicjalizacyjne podczas tworzenia obiektu (np. ustawić wartości początkowe właściwości).
Czy konstruktor może coś zwracać?
Konstruktor w PHP (__construct
) nie powinien jawnie zwracać wartości za pomocą return
. Jego głównym celem jest inicjalizacja obiektu. Próba zwrócenia wartości z konstruktora nie spowoduje błędu, ale zwrócona wartość zostanie zignorowana.
Kiedy dokładnie wywoływany jest destruktor?
Destruktor (__destruct
) jest wywoływany, gdy obiekt ma zostać usunięty z pamięci. Dzieje się to, gdy nie ma już żadnych odwołań do obiektu (np. zmienna go przechowująca wychodzi poza zasięg, jest jej przypisywana inna wartość, lub użyto na niej unset()
) albo na samym końcu działania skryptu PHP dla globalnie zdefiniowanych obiektów.
Czy mogę jawnie wywołać konstruktor lub destruktor?
Nie powinno się jawnie wywoływać konstruktora ($obiekt->__construct()
) po utworzeniu obiektu, ponieważ jego rolą jest inicjalizacja podczas tworzenia (new Klasa()
). Podobnie, destruktora ($obiekt->__destruct()
) nie wywołuje się jawnie; jest on zarządzany przez PHP. Wyjątkiem jest wywołanie konstruktora rodzica z konstruktora klasy potomnej za pomocą parent::__construct()
.
Do czego przydają się metody magiczne __get
, __set
, __isset
, __unset
?
Pozwalają one na implementację tzw. "przeciążania właściwości" (property overloading). Dzięki nim możemy kontrolować dostęp do właściwości, które nie są jawnie zadeklarowane jako publiczne, lub dynamicznie tworzyć "wirtualne" właściwości. Są przydatne np. w klasach mapujących dane z bazy danych na obiekty (ORM) lub w klasach konfiguracyjnych.
Czy nadużywanie metod magicznych jest złe?
Tak, nadmierne lub nieprzemyślane użycie metod magicznych (szczególnie __call
, __get
, __set
) może prowadzić do kodu, który jest mniej czytelny, trudniejszy do debugowania i analizy statycznej. Należy ich używać świadomie, gdy przynoszą rzeczywiste korzyści, a nie jako sposobu na "obejście" zasad OOP.
Czy promocja właściwości w konstruktorze (PHP 8.0+) jest zawsze lepsza?
Promocja właściwości jest bardzo wygodna i skraca kod, szczególnie dla prostych klas typu DTO (Data Transfer Objects) lub encji. Jednak w przypadkach, gdy konstruktor wykonuje bardziej złożoną logikę inicjalizacyjną oprócz samego przypisania wartości do właściwości, tradycyjne podejście z jawną deklaracją właściwości i przypisaniami w ciele konstruktora może być bardziej czytelne.