Meros — extends, parent::, metodni qayta yozish

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

Muammodan boshlaylik

Do'konda ikki turdagi mahsulot bor: jismoniy (kitob, telefon — yetkazib berish kerak, og'irligi bor) va raqamli (elektron kitob, kurs — yuklab olinadi, havolasi bor).

Ikkalasining ham nomi, narxi, formatlanganNarx() metodi bor. Ikkita alohida class yozsak — bu umumiy qism ikki marta takrorlanadi. Narx formatini o'zgartirsak — ikki joyda o'zgartiramiz.

Meros — umumiy qismni bitta "ota" classda yozib, "bola" classlar uni avtomatik olishi.


extends

<?php

class Mahsulot
{
    public function __construct(
        protected string $nom,
        protected int $narx,
    ) {}

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

    public function formatlanganNarx(): string
    {
        return number_format($this->narx, 0, '', ' ') . " so'm";
    }
}

class JismoniyMahsulot extends Mahsulot
{
    public function __construct(
        string $nom,
        int $narx,
        private float $ogirlikKg,
    ) {
        parent::__construct($nom, $narx);
    }

    public function yetkazishNarxi(): int
    {
        return 15000 + (int) ceil($this->ogirlikKg) * 5000;
    }
}

class RaqamliMahsulot extends Mahsulot
{
    public function __construct(
        string $nom,
        int $narx,
        private string $yuklashHavolasi,
    ) {
        parent::__construct($nom, $narx);
    }

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

$kitob = new JismoniyMahsulot("O'tkan kunlar", 65000, 0.6);
echo $kitob->nom();                // O'tkan kunlar — otadan meros
echo $kitob->formatlanganNarx();   // 65 000 so'm — otadan meros
echo $kitob->yetkazishNarxi();     // 20000 — o'ziniki

$kurs = new RaqamliMahsulot("PHP kursi", 299000, "https://...");
echo $kurs->formatlanganNarx();    // 299 000 so'm
  • class B extends A — B classi A ning barcha public va protected a'zolarini oladi
  • JismoniyMahsulot — bola (child, subclass), Mahsulot — ota (parent)
  • Xususiyatlar protected — bolalar ularni ko'radi, tashqaridan esa yopiq

parent:: — ota classga murojaat

Bola class o'z konstruktorini yozsa, otaning konstruktori avtomatik chaqirilmaydi. Uni qo'lda chaqirish kerak:

parent::__construct($nom, $narx);

Busiz $this->nom va $this->narx bo'sh qoladi va xato beradi.


Metodni qayta yozish (overriding)

Bola class otaning metodini o'zgartirishi mumkin:

<?php

class Mahsulot
{
    public function __construct(protected string $nom, protected int $narx) {}

    public function tavsif(): string
    {
        return "{$this->nom} — " . number_format($this->narx, 0, '', ' ') . " so'm";
    }
}

class AksiyaMahsulot extends Mahsulot
{
    public function __construct(string $nom, int $narx, private int $chegirmaFoiz)
    {
        parent::__construct($nom, $narx);
    }

    public function tavsif(): string
    {
        $asl = parent::tavsif();   // otaning versiyasini chaqirib, kengaytiramiz
        return "🔥 $asl (-{$this->chegirmaFoiz}%)";
    }
}

$m = new AksiyaMahsulot("Quloqchin", 249000, 20);
echo $m->tavsif();   // 🔥 Quloqchin — 249 000 so'm (-20%)

parent::tavsif() — otaning metodini to'liq qayta yozmasdan, ustiga qo'shish.

#[\Override] (PHP 8.3+)

#[\Override]
public function tavsif(): string { /* ... */ }

Bu atribut PHP ga "men otaning metodini qayta yozyapman" deydi. Agar otada bunday metod bo'lmasa (masalan, nomini xato yozgan bo'lsangiz — tavsf) — PHP xato beradi.


Polimorfizm — bitta kod, turli xatti-harakat

Merosning haqiqiy kuchi shu yerda:

<?php

$savat = [
    new Mahsulot("Daftar", 12000),
    new AksiyaMahsulot("Quloqchin", 249000, 20),
    new Mahsulot("Ruchka", 3000),
];

foreach ($savat as $m) {
    echo $m->tavsif() . PHP_EOL;
}
Daftar — 12 000 so'm
🔥 Quloqchin — 249 000 so'm (-20%)
Ruchka — 3 000 so'm

foreach ichidagi kod har bir ob'ektning turini bilmaydi va bilishi shart emas. U faqat tavsif() ni chaqiradi — har bir ob'ekt o'zini qanday tasvirlashni o'zi biladi. Ertaga YangiMahsulot classi qo'shilsa — bu sikl o'zgarmaydi.

Funksiya parametri ham ota turini qabul qiladi:

<?php

function kartaChiqar(Mahsulot $m): string   // bolalarini ham qabul qiladi
{
    return "<div class='karta'>" . htmlspecialchars($m->tavsif()) . "</div>";
}

final — merosni taqiqlash

<?php

final class Pul { /* ... */ }

class MeningPulim extends Pul {}
// Fatal error: Class MeningPulim cannot extend final class Pul

Metodni ham final qilish mumkin — bola uni qayta yoza olmaydi:

<?php

class Toloq
{
    final public function tasdiqlash(): void { /* xavfsizlik mantig'i */ }
}

Meros qachon noto'g'ri tanlov

Meros — kuchli, lekin ko'p suiiste'mol qilinadigan vosita. Tekshirish usuli — "B — bu A ning bir turi" gapi to'g'rimi?

  • JismoniyMahsulot — bu Mahsulot ning bir turi ✅
  • Admin — bu Foydalanuvchi ning bir turi ✅
  • Savat — bu Mahsulot ning bir turi? ❌ Savat mahsulotlarni o'z ichiga oladi

Faqat kodni qayta ishlatish uchun meros olmang:

<?php

// ❌ Logger kerak bo'lgani uchun meros
class Buyurtma extends Logger { }

// ✅ Logger ni ichida saqlash (kompozitsiya)
class Buyurtma
{
    public function __construct(private Logger $logger) {}
}

Qoida: "o'z ichiga oladi" (has-a) — kompozitsiya, "bir turi" (is-a) — meros. Shubha bo'lsa — kompozitsiya.

Yana bir qoida: meros zanjiri 2-3 qavatdan oshmasin. A → B → C → D → E bo'lsa, E ning metodi qayerdan kelganini topish uchun 5 ta faylni ochasiz.


Nima uchun shunday?

Nima uchun PHP da faqat bitta ota class bo'lishi mumkin (extends A, B yozib bo'lmaydi)?

Ko'p merosli tillarda (C++) "olmos muammosi" bor: B va C ikkalasi ham A dan meros olgan va bir xil metodni turlicha qayta yozgan; D esa B va C dan meros oldi — D qaysi versiyani oladi? Bu chalkashlikdan qochish uchun PHP (Java, C# kabi) bitta ota bilan cheklangan. Bir nechta classdan xatti-harakat olish kerak bo'lsa — interface (31-dars) va trait (32-dars) bor.


Xulosa

Eslab qoling:

  • class Bola extends Ota — otaning public va protected a'zolarini oladi
  • Bola konstruktorida parent::__construct(...) ni chaqiring
  • Metodni qayta yozish mumkin; parent::metod() — otaning versiyasi
  • Polimorfizm: ota turida ishlaydigan kod barcha bolalar bilan ishlaydi
  • final — merosni yoki qayta yozishni taqiqlash
  • "Bir turi" — meros, "o'z ichiga oladi" — kompozitsiya

Keyingi dars

31-dars: Interface va abstract class