public, private, protected va constructor promotion

~15 daq · PHP · OOP — Ob'ektga yo'naltirilgan PHP

Muammodan boshlaylik

Bank hisobi classi:

<?php

class Hisob
{
    public int $balans = 0;

    public function toldirish(int $summa): void
    {
        if ($summa <= 0) {
            throw new InvalidArgumentException("Summa musbat bo'lsin");
        }
        $this->balans += $summa;
    }
}

$hisob = new Hisob();
$hisob->balans = 999999999;   // 😱

toldirish() metodida tekshiruv bor. Lekin balans — public, va har kim uni to'g'ridan-to'g'ri o'zgartira oladi. Tekshiruvning foydasi yo'q.

Ob'ektning ichki ma'lumotini himoya qilish — inkapsulyatsiya (encapsulation). Buning uchun kirish darajalari bor.


Uch daraja

Daraja Class ichida Voris (meros) class da Tashqarida
public ✅ ✅ ✅
protected ✅ ✅ ❌
private ✅ ❌ ❌
<?php

class Hisob
{
    private int $balans = 0;

    public function toldirish(int $summa): void
    {
        if ($summa <= 0) {
            throw new InvalidArgumentException("Summa musbat bo'lsin");
        }
        $this->balans += $summa;
    }

    public function yechish(int $summa): void
    {
        if ($summa > $this->balans) {
            throw new RuntimeException("Mablag' yetarli emas");
        }
        $this->balans -= $summa;
    }

    public function balans(): int
    {
        return $this->balans;
    }
}

$hisob = new Hisob();
$hisob->toldirish(500000);
$hisob->yechish(120000);
echo $hisob->balans();   // 380000

$hisob->balans = 999999999;
// Error: Cannot modify private property Hisob::$balans

Endi balansni o'zgartirishning yagona yo'li — toldirish() va yechish(), ikkalasi ham qoidalarni tekshiradi. O'qish uchun esa alohida balans() metodi.

Hayotiy o'xshatma: bankomat. Siz ichidagi pul qutisiga to'g'ridan-to'g'ri qo'l tiqa olmaysiz. Faqat ekran va tugmalar (public metodlar) orqali — ular PIN kodni va balansni tekshiradi.


Qaysi darajani tanlash kerak?

Qoida: hamma narsani private qilib boshlang. Tashqaridan haqiqatan kerak bo'lgandagina public ga oching.

  • Xususiyatlar — deyarli doim private
  • Metodlar — tashqi dunyo uchun mo'ljallanganlari public, ichki yordamchilar private
  • protected — faqat meros orqali kengaytirish rejalashtirilgan bo'lsa (30-dars)
<?php

class Buyurtma
{
    private array $qatorlar = [];

    public function jami(): int                      // tashqi dunyo uchun
    {
        return $this->oraliqJami() - $this->chegirma();
    }

    private function oraliqJami(): int { /* ... */ return 0; }   // ichki hisob
    private function chegirma(): int { /* ... */ return 0; }     // ichki hisob
}

private metodlarni keyinchalik bemalol o'zgartirasiz yoki o'chirasiz — tashqarida hech kim ularga bog'liq emas. public metodni o'zgartirish esa uni ishlatgan barcha kodni buzishi mumkin.


Constructor promotion (PHP 8.0+)

Klassik usul — juda ko'p takrorlanish:

<?php

class Foydalanuvchi
{
    private string $ism;
    private string $email;
    private int $yosh;

    public function __construct(string $ism, string $email, int $yosh)
    {
        $this->ism = $ism;
        $this->email = $email;
        $this->yosh = $yosh;
    }
}

Har bir xususiyat nomi to'rt marta yozilgan. Constructor promotion bilan:

<?php

class Foydalanuvchi
{
    public function __construct(
        private string $ism,
        private string $email,
        private int $yosh = 18,
    ) {}
}

Natija aynan bir xil. Konstruktor parametriga kirish darajasi (private, public, protected) qo'yilsa — PHP avtomatik xususiyat yaratadi va qiymat beradi.

Tekshiruv kerak bo'lsa — konstruktor tanasiga yoziladi:

<?php

class Foydalanuvchi
{
    public function __construct(
        private string $ism,
        private string $email,
    ) {
        if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
            throw new InvalidArgumentException("Email noto'g'ri: $email");
        }
    }

    public function ism(): string
    {
        return $this->ism;
    }
}

readonly — faqat bir marta yoziladigan xususiyat (PHP 8.1+)

Ba'zi qiymatlar yaratilgandan keyin hech qachon o'zgarmasligi kerak — masalan, ID yoki yaratilgan sana.

<?php

class Buyurtma
{
    public function __construct(
        public readonly int $id,
        public readonly DateTimeImmutable $yaratilgan,
    ) {}
}

$b = new Buyurtma(1043, new DateTimeImmutable());
echo $b->id;   // 1043 — o'qish mumkin

$b->id = 5;
// Error: Cannot modify readonly property Buyurtma::$id

public readonly — "hamma o'qiy oladi, hech kim o'zgartira olmaydi". Bu getter metod yozmasdan himoya qilishning qisqa yo'li.

PHP 8.2 dan butun classni readonly qilish mumkin:

<?php

readonly class Pul
{
    public function __construct(
        public int $miqdor,
        public string $valyuta,
    ) {}
}

Getter va setter

Boshqa tillardan kelgan odat — har bir xususiyat uchun getX() va setX():

<?php

class Mahsulot
{
    private int $narx;

    public function getNarx(): int { return $this->narx; }
    public function setNarx(int $narx): void { $this->narx = $narx; }
}

Bu public int $narx dan farqi yo'q — himoya nol. Setter faqat tekshiruv yoki mantiq qo'shganda ma'noga ega. Yaxshiroq — metodni ma'noli nomlash:

<?php

class Mahsulot
{
    public function __construct(private int $narx) {}

    public function narx(): int
    {
        return $this->narx;
    }

    public function chegirmaQollash(int $foiz): void
    {
        if ($foiz < 0 || $foiz > 90) {
            throw new InvalidArgumentException("Chegirma 0-90% oralig'ida");
        }
        $this->narx = intdiv($this->narx * (100 - $foiz), 100);
    }
}

setNarx(5) — "narxni 5 qil". chegirmaQollash(10) — "10% chegirma qo'lla" — biznes ma'nosi aniq va qoidalar ichida.


Nima uchun shunday?

Nima uchun ma'lumotni yashirish kerak — biz o'zimiz yozgan kodga o'zimiz ishonmaymizmi?

Gap ishonchda emas, o'zgarishga tayyorlikda. Bugun $balans — oddiy son. Ertaga uni tiyinlarda saqlashga yoki har bir o'zgarishni logga yozishga qaror qildingiz. Agar $balans private bo'lsa — faqat class ichini o'zgartirasiz. Agar public bo'lsa va loyihaning 40 joyida $hisob->balans = ... yozilgan bo'lsa — 40 joyni qidirib topasiz. Inkapsulyatsiya — kelajakdagi o'zingizga g'amxo'rlik.


Xulosa

Eslab qoling:

  • public — hamma, protected — class va vorislari, private — faqat class ichi
  • Xususiyatlarni private qiling; o'zgartirish — tekshiruvli metodlar orqali
  • Constructor promotion: public function __construct(private string $ism) {}
  • readonly — bir marta beriladi, keyin faqat o'qiladi
  • Ma'nosiz getter/setter emas — ma'noli metodlar yozing

Keyingi dars

30-dars: Meros — extends, parent::, metodni qayta yozish