Class va ob'ekt — xususiyat, metod, $this, konstruktor

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

Muammodan boshlaylik

Internet do'kondagi savatni massiv va funksiyalar bilan yozdik:

<?php

$savat = [];

function savatgaQosh(array &$savat, string $nom, int $narx, int $soni): void { /* ... */ }
function savatJami(array $savat): int { /* ... */ }
function savatTozala(array &$savat): void { /* ... */ }

Ishlaydi. Lekin muammolar bor:

  • Har bir funksiyaga $savat ni qo'lda uzatish kerak
  • Kimdir $savat[] = 'salom'; deb yozib qo'yishi mumkin — hech narsa to'xtatmaydi
  • savatJami() ni boshqa massiv bilan chaqirish mumkin — u nima ekanini bilmaydi
  • Ma'lumot va uni boshqaradigan funksiyalar alohida yashaydi

OOP (Ob'ektga yo'naltirilgan dasturlash) ma'lumot va uning ustidagi amallarni bitta birlikka birlashtiradi.


Class va ob'ekt

Class — chizma (loyiha). Ob'ekt — shu chizma bo'yicha yasalgan narsa.

Hayotiy o'xshatma: avtomobil zavodidagi chizma — bu class. Undan yasalgan har bir mashina — ob'ekt. Chizma bitta, mashinalar ko'p. Har bir mashinaning o'z rangi, o'z probegi bor, lekin hammasi bir xil tuzilgan va bir xil ishlaydi.

<?php

class Mahsulot
{
    public string $nom;
    public int $narx;
}

$telefon = new Mahsulot();   // ob'ekt yaratish
$telefon->nom = "Telefon";
$telefon->narx = 3200000;

$quloqchin = new Mahsulot();
$quloqchin->nom = "Quloqchin";
$quloqchin->narx = 249000;

echo $telefon->nom;      // Telefon
echo $quloqchin->nom;    // Quloqchin
  • class Mahsulot — class e'loni, nom PascalCase da
  • public string $nom; — xususiyat (property): ob'ektning ma'lumoti
  • new Mahsulot() — yangi ob'ekt yaratish
  • -> — ob'ektning xususiyati yoki metodiga murojaat

Metodlar — ob'ektning amallari

Metod — class ichidagi funksiya:

<?php

class Mahsulot
{
    public string $nom;
    public int $narx;

    public function chegirmaliNarx(int $foiz): int
    {
        return $this->narx - intdiv($this->narx * $foiz, 100);
    }

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

$telefon = new Mahsulot();
$telefon->nom = "Telefon";
$telefon->narx = 3200000;

echo $telefon->formatlanganNarx();     // 3 200 000 so'm
echo $telefon->chegirmaliNarx(10);     // 2880000

$this nima?

$this — "men", ya'ni metod chaqirilgan ob'ektning o'zi.

$telefon->formatlanganNarx() chaqirilganda, metod ichidagi $this — bu $telefon. $quloqchin->formatlanganNarx() chaqirilganda — $quloqchin. Bitta metod — har bir ob'ekt uchun o'z ma'lumoti bilan ishlaydi.


Konstruktor — yaratilish paytida sozlash

Yuqoridagi misolda muammo bor:

<?php

$x = new Mahsulot();
echo $x->formatlanganNarx();
// Error: Typed property Mahsulot::$narx must not be accessed before initialization

Ob'ekt yaratildi, lekin ichi bo'sh. Konstruktor buni hal qiladi — u new chaqirilganda avtomatik ishga tushadi:

<?php

class Mahsulot
{
    public string $nom;
    public int $narx;

    public function __construct(string $nom, int $narx)
    {
        if ($narx < 0) {
            throw new InvalidArgumentException("Narx manfiy bo'lishi mumkin emas");
        }
        $this->nom = $nom;
        $this->narx = $narx;
    }

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

$telefon = new Mahsulot("Telefon", 3200000);
echo $telefon->formatlanganNarx();   // 3 200 000 so'm

new Mahsulot("Xato", -100);   // InvalidArgumentException

Endi Mahsulot ob'ekti hech qachon yarim tayyor yoki noto'g'ri holatda bo'lmaydi. Bu — OOP ning asosiy foydasi.

__construct — ikki past chiziq bilan boshlanadigan "sehrli metod". PHP ularni maxsus holatlarda o'zi chaqiradi.


Muammoni hal qilamiz: Savat classi

<?php
declare(strict_types=1);

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

class Savat
{
    private array $qatorlar = [];

    public function qosh(Mahsulot $mahsulot, int $soni = 1): void
    {
        if ($soni < 1) {
            throw new InvalidArgumentException("Soni kamida 1");
        }
        $this->qatorlar[] = ['mahsulot' => $mahsulot, 'soni' => $soni];
    }

    public function jami(): int
    {
        $jami = 0;
        foreach ($this->qatorlar as $q) {
            $jami += $q['mahsulot']->narx * $q['soni'];
        }
        return $jami;
    }

    public function soni(): int
    {
        return count($this->qatorlar);
    }

    public function tozala(): void
    {
        $this->qatorlar = [];
    }
}

$savat = new Savat();
$savat->qosh(new Mahsulot("Kitob", 85000), 3);
$savat->qosh(new Mahsulot("Daftar", 12000), 5);

echo $savat->soni();   // 2
echo $savat->jami();   // 315000

Yangi narsalar:

  • public function __construct(public string $nom, ...) — constructor promotion: xususiyatni e'lon qilish va qiymat berish bitta qatorda (keyingi darsda batafsil)
  • private array $qatorlar — tashqaridan kirib bo'lmaydi. $savat->qatorlar[] = 'salom' — xato beradi. Savatni faqat qosh() orqali o'zgartirish mumkin, u esa tekshiradi.
  • qosh(Mahsulot $mahsulot) — parametr turi class. Unga faqat Mahsulot ob'ekti berish mumkin.

Endi muammodagi to'rtta kamchilik ham hal bo'ldi.


Ob'ektlar havola orqali uzatiladi

Massivlar funksiyaga nusxa sifatida beriladi. Ob'ektlar esa — havola (bir xil ob'ektga ishora):

<?php

function narxniOshir(Mahsulot $m): void
{
    $m->narx += 10000;
}

$kitob = new Mahsulot("Kitob", 85000);
narxniOshir($kitob);
echo $kitob->narx;   // 95000 — asl ob'ekt o'zgardi!

$nusxa = $kitob;      // bu NUSXA EMAS — o'sha ob'ektning ikkinchi nomi
$nusxa->narx = 1;
echo $kitob->narx;    // 1

$haqiqiyNusxa = clone $kitob;   // haqiqiy nusxa

Massivlarda & kerak edi — ob'ektlarda bu o'z-o'zidan shunday.


Ob'ektni tekshirish

<?php

$x = new Mahsulot("Kitob", 85000);

var_dump($x instanceof Mahsulot);   // bool(true)
echo $x::class;                      // Mahsulot
var_dump($x);
// object(Mahsulot)#1 (2) { ["nom"]=> string(5) "Kitob" ["narx"]=> int(85000) }

Nima uchun shunday?

Nima uchun OOP kerak, agar funksiyalar bilan ham hammasini yozsa bo'lsa?

Kichik skriptda — haqiqatan ham kerak emas. Lekin loyiha o'sib, 50 ming qatorga va 5 ta dasturchiga yetganda asosiy savol tug'iladi: "Bu ma'lumotni kim o'zgartirishi mumkin?". Massiv bo'lsa — hamma, istalgan joyda. Ob'ekt bo'lsa — faqat uning metodlari, va ular qoidalarni tekshiradi. OOP — katta kodni boshqariladigan qismlarga bo'lish va har bir qismning ichki tuzilmasini himoya qilish usuli. Laravel, Symfony, WordPress plaginlari — hammasi classlar ustiga qurilgan.


Tez-tez uchraydigan xatolar

$this siz murojaat:

public function jami(): int
{
    return $narx * 2;   // ❌ Undefined variable $narx
    return $this->narx * 2;   // ✅
}

-> dan keyin $ qo'yish:

echo $telefon->$nom;   // ❌ bu "o'zgaruvchi nomli xususiyat" degani
echo $telefon->nom;    // ✅

Classni ikki marta e'lon qilish — Cannot declare class Mahsulot, because the name is already in use. Class fayllarini require_once bilan ulang (33-darsda autoload bilan butunlay hal qilamiz).


Xulosa

Eslab qoling:

  • Class — chizma, ob'ekt — new bilan yaratilgan nusxa
  • Xususiyat — ma'lumot, metod — amal; -> bilan murojaat
  • $this — joriy ob'ekt
  • __construct — yaratilishda avtomatik chaqiriladi; ob'ektni to'g'ri holatda boshlaydi
  • private — tashqaridan yopiq; o'zgartirish faqat metodlar orqali
  • Ob'ektlar havola orqali uzatiladi; nusxa uchun clone

Keyingi dars

29-dars: public, private, protected va constructor promotion