2.7 Static xususiyat va metodlar¶
β¬ οΈ Oldingi: 2.6 Interfeys (interface) Β· π README Β· Keyingi: 2.8 Trait β metodlarni ulashish β‘οΈ
Obyektga emas, class'ning o'ziga tegishli¶
Hozirgacha xususiyat va metodlar har bir obyektga tegishli edi: $mashina1->rang, $mashina2->rang β har birida alohida. Lekin ba'zan ma'lumot yoki amal butun class'ga tegishli bo'ladi, alohida obyektga emas. Ana shunda static ishlatiladi.
Misol: nechta obyekt yaratilganini sanash. Bu son birorta obyektga emas, butun class'ga tegishli.
<?php
class Talaba {
public static int $soni = 0; // static β butun class'ga tegishli (PHP 8.4: tiplangan)
public function __construct() {
self::$soni++; // har obyekt yaratilganda umumiy hisobni oshiramiz
}
}
new Talaba();
new Talaba();
new Talaba();
echo Talaba::$soni; // 3
Diqqat qiling:
- public static int $soni = 0; β static so'zi: bu xususiyat obyektlarga emas, class'ning o'ziga tegishli. U bitta, hamma obyektlar uchun umumiy. Zamonaviy PHP'da static xususiyatlarni ham tiplaymiz (int) β bu xatolardan saqlaydi.
- self::$soni β static xususiyatga class ichidan murojaat. self β "shu class" degani ($this "shu obyekt" edi; self "shu class").
- Talaba::$soni β static xususiyatga tashqaridan murojaat. Obyekt orqali emas ($t->soni emas), balki class nomi orqali (Talaba::$soni). :: belgisi static narsalar uchun ishlatiladi.
Uchta obyekt yaratdik, har biri konstruktorda self::$soni++ qildi β natijada umumiy hisob 3 bo'ldi.
Eslatma (zamonaviy uslub): eski PHP 5/7 kodida ko'pincha tipsiz
public static $soni = 0;yoziladi. PHP 7.4 dan boshlab xususiyatlarga tip qo'yish mumkin βpublic static int $soni = 0;. Tip qo'ysangiz, noto'g'ri qiymat berilsa PHP darrov ogohlantiradi. Shu kitobda biz hamma joyda tiplangan uslubni ishlatamiz.
Static metodlar¶
Metod ham static bo'lishi mumkin β bunda uni obyekt yaratmasdan, to'g'ridan-to'g'ri class orqali chaqirasiz. Bu odatda "yordamchi" (utility) funksiyalar uchun ishlatiladi:
<?php
class Matematika {
public static function kvadrat(int|float $son): int|float {
return $son * $son;
}
public static function kub(int|float $son): int|float {
return $son * $son * $son;
}
}
// Obyekt yaratish SHART EMAS β to'g'ridan-to'g'ri class orqali:
echo Matematika::kvadrat(5); // 25
echo "<br>";
echo Matematika::kub(3); // 27
Matematika::kvadrat(5) β obyekt yaratmadik (new yo'q), to'g'ridan-to'g'ri class orqali metodni chaqirdik. Bu mantiqiy: "kvadratni hisoblash" uchun "matematika obyekti" yaratish shart emas β funksiyaning o'zi yetarli. E'tibor bering: argumentni int|float (ikkala tip ham mumkin) deb tipladik va qaytarish tipini ham ko'rsatdik β bu xato kiritishdan himoya qiladi.
Qachon static? Agar metod yoki ma'lumot biror aniq obyektga bog'liq bo'lmasa β static qiling. Masalan, "kvadratni hisoblash" hech qaysi obyektga bog'liq emas β static. Lekin "shu hisobning balansi" β aniq obyektga bog'liq β static EMAS. Boshlanishida static'ni kam ishlating; ko'pincha oddiy (obyektga tegishli) metodlar to'g'ri tanlov.
self vs static β meros bilan eng muhim farq (Late Static Binding)¶
Endi static mavzusining eng muhim va eng ko'p chalkashtiradigan qismiga keldik. self:: va static:: β ikkalasi ham "class'ning static a'zosi" degani, lekin meros (extends) ishtirokida ular boshqacha ishlaydi:
self::β har doim e'lon qilingan class'ni (kod yozilgan joyni) bildiradi. U meros bilan o'zgarmaydi.static::β aktual (haqiqatda chaqirilgan) class'ni bildiradi. U meros bilan o'zgaradi. Bu xatti-harakat Late Static Binding ("kech static bog'lanish") deb ataladi: PHP qaysi class'ni ishlatishni kompilyatsiya paytida emas, chaqiruv paytida hal qiladi.
Eng yaxshi tushuntiruvchi misol β bola class ota class'dagi metodni qayta yozsa:
<?php
class Asosiy {
public static function salom(): string {
return "Asosiy class'dan salom";
}
public static function selfTest(): string {
return self::salom(); // self:: β SHU class (Asosiy) ni qattiq bog'laydi
}
public static function staticTest(): string {
return static::salom(); // static:: β AKTUAL (chaqirgan) class'ni ishlatadi
}
}
class Bola extends Asosiy {
public static function salom(): string { // ota metodni qayta yozdik
return "Bola class'dan salom";
}
}
echo Bola::selfTest(); // "Asosiy class'dan salom" β self qattiq Asosiy'ga bog'langan
echo "<br>";
echo Bola::staticTest(); // "Bola class'dan salom" β static aktual class (Bola) ni topdi
Diqqat bilan kuzating:
- Bola::selfTest() ni chaqirdik. selfTest() ichida self::salom() bor. self "kod yozilgan joy" β ya'ni Asosiy class. Shuning uchun Bola'dagi qayta yozilgan salom() e'tiborga olinmaydi, Asosiy'niki ishlaydi.
- Bola::staticTest() ni chaqirdik. staticTest() ichida static::salom() bor. static "kim chaqirgan bo'lsa, o'sha" β ya'ni Bola. Shuning uchun Bola'dagi yangi salom() ishlaydi.
Oddiy qoida: agar bola class metodni qayta yozishi mumkin bo'lsa va siz eng so'nggi (bola) versiyani ishlatmoqchi bo'lsangiz β
static::ishlating. Agar aniq shu yerda yozilgan versiya kerak bo'lsa βself::. Amalda zamonaviy frameworklar (Laravel kabi) deyarli hamma joydastatic::ishlatadi, chunki kengaytirish moslashuvchanligini beradi.
new static() β meros bilan ishlaydigan factory metod¶
static:: faqat metodlarda emas, obyekt yaratishda ham juda foydali. new self() doim aniq shu class obyektini yaratadi; new static() esa aktual class obyektini yaratadi. Bu farq static factory metod ("yasovchi metod") naqshida juda muhim.
Factory metod β bu obyektni new o'rniga mazmunli nom bilan yaratadigan static metod. User::yarat(...) new User(...) dan o'qishga qulayroq va ko'pincha qo'shimcha mantiq (validatsiya, standart qiymatlar) joylash uchun qulay:
<?php
class Foydalanuvchi {
// PHP 8.4: constructor promotion β qisqa, tiplangan konstruktor
public function __construct(
public string $ism,
public string $email,
) {}
// Static factory metod β obyekt yaratishning mazmunli yo'li
public static function yarat(string $ism, string $email): static {
return new static($ism, $email); // new static -> aktual class obyekti
}
// Tayyor qiymatlar bilan "mehmon" yaratuvchi factory
public static function mehmon(): static {
return new static("Mehmon", "mehmon@example.com");
}
}
class Admin extends Foydalanuvchi {
public function rol(): string {
return "administrator";
}
}
$u = Foydalanuvchi::yarat("Ali", "ali@example.com");
echo $u->ism . " β " . $u->email; // Ali β ali@example.com
echo "<br>";
// MUHIM: new static() tufayli Admin::yarat() Admin obyektini qaytaradi (Foydalanuvchi emas!)
$a = Admin::yarat("Vali", "vali@example.com");
echo get_class($a); // Admin
echo "<br>";
echo $a->rol(); // administrator β chunki bu haqiqatan Admin obyekti
$mehmon = Foydalanuvchi::mehmon();
echo "<br>";
echo $mehmon->ism; // Mehmon
Bu yerda sehr new static() da:
- Foydalanuvchi::yarat(...) chaqirilsa, new static() Foydalanuvchi obyektini yaratadi.
- Admin::yarat(...) chaqirilsa, xuddi shu yarat() metodi (Admin uni meros oldi) endi new static() orqali Admin obyektini yaratadi.
Agar new self() yozganimizda, Admin::yarat() baribir Foydalanuvchi obyektini qaytarardi β chunki self qattiq Foydalanuvchiga bog'langan bo'lardi. Demak, factory metodlarda deyarli har doim new static() ishlatiladi: shunda metod meros bilan to'g'ri ishlaydi.
Diqqat: qaytarish tipi sifatida
: staticyozdik. Bu PHP 8.0 dan beri mavjud va "qaytariladigan obyekt aktual class'niki bo'ladi" deb va'da beradi β IDE va kelajakdagi o'zingiz uchun aniq hujjat.
Static xususiyat meros bilan qanday ulashiladi¶
Static xususiyat (metod emas) meros qilinganda yana bir nozik holat bor: bola class otaning static xususiyatini qayta e'lon qilmasa, ular bitta umumiy xotirani bo'lishadi:
<?php
class Ulagich {
public static int $soni = 0;
public function __construct() {
static::$soni++; // aktual class hisoblagichini oshiramiz
}
}
class Bola extends Ulagich {} // $soni ni qayta e'lon qilmadi -> umumiy
new Ulagich(); // soni -> 1
new Bola(); // umumiy bo'lgani uchun -> 2
new Bola(); // -> 3
echo "Ulagich: " . Ulagich::$soni; // 3
echo "<br>";
echo "Bola: " . Bola::$soni; // 3 β IKKALASI ham bitta xotirani ulashadi!
Bola $sonini qayta e'lon qilmaganligi sababli, Ulagich::$soni va Bola::$soni bir xil o'zgaruvchi β ikkalasi ham 3. Agar har bir class'da alohida hisoblagich kerak bo'lsa, bola class xususiyatni qayta e'lon qilishi kerak:
<?php
class Ulagich {
public static int $soni = 0;
public function __construct() {
static::$soni++; // static:: -> aktual class'ning o'z hisoblagichi
}
}
class Bola extends Ulagich {
public static int $soni = 0; // O'ZINING alohida hisoblagichi
}
new Ulagich();
new Bola();
new Bola();
echo "Ulagich: " . Ulagich::$soni; // 1
echo "<br>";
echo "Bola: " . Bola::$soni; // 2 β endi alohida
E'tibor bering: bu yerda konstruktorda self::$soni++ emas, static::$soni++ yozdik. static:: aktual class'ning xususiyatini topadi β shuning uchun Bola yaratilganda Bola'ning o'z $sonisi oshadi. Agar self::$soni++ yozganimizda, doim Ulagich'niki oshib qolardi. Bu yana bir misol: meros bilan ishlaydigan static kod uchun static:: tanlang.
Static hisoblagich β chuqurroq qarash¶
Yuqorida ko'rgan oddiy hisoblagichni "to'liq" qilamiz: hisobni o'qish va nollash metodlari bilan. Bu obyektga bog'liq bo'lmagan umumiy holatni boshqarishning klassik misoli:
<?php
class Hisoblagich {
public static int $soni = 0;
public function __construct() {
self::$soni++;
}
public static function qancha(): int {
return self::$soni; // joriy qiymatni o'qish
}
public static function nolla(): void {
self::$soni = 0; // qaytadan boshlash
}
}
new Hisoblagich();
new Hisoblagich();
echo Hisoblagich::qancha(); // 2
echo "<br>";
Hisoblagich::nolla();
echo Hisoblagich::qancha(); // 0
echo "<br>";
new Hisoblagich();
echo Hisoblagich::qancha(); // 1
Static $soni dastur ishlayotgan vaqt davomida bitta va saqlanib turadi β har yangi obyekt uni oshiradi, nolla() esa qaytadan boshlaydi. Bu naqsh: faol foydalanuvchilarni sanash, bajarilgan so'rovlar sonini hisoblash, test paytida holatni tozalash kabi joylarda ishlatiladi.
Ogohlantirish: static holat (umumiy o'zgaruvchi) qulay, lekin ko'p ishlatish kodni testlash va kuzatishni qiyinlashtiradi β chunki "kim qachon o'zgartirgani" yashirin bo'ladi. Shuning uchun static'ni faqat haqiqatan butun class'ga umumiy ma'lumot uchun ishlating, oddiy ma'lumotni esa obyekt ichida saqlang.
Mashqlar¶
Oson
1. Talaba class'iga static $soni qo'shing. Bir nechta obyekt yarating va umumiy sonni chiqaring.
2. Matematika class'iga static kvadrat($son) metodini qo'shing, obyektsiz chaqiring.
3. Matematikaga static kub() va ikkilantir() metodlarini qo'shing.
4. Static xususiyatga obyekt orqali emas, class nomi (::) orqali murojaat qiling.
5. Konvertor class'i: static kmdanMilga($km) metodi (km Γ 0.621) qaytarsin.
O'rta
6. Mahsulot class'i: static $umumiySoni har obyekt yaratilganda oshsin. 5 ta mahsulot yaratib, sonini chiqaring.
7. Yordamchi class'i: static formatNarx($son) metodi sonni "5 000 so'm" ko'rinishida qaytarsin (number_format dan foydalaning).
8. Hisoblagich class'i: static $qiymat, static oshir() va qiymatniOl() metodlari bilan.
9. Tasodif class'i: static tanga() metodi tasodifiy "bosh" yoki "tail" qaytarsin (rand(0, 1) dan foydalaning).
10. (LSB) Shakl abstract class'ida static yarat() metodi new static() qaytarsin. Doira va Kvadrat bola class'larini yarating; har biri nomi() ni qaytarsin. Doira::yarat()->nomi() va Kvadrat::yarat()->nomi() ni chiqaring va natija farqini kuzating.
Qiyin
11. IDgenerator class'i: static $oxirgiId = 0. Static yangiId() metodi har chaqirilganda IDni 1 ga oshirib qaytarsin (1, 2, 3, ...). Bu β har bir yangi yozuvga noyob raqam berishning oddiy usuli.
12. Foydalanuvchi class'i: konstruktor ism olsin va static $royxat massiviga o'zini qo'shsin. Static hammasi() metodi barcha foydalanuvchilar ro'yxatini qaytarsin.
13. Statistika class'i: static metodlar bilan son massividan eng katta, eng kichik va o'rtacha qiymatni qaytaruvchi vositalar to'plamini yarating.
14. (Self vs static) Asosiy class'ida static kim() metodi "Asosiy" qaytarsin va selfChaqir()/staticChaqir() metodlari mos ravishda self::kim() va static::kim() ni qaytarsin. Bola class'ida kim() ni "Bola" ga qayta yozing. Bola::selfChaqir() va Bola::staticChaqir() natijalarini taqqoslab tushuntiring.
15. (Factory + LSB) Model class'i: konstruktor int $id olsin (constructor promotion). Static topish(int $id): static metodi new static($id) qaytarsin. jadval() metodi static::class asosida jadval nomini qaytarsin (masalan Mahsulot -> "mahsulotlar"). Mahsulot va Buyurtma bola class'larini yarating va Mahsulot::topish(5) qaysi class obyektini va qaysi jadval nomini berishini ko'rsating.
Yechim β 10 (Shakl factory + new static)
<?php
abstract class Shakl {
public static function yarat(): static {
return new static(); // aktual class obyektini yaratadi
}
abstract public function nomi(): string;
}
class Doira extends Shakl {
public function nomi(): string { return "Doira"; }
}
class Kvadrat extends Shakl {
public function nomi(): string { return "Kvadrat"; }
}
echo Doira::yarat()->nomi(); // Doira
echo "<br>";
echo Kvadrat::yarat()->nomi(); // Kvadrat
yarat() faqat bir marta (ota class'da) yozilgan, lekin new static() tufayli Doira::yarat() Doira obyektini, Kvadrat::yarat() Kvadrat obyektini qaytaradi. Agar new self() bo'lganida, abstract class'ni yaratishga urinib xato berardi. Bu β bitta factory metodni hamma bola class'lar uchun qayta ishlatishning kuchi.
Yechim β 11 (ID generator)
<?php
class IDgenerator {
public static int $oxirgiId = 0;
public static function yangiId(): int {
self::$oxirgiId++; // umumiy hisobni oshiramiz
return self::$oxirgiId;
}
}
echo IDgenerator::yangiId(); // 1
echo "<br>";
echo IDgenerator::yangiId(); // 2
echo "<br>";
echo IDgenerator::yangiId(); // 3
$oxirgiId bitta va saqlanib turadi β har chaqiruvda davom etadi. Bu obyektga bog'liq bo'lmagan, "umumiy hisoblagich" uchun ajoyib misol. E'tibor bering: xususiyatni int deb, metodni : int deb tipladik β zamonaviy uslub.
Yechim β 12 (Foydalanuvchilar ro'yxati)
<?php
class Foydalanuvchi {
public static array $royxat = []; // barcha foydalanuvchilar β butun class'ga umumiy
// PHP 8.4: constructor promotion β $ism ni darrov xususiyatga aylantiradi
public function __construct(public string $ism) {
self::$royxat[] = $ism; // o'zini umumiy ro'yxatga qo'shadi
}
public static function hammasi(): array {
return self::$royxat;
}
}
new Foydalanuvchi("Ali");
new Foydalanuvchi("Vali");
new Foydalanuvchi("Guli");
print_r(Foydalanuvchi::hammasi()); // ["Ali", "Vali", "Guli"]
$royxat static β u bitta va barcha obyektlar uchun umumiy. Har bir yangi foydalanuvchi konstruktorda self::$royxatga qo'shiladi, shuning uchun hammasi() to'liq ro'yxatni beradi. self:: β "shu class'ning static a'zosi" (obyektda $this, static'da self). Konstruktorda public string $ism yozish (constructor promotion) $this->ism = $ism; qatorini avtomatik bajaradi β qisqa va aniq.
Yechim β 13 (Statistika vositalari)
<?php
class Statistika {
public static function engKatta(array $sonlar): int|float {
return max($sonlar);
}
public static function engKichik(array $sonlar): int|float {
return min($sonlar);
}
public static function ortacha(array $sonlar): float {
return array_sum($sonlar) / count($sonlar);
}
}
$ballar = [85, 90, 70, 95, 60];
echo "Eng katta: " . Statistika::engKatta($ballar) . "<br>"; // 95
echo "Eng kichik: " . Statistika::engKichik($ballar) . "<br>"; // 60
echo "O'rtacha: " . Statistika::ortacha($ballar); // 80
Statistika::ortacha(...) deb to'g'ridan-to'g'ri ishlatamiz. Bu β "yordamchi funksiyalar to'plami" uchun mos naqsh. Argument va qaytarish tiplarini ko'rsatish kodni xatolardan himoya qiladi.
Yechim β 14 (self vs static)
<?php
class Asosiy {
public static function kim(): string {
return "Asosiy";
}
public static function selfChaqir(): string {
return self::kim(); // qattiq Asosiy'ga bog'langan
}
public static function staticChaqir(): string {
return static::kim(); // aktual class'ni topadi
}
}
class Bola extends Asosiy {
public static function kim(): string {
return "Bola"; // ota metodni qayta yozdik
}
}
echo Bola::selfChaqir(); // "Asosiy" β self e'lon qilingan joyni (Asosiy) oladi
echo "<br>";
echo Bola::staticChaqir(); // "Bola" β static aktual class (Bola) ni oladi
selfChaqir() ichidagi self::kim() doim Asosiy'ning kim()ini chaqiradi, chunki self "kod yozilgan class"ga qattiq bog'langan. staticChaqir() ichidagi static::kim() esa "kim chaqirgan bo'lsa, o'shaning" kim()ini chaqiradi β biz Bola::staticChaqir() deganimiz uchun Bola'niki ishlaydi. Mana shu β Late Static Binding: bola class metodni qayta yozsa va siz eng so'nggi versiyani xohlasangiz, static:: ishlatishingiz kerak.
Yechim β 15 (Factory + LSB: Model)
<?php
class Model {
public function __construct(public int $id) {} // constructor promotion
public static function topish(int $id): static {
return new static($id); // aktual class obyekti
}
public function jadval(): string {
return strtolower(static::class) . "lar"; // static::class -> aktual class nomi
}
}
class Mahsulot extends Model {}
class Buyurtma extends Model {}
$p = Mahsulot::topish(5);
echo get_class($p) . " #" . $p->id . " -> " . $p->jadval() . "<br>";
// Mahsulot #5 -> mahsulotlar
$o = Buyurtma::topish(9);
echo get_class($o) . " #" . $o->id . " -> " . $o->jadval();
// Buyurtma #9 -> buyurtmalar
topish() faqat bir marta (Model'da) yozilgan, ammo new static($id) tufayli Mahsulot::topish(5) Mahsulot obyektini, Buyurtma::topish(9) Buyurtma obyektini beradi. static::class esa aktual class nomini matn sifatida qaytaradi β shuning uchun jadval() har bir class uchun to'g'ri nom (mahsulotlar, buyurtmalar) hosil qiladi. Mana shunday yondashuv haqiqiy ORM kutubxonalarida (masalan Laravel'ning Eloquent'ida) User::find(1) kabi metodlar ortida turadi.