12-bo‘lim
OOP asoslari
Sinf va obyekt, konstruktor xossalarini ko'chirish, kirish darajalari, readonly va statik a'zolar.
Ushbu bo‘lim mundarijasi
Massivlar bilan ishlash oson, lekin ular hech narsani
kafolatlamaydi. $mahsulot["narx"] bo'sh bo'lishi,
$mahsulot["narxi"] deb xato yozilishi mumkin va kod
faqat ish paytida yiqiladi.
Sinf bu muammoni hal qiladi.
Massivdan obyektga #
// Har bir misol qat'iy turlar rejimida ishlaydi.
// Massiv bilan - hech qanday kafolat yo'q
$massiv = ["nom" => "Klaviatura", "narx" => 350000];
echo "Massiv: {$massiv['nom']}\n";
echo "Xato yozilgan kalit: " . ($massiv["narxi"] ?? "(yo'q - xato sezilmadi)") . "\n";
echo "\n";
// Sinf bilan - tuzilma majburiy
final class Mahsulot
{
public function __construct(
public readonly string $nom,
public readonly int $narxTiyin,
public readonly int $soni = 0,
) {
if ($nom === "") {
throw new InvalidArgumentException("Nom bo'sh bo'lmasin");
}
if ($narxTiyin < 0) {
throw new InvalidArgumentException("Narx manfiy bo'lmasin");
}
}
public function narxMatni(): string
{
return number_format($this->narxTiyin / 100, 2, ",", " ") . " so'm";
}
public function omborda(): bool
{
return $this->soni > 0;
}
public function jamiQiymat(): int
{
return $this->narxTiyin * $this->soni;
}
}
$m = new Mahsulot("Klaviatura", 35000000, 12);
echo "Obyekt: {$m->nom}\n";
echo "Narxi: {$m->narxMatni()}\n";
echo "Omborda: " . ($m->omborda() ? "ha" : "yo'q") . "\n";
echo "Jami: " . number_format($m->jamiQiymat() / 100, 2, ",", " ") . " so'm\n";
echo "\n";
try {
new Mahsulot("", 1000);
} catch (InvalidArgumentException $x) {
echo "Konstruktor rad etdi: " . $x->getMessage() . "\n";
}
Massiv: Klaviatura
Xato yozilgan kalit: (yo'q - xato sezilmadi)
Obyekt: Klaviatura
Narxi: 350 000,00 so'm
Omborda: ha
Jami: 4 200 000,00 so'm
Konstruktor rad etdi: Nom bo'sh bo'lmasin
PHP 8 gacha bir xil narsani uch marta yozish kerak edi:
class Mahsulot {
private string $nom; // 1. e'lon
private int $narx;
public function __construct(string $nom, int $narx) { // 2. parametr
$this->nom = $nom; // 3. tayinlash
$this->narx = $narx;
}
}
PHP 8 da esa bir marta:
class Mahsulot {
public function __construct(
private string $nom,
private int $narx,
) {}
}
Kirish darajasi (public, private, protected) yoki
readonly yozilishi bilan parametr avtomatik xossaga
aylanadi.
Diqqat: konstruktor tanasi kerak bo'lsa, u xossalar tayinlangandan keyin ishlaydi - shuning uchun yuqoridagi tekshiruvlar to'g'ri joyda turibdi.
Oxirgi parametrdan keyin vergul qo'yish mumkin - bu
git tarixini toza saqlaydi.
readonly xossalar #
final class Buyurtma
{
public function __construct(
public readonly int $id,
public readonly string $mijoz,
public readonly array $mahsulotlar,
private string $holat = "yangi",
) {}
public function holat(): string
{
return $this->holat;
}
// readonly bo'lmagan xossani o'zgartirish mumkin
public function holatniOzgartir(string $yangi): void
{
$ruxsat = ["yangi", "tasdiqlangan", "yuborilgan", "yakunlangan", "bekor"];
if (!in_array($yangi, $ruxsat, true)) {
throw new InvalidArgumentException("Noma'lum holat: {$yangi}");
}
$this->holat = $yangi;
}
}
$b = new Buyurtma(101, "Malika", ["Klaviatura", "Sichqoncha"]);
echo "Buyurtma #{$b->id}\n";
echo "Mijoz: {$b->mijoz}\n";
echo "Holat: {$b->holat()}\n";
$b->holatniOzgartir("tasdiqlangan");
echo "Yangi holat: {$b->holat()}\n";
echo "\n";
// readonly xossani o'zgartirishga urinish
try {
$b->id = 999;
} catch (Error $x) {
echo "readonly himoyasi ishladi:\n";
echo " " . $x->getMessage() . "\n";
}
echo "\n";
try {
$b->holatniOzgartir("qandaydir");
} catch (InvalidArgumentException $x) {
echo "Noto'g'ri holat rad etildi: " . $x->getMessage() . "\n";
}
Buyurtma #101
Mijoz: Malika
Holat: yangi
Yangi holat: tasdiqlangan
readonly himoyasi ishladi:
Cannot modify readonly property Buyurtma::$id
Noto'g'ri holat rad etildi: Noma'lum holat: qandaydir
readonly faqat bir marta yozishga ruxsat beradireadonly xossa konstruktorda (yoki sinf ichida) bir
marta qiymat oladi va keyin o'zgarmaydi.
Bu nimani beradi:
| Foyda | Izoh |
|---|---|
| Obyekt holati bashorat qilinadi | Yaratilgandan keyin o'zgarmaydi |
| Ko'p oqimli xatolar yo'q | O'zgarmas qiymat xavfsiz |
| Nusxa olish shart emas | Kimdir o'zgartira olmaydi |
Ikki muhim cheklov:
1. Faqat tur e'lon qilingan xossaga qo'llanadi.
public readonly $x; xato - tur yozilishi shart.
2. Massiv ichini o'zgartirib bo'lmaydi, lekin uning
elementlari o'zgarmas bo'lishi shart emas. Ya'ni
readonly array $mahsulotlar massivning o'zini
almashtirishdan saqlaydi, lekin ichidagi obyektlar
o'zgaruvchan bo'lishi mumkin.
PHP 8.2 da readonly class ham qo'shildi - u butun
sinfning barcha xossalarini readonly qiladi:
final readonly class Nuqta {
public function __construct(
public int $x,
public int $y,
) {}
}
Qiymat obyektlari (value object) uchun bu ideal.
Kirish darajalari amalda #
class Hisob
{
private int $balansTiyin = 0;
private array $tarix = [];
public function __construct(
public readonly string $egasi,
int $boshlangichTiyin = 0,
) {
if ($boshlangichTiyin > 0) {
$this->kirim($boshlangichTiyin, "boshlang'ich");
}
}
public function kirim(int $tiyin, string $izoh = ""): void
{
if ($tiyin <= 0) {
throw new InvalidArgumentException("Summa musbat bo'lsin");
}
$this->balansTiyin += $tiyin;
$this->yoz("+", $tiyin, $izoh);
}
public function chiqim(int $tiyin, string $izoh = ""): bool
{
if ($tiyin <= 0) {
throw new InvalidArgumentException("Summa musbat bo'lsin");
}
if ($tiyin > $this->balansTiyin) {
return false;
}
$this->balansTiyin -= $tiyin;
$this->yoz("-", $tiyin, $izoh);
return true;
}
public function balans(): int
{
return $this->balansTiyin;
}
public function tarix(): array
{
return $this->tarix; // NUSXA qaytariladi
}
// Faqat shu sinf ishlata oladi
private function yoz(string $belgi, int $tiyin, string $izoh): void
{
$this->tarix[] = sprintf("%s%s %s", $belgi,
number_format($tiyin / 100, 2, ",", " "), $izoh);
}
}
$h = new Hisob("Husanboy Qodirov", 50000000);
$h->kirim(25000000, "oylik");
$ok = $h->chiqim(30000000, "ijara");
$yoq = $h->chiqim(99900000, "avtomobil");
printf("Egasi: %s\n", $h->egasi);
printf("Balans: %s so'm\n", number_format($h->balans() / 100, 2, ",", " "));
printf("Katta chiqim amalga oshdimi: %s\n", $yoq ? "ha" : "yo'q");
echo "\nTarix:\n";
foreach ($h->tarix() as $q) {
echo " {$q}\n";
}
echo "\n";
// private xossaga tashqaridan murojaat
try {
echo $h->balansTiyin;
} catch (Error $x) {
echo "private himoyasi: " . substr($x->getMessage(), 0, 52) . "\n";
}
Egasi: Husanboy Qodirov
Balans: 450 000,00 so'm
Katta chiqim amalga oshdimi: yo'q
Tarix:
+500 000,00 boshlang'ich
+250 000,00 oylik
-300 000,00 ijara
private himoyasi: Cannot access private property Hisob::$balansTiyin
tarix() usuli $this->tarix massivini qaytaradi.
PHP da massiv qiymat bo'yicha uzatiladi, ya'ni
chaqiruvchi nusxani oladi va uni o'zgartirsa, obyekt
ichidagi massiv o'zgarmaydi.
Lekin obyektlar uchun bu ishlamaydi:
private array $mahsulotlar = []; // ichida Mahsulot obyektlari
public function mahsulotlar(): array {
return $this->mahsulotlar; // massiv nusxa, OBYEKTLAR esa havola
}
Chaqiruvchi massiv nusxasini oladi, lekin undagi obyektlar o'shalar - ularni o'zgartirsa, sizning obyektingiz ichida ham o'zgaradi.
Yechimlar:
| Usul | Izoh |
|---|---|
Obyektlarni readonly qilish | Eng oddiy va tavsiya etiladi |
Chuqur nusxa (__clone) | Qimmat, lekin ishonchli |
readonly class (PHP 8.2) | Butun sinf o'zgarmas |
Amaliy tavsiya: ichki holatni qaytaradigan usullar o'zgarmas qiymatlar qaytarsin. Shunda bu muammo umuman paydo bo'lmaydi.
Statik a'zolar #
final class Hisoblagich
{
private static int $yaratilgan = 0;
private static array $royxat = [];
public const ENG_KOP = 3;
public function __construct(public readonly string $nom)
{
if (self::$yaratilgan >= self::ENG_KOP) {
throw new RuntimeException(
"Eng ko'pi " . self::ENG_KOP . " ta yaratish mumkin"
);
}
self::$yaratilgan++;
self::$royxat[] = $nom;
}
public static function soni(): int
{
return self::$yaratilgan;
}
public static function royxat(): array
{
return self::$royxat;
}
// Nomli konstruktor - statik usulning eng foydali qo'llanishi
public static function mehmon(): self
{
return new self("mehmon-" . (self::$yaratilgan + 1));
}
}
echo "Boshlang'ich soni: " . Hisoblagich::soni() . "\n";
echo "Chegara (doimiy): " . Hisoblagich::ENG_KOP . "\n\n";
$a = new Hisoblagich("birinchi");
$b = Hisoblagich::mehmon();
$c = new Hisoblagich("uchinchi");
echo "Yaratilganlar: " . implode(", ", Hisoblagich::royxat()) . "\n";
echo "Soni: " . Hisoblagich::soni() . "\n\n";
try {
new Hisoblagich("tortinchi");
} catch (RuntimeException $x) {
echo "Chegara ishladi: " . $x->getMessage() . "\n";
}
Boshlang'ich soni: 0
Chegara (doimiy): 3
Yaratilganlar: birinchi, mehmon-2, uchinchi
Soni: 3
Chegara ishladi: Eng ko'pi 3 ta yaratish mumkin
self, static va $this farqi| Kalit so'z | Nimaga ishora qiladi |
|---|---|
$this | Joriy obyekt namunasi |
self | Yozilgan sinf |
static | Chaqirilgan sinf (kech bog'lanish) |
Farq vorislikda ko'rinadi:
class Asos {
public static function yarat(): static {
return new static(); // VORIS sinfni yaratadi
}
public static function yaratSelf(): self {
return new self(); // har doim Asos yaratadi
}
}
class Voris extends Asos {}
Voris::yarat(); // Voris obyekti
Voris::yaratSelf(); // Asos obyekti
Nomli konstruktorlar uchun deyarli har doim static
kerak - shunda voris sinflarda ham to'g'ri ishlaydi.
Statik holatdan esa ehtiyot bo'ling: u global o'zgaruvchi bilan bir xil muammolarga ega. Sinovlarda bir test ikkinchisiga ta'sir qiladi, chunki statik qiymat so'rovlar orasida saqlanib qoladi.
Statik usullarni asosan nomli konstruktor va toza yordamchi funksiyalar uchun ishlating.
Sehrli usullar #
final class Sozlama
{
private array $qiymatlar = [];
public function __construct(array $boshlangich = [])
{
$this->qiymatlar = $boshlangich;
}
public function __get(string $nom): mixed
{
return $this->qiymatlar[$nom] ?? null;
}
public function __set(string $nom, mixed $qiymat): void
{
$this->qiymatlar[$nom] = $qiymat;
}
public function __isset(string $nom): bool
{
return isset($this->qiymatlar[$nom]);
}
public function __toString(): string
{
$qismlar = [];
foreach ($this->qiymatlar as $k => $v) {
$qismlar[] = $k . "=" . (is_bool($v) ? ($v ? "true" : "false") : (string) $v);
}
return implode(", ", $qismlar);
}
}
$s = new Sozlama(["til" => "uz", "mavzu" => "tungi"]);
echo "__get: " . $s->til . "\n";
echo "Yo'q kalit: " . var_export($s->rang, true) . "\n";
$s->hajm = 14;
echo "__set dan keyin hajm: {$s->hajm}\n";
echo "\n";
echo "__isset('til'): " . (isset($s->til) ? "ha" : "yo'q") . "\n";
echo "__isset('rang'): " . (isset($s->rang) ? "ha" : "yo'q") . "\n";
echo "\n";
echo "__toString: {$s}\n";
__get: uz
Yo'q kalit: NULL
__set dan keyin hajm: 14
__isset('til'): ha
__isset('rang'): yo'q
__toString: til=uz, mavzu=tungi, hajm=14
__get va __set kodni qisqartiradi, lekin uch narsani
buzadi:
| Nima buziladi | Sabab |
|---|---|
| Avtoto'ldirish | Muharrir qanday xossalar borligini bilmaydi |
| Statik tahlil | Xato yozilgan nom topilmaydi |
| Tezlik | Oddiy xossaga murojaatdan sekinroq |
Eng yomoni birinchisi: $s->tli deb xato yozsangiz,
kod xato bermaydi - null qaytaradi va muammo uzoqda
ko'rinadi.
Qachon o'rinli:
| O'rinli | O'rinsiz |
|---|---|
| Sozlama konteyneri | Oddiy ma'lumot sinfi |
| ORM (bazadan kelgan maydonlar) | Xossalar oldindan ma'lum bo'lsa |
| Proksi va dekorator | Faqat "kam yozish" uchun |
__toString esa boshqacha - u foydali va xavfsiz.
Faqat bitta qoida: u hech qachon istisno
tashlamasin va har doim satr qaytarsin.
- Massiv bilan ishlovchi kodni sinfga aylantiring.
- Konstruktorda tekshiruv qo'ying va noto'g'ri qiymat bering.
- Konstruktor xossalarini ko'chirish sintaksisini qo'llang.
readonlyxossani o'zgartirishga urinib ko'ring.privatexossaga tashqaridan murojaat qiling.- Massiv qaytaruvchi usul yozing va natijani o'zgartiring - asli o'zgardimi?
- Statik hisoblagich bilan yaratilgan obyektlar sonini sanang.
- Nomli konstruktor (
static function yarat()) yozing. selfvastaticfarqini vorislikda sinab ko'ring.__getbilan xossa yozing va nomini xato yozib ko'ring.
Xulosa #
- Massiv tuzilmani kafolatlamaydi - xato yozilgan kalit jimgina
nullberadi. - Sinf konstruktorda invariantni tekshiradi: noto'g'ri obyekt umuman yaratilmaydi.
- Konstruktor xossalarini ko'chirish (PHP 8) uch marta yozishni bir martaga tushiradi.
readonlyxossa bir marta yoziladi va keyin o'zgarmaydi.readonlyfaqat tur e'lon qilingan xossaga qo'llanadi.- PHP 8.2 da
readonly classbutun sinfni o'zgarmas qiladi. - Kirish darajasini eng tordan boshlang - kengaytirish oson, toraytirish esa buzadi.
- Massiv qiymat bo'yicha qaytariladi, lekin ichidagi obyektlar havola.
self- yozilgan sinf,static- chaqirilgan sinf; nomli konstruktordastaticishlating.- Statik holat global o'zgaruvchi bilan bir xil muammolarga ega - ehtiyot bo'ling.
__get/__setavtoto'ldirish va statik tahlilni buzadi - kam ishlating.
Keyingi bo'limda vorislik, interfeys va trait ni
ko'ramiz.
O‘qish tarixini saqlamoqchimisiz?
Tizimga kirsangiz, tugatgan bo‘limlaringiz saqlanadi va qoldirgan joyingizdan davom etasiz.
Xatolik topdingizmi?
Imlo xatosi, ishlamaydigan kod yoki noto‘g‘ri ma‘lumotni ko‘rsangiz - bizga xabar bering. Har bir xabar administrator tomonidan ko‘rib chiqiladi.