Konstruktor i property promotion
🎯 Po tej lekcji będziesz używać __construct do inicjalizacji obiektów i skrótu property promotion z PHP 8.
W poprzedniej lekcji tworzyłeś obiekt i ręcznie ustawiałeś jego właściwości jedna po drugiej. To działa, ale jest nieporęczne — szczególnie gdy obiekt ma kilka wymaganych pól. PHP rozwiązuje ten problem przez konstruktor: metodę specjalną wykonywaną automatycznie zaraz po new.
__construct — metoda inicjalizująca
Konstruktor nosi zawsze tę samą nazwę: __construct. Możesz w nim przyjąć parametry i przypisać ich wartości do właściwości obiektu:
<?php
declare(strict_types=1);
class Ksiazka
{
public string $tytul;
public string $autor;
public float $cena;
public function __construct(string $tytul, string $autor, float $cena)
{
$this->tytul = $tytul;
$this->autor = $autor;
$this->cena = $cena;
}
public function opisz(): string
{
return '"' . $this->tytul . '" — ' . $this->autor . ' (' . $this->cena . ' zł)';
}
}
$k1 = new Ksiazka('Pan Tadeusz', 'Adam Mickiewicz', 29.99);
$k2 = new Ksiazka('Solaris', 'Stanisław Lem', 34.90);
echo $k1->opisz(); // wynik: "Pan Tadeusz" — Adam Mickiewicz (29.99 zł)
echo $k2->opisz(); // wynik: "Solaris" — Stanisław Lem (34.9 zł)
Argumenty przekazujesz w nawiasach new Ksiazka(...) — dokładnie jak do zwykłej funkcji.
Parametry z wartościami domyślnymi
Konstruktor może mieć parametry opcjonalne (z wartościami domyślnymi). Obowiązuje ta sama zasada co w funkcjach — parametry z wartościami domyślnymi muszą być na końcu:
<?php
declare(strict_types=1);
class Produkt
{
public string $nazwa;
public float $cena;
public bool $dostepny;
public function __construct(string $nazwa, float $cena, bool $dostepny = true)
{
$this->nazwa = $nazwa;
$this->cena = $cena;
$this->dostepny = $dostepny;
}
}
$p1 = new Produkt('Jabłka', 3.49); // dostepny = true (domyślnie)
$p2 = new Produkt('Arbuz', 12.99, false); // jawnie: niedostępny
echo $p1->dostepny; // wynik: 1
echo $p2->dostepny; // wynik: (puste — false nie wyświetla się jako tekst)
Constructor property promotion — skrót PHP 8
Wzorzec „zadeklaruj właściwość → przyjmij parametr → przypisz" jest tak częsty, że PHP 8 wprowadził property promotion (promocję właściwości w konstruktorze). Wystarczy dodać modyfikator widoczności (public, private, protected) przed parametrem konstruktora — PHP automatycznie tworzy właściwość i przypisuje wartość:
<?php
declare(strict_types=1);
// Przed PHP 8 (tradycyjny styl):
class SamochodStary
{
public string $marka;
public string $model;
public int $rokProdukcji;
public function __construct(string $marka, string $model, int $rokProdukcji)
{
$this->marka = $marka;
$this->model = $model;
$this->rokProdukcji = $rokProdukcji;
}
}
// PHP 8+ (property promotion):
class Samochod
{
public function __construct(
public string $marka,
public string $model,
public int $rokProdukcji,
) {}
}
$auto = new Samochod('Toyota', 'Corolla', 2022);
echo $auto->marka; // wynik: Toyota
echo $auto->model; // wynik: Corolla
echo $auto->rokProdukcji; // wynik: 2022
Obie klasy zachowują się identycznie — promotion to czysty skrót składniowy. Zwróć uwagę na pusty blok {} w konstruktorze z promotion: nie ma już potrzeby nic tam pisać.
readonly do promoted property, żeby uniemożliwić jej zmianę po inicjalizacji: public readonly string $marka. Próba zapisu po konstruktorze skończy się błędem. Świetne do modelowania obiektów wartości (ang. value objects).Mieszanie promotion z tradycyjnym stylem
Możesz łączyć promoted i niepromoted parametry w jednym konstruktorze. Czasem masz właściwość obliczaną podczas inicjalizacji:
<?php
declare(strict_types=1);
class Zamowienie
{
public float $wartoscBrutto;
public function __construct(
public readonly string $numerZamowienia,
public readonly float $wartoscNetto,
public readonly float $stawkaVat = 0.23,
) {
// właściwość obliczana — nie nadaje się do promotion
$this->wartoscBrutto = $wartoscNetto * (1 + $stawkaVat);
}
}
$z = new Zamowienie('ZAM-2024-001', 100.0);
echo $z->numerZamowienia; // wynik: ZAM-2024-001
echo $z->wartoscNetto; // wynik: 100
echo $z->wartoscBrutto; // wynik: 123
Konstruktor z walidacją
Konstruktor to dobre miejsce na sprawdzenie, czy dane mają sens — zanim zostaną zapisane:
<?php
declare(strict_types=1);
class Wiek
{
public function __construct(
public readonly int $wartosc,
) {
if ($wartosc < 0 || $wartosc > 150) {
throw new \InvalidArgumentException(
"Wiek musi być w zakresie 0–150, podano: $wartosc"
);
}
}
}
$w = new Wiek(25);
echo $w->wartosc; // wynik: 25
// new Wiek(200); // rzuci InvalidArgumentException
O wyjątkach (throw, catch) piszemy dokładnie w lekcji 9. Na razie zapamiętaj, że konstruktor może przerywać tworzenie obiektu, gdy dane są nieprawidłowe.
W kolejnej lekcji poznasz widoczność właściwości i metod — czyli dlaczego private jest ważniejsze niż public.
Sprawdź się
Jak PHP nazywa metodę wywoływaną automatycznie przy tworzeniu obiektu przez new?
__construct to magiczna metoda PHP, która uruchamia się automatycznie zaraz po utworzeniu nowego obiektu przez new. Możesz w niej ustawiać właściwości na wartości przekazane jako argumenty.
Co robi constructor property promotion w PHP 8?
Property promotion pozwala napisać public/private/protected przed parametrem konstruktora — PHP automatycznie deklaruje właściwość i przypisuje jej wartość przekazaną przy new. Oszczędza to powtarzalny kod.
Jak w metodzie klasy odwołasz się do właściwości $imie zadeklarowanej przez property promotion?
Niezależnie od tego, czy właściwość powstała przez klasyczne public string $imie czy przez promotion w konstruktorze, dostęp do niej wewnątrz klasy zawsze odbywa się przez $this->imie.
Ćwiczenie
Zdefiniuj klasę Pracownik z właściwościami: string $imie, string $stanowisko i float $wynagrodzenie. Użyj constructor property promotion (PHP 8). Dodaj metodę miesiecznyKoszt(): float, która zwraca wynagrodzenie powiększone o 20% (koszty pracodawcy). Stwórz dwa obiekty i wyświetl miesięczny koszt każdego.
Pokaż rozwiązanie
<?php
declare(strict_types=1);
class Pracownik
{
public function __construct(
public readonly string $imie,
public readonly string $stanowisko,
public readonly float $wynagrodzenie,
) {}
public function miesiecznyKoszt(): float
{
return $this->wynagrodzenie * 1.20;
}
}
$anna = new Pracownik('Anna Kowalska', 'Programistka', 8000.0);
$tomek = new Pracownik('Tomek Nowak', 'Tester', 6000.0);
echo $anna->imie . ': ' . $anna->miesiecznyKoszt() . ' zł'; // wynik: Anna Kowalska: 9600 zł
echo $tomek->imie . ': ' . $tomek->miesiecznyKoszt() . ' zł'; // wynik: Tomek Nowak: 7200 zł