Meros — extends, parent::, metodni qayta yozish
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 barchapublicvaprotecteda'zolarini oladiJismoniyMahsulot— 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— buMahsulotning bir turi ✅Admin— buFoydalanuvchining bir turi ✅Savat— buMahsulotning 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— otaningpublicvaprotecteda'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