2.12 Namespace va autoloading¶
β¬ οΈ Oldingi: 2.11 Magic metodlar Β· π README Β· Keyingi: 3.1 Ma'lumotlar bazasi nima va nega kerak? β‘οΈ
Loyihangiz o'sib boradi: bugun 5 ta class, ertaga 50 ta, keyin 500 ta. Va shu yerda ikkita og'riq paydo bo'ladi.
Birinchisi β nomlar to'qnashuvi. Siz User degan class yozdingiz. Tashqi kutubxonada ham User bor. Ikkalasini bitta loyihada ishlatmoqchisiz β PHP "Cannot redeclare class User" deb baqiradi. Bitta loyihada bir xil nomli ikkita class yashay olmaydi.
Ikkinchisi β qo'lda yuklash azobi. Har bir class alohida faylda. Har birini ishlatishdan oldin require 'fayl.php' yozish kerak. 50 ta class β index.php boshida 50 qator require. Bittasini unutsangiz β "Class not found" xatosi. Birini ko'chirsangiz β barcha yo'llarni qayta yozasiz.
Namespace birinchi muammoni hal qiladi β class'larni "papkalarga" ajratib, bir xil nomlilar yonma-yon yashashiga imkon beradi. Autoloading ikkinchisini hal qiladi β class kerak bo'lganda PHP uni avtomatik topib yuklaydi, siz require yozmaysiz. Ushbu bobda ikkalasini ham, va ularni birlashtirgan zamonaviy standart β PSR-4 ni o'rganamiz.
Muammoni ko'ramiz: nomlar to'qnashuvi¶
Tasavvur qiling, loyihangizda foydalanuvchini bildiradigan User class bor. Bir vaqtning o'zida to'lov kutubxonasida ham User bor (ularning "user" tushunchasi β boshqacha). Ikkalasini bitta faylga qo'shsangiz:
<?php
class User { // sizniki
public string $ism = "Ali";
}
class User { // β Fatal error: Cannot redeclare class User
public string $karta = "1234";
}
PHP ishga tushmaydi. Sabab oddiy: global "maydonda" User degan nom faqat bittaga tegishli bo'la oladi. Ikkita kishi bir uyda bir xil ismda yashayolmaydi β kimligini ajratib bo'lmaydi.
Yechim β har bir User ni o'z "papkasi"ga joylash. Aynan shuni namespace qiladi.
Namespace e'lon qilish β namespace¶
Namespace (nom fazosi) β class, funksiya va konstantalarni mantiqiy guruhlarga ajratuvchi "nomli konteyner". Uni faylning eng boshida, namespace kalit so'zi bilan e'lon qilasiz:
Endi bu User ning "to'liq nomi" β App\Models\User. U boshqa namespace'dagi User bilan to'qnashmaydi, chunki ular boshqa-boshqa "papkalarda".
Muhim qoidalar:
namespacee'loni faylning eng birinchi kod qatori bo'lishi shart (<?phpdan keyin, har qanday boshqa koddan oldin).\(backslash) β namespace bo'laklarini ajratuvchi belgi.App\Modelsβ "App ichidagi Models".- Odatda bir faylga bitta namespace va bitta class joylanadi (bu PSR-4 standarti talabi β pastda ko'ramiz).
To'liq malakali nom (Fully Qualified Name)¶
Namespace'dagi class'ni boshqa joydan ishlatish uchun uning to'liq nomini yozasiz β \ bilan boshlab, butun yo'lni:
<?php
// 1-fayl: User class App\Models namespace'ida e'lon qilingan deb hisoblaymiz
$u = new \App\Models\User("Ali");
echo $u->ism; // Ali
\App\Models\User β bu to'liq malakali nom (fully qualified name, FQN). Boshidagi \ "global ildizdan boshla" degani β xuddi fayl tizimidagi /home/user kabi mutlaq yo'l. Bu nom aniq, lekin uzun. Har safar to'liq yozish charchatadi. Shu sababli use bor.
Import qilish β use¶
use β namespace'dan biror class'ni joriy faylga "olib kirish" (import). Shundan keyin uni qisqa nomi bilan ishlatasiz:
<?php
namespace App\Controllers;
use App\Models\User; // App\Models\User ni import qildik
class UserController {
public function yarat(): User { // endi qisqa "User" yetarli
return new User("Ali");
}
}
use App\Models\User; dan keyin shu faylda har qanday User β aynan App\Models\User ni bildiradi. To'liq yo'lni qayta-qayta yozish shart emas.
Diqqat:
useda nom boshida\yozilmaydi (use App\Models\User,use \App\Models\Useremas).usedoim global ildizdan boshlaydi, shuning uchun boshlang'ich\ortiqcha.
Alias berish β use ... as¶
Ikki xil namespace'dan bir xil nomli class'larni import qilsangiz, baribir to'qnashuv bo'ladi. as β import qilingan nomga taxallus (alias) berib, shu muammoni hal qiladi:
<?php
namespace App\Services;
use App\Models\User; // bizning User
use Payment\Models\User as PaymentUser; // to'lov kutubxonasi User'i β boshqa nom bilan
class CheckoutService {
public function ishla(): string {
$bizniki = new User("Ali");
$tolov = new PaymentUser("1234-5678");
return $bizniki->ism;
}
}
Endi User β bizning class, PaymentUser β kutubxonaniki. Ikkalasi yonma-yon, hech qanday to'qnashuvsiz. as ayniqsa uzun yoki noqulay class nomini qisqartirishda ham foydali: use Some\Very\Long\Namespace\ReportGenerator as Report;.
Global namespace β boshidagi \¶
Siz namespace ichida bo'lganingizda, PHP'ning o'rnatilgan class va funksiyalari (Exception, DateTime, strlen ...) "global namespace"da turadi. Namespace ichidan ularga murojaat qilganda boshiga \ qo'yiladi:
<?php
namespace App\Services;
class DateService {
public function hozir(): string {
// \DateTime β global namespace'dagi PHP class'i.
// Agar \ qo'ymasak, PHP App\Services\DateTime ni qidiradi va topmaydi!
$sana = new \DateTime();
return $sana->format("Y-m-d");
}
}
$s = new DateService();
echo $s->hozir(); // masalan: 2026-06-11
Sabab: namespace ichida new DateTime() deyilsa, PHP avval joriy namespace'da (App\Services\DateTime) qidiradi, topmasa xato beradi. Boshiga \ qo'yib (new \DateTime()), "global ildizdan ol" deysiz.
Maslahat: Funksiyalar uchun (
strlen,count...) PHP avtomatik global'ga "tushib" qidiradi, shuning uchun ularda\ko'pincha shart emas. Lekin class'larda har doim\kerak yoki yuqoridause \DateTime;qiling.
Bir faylda bir nechta namespace (va nega bunday qilinmaydi)¶
Texnik jihatdan bitta faylda bir nechta namespace e'lon qilish mumkin, lekin buning uchun jingalak qavs { } sintaksisi ishlatiladi:
<?php
namespace App\Models {
class Product {
public function __construct(public string $nom) {}
}
}
namespace App\Services {
class Cart {
public function qosh(\App\Models\Product $p): string {
return $p->nom;
}
}
}
namespace { // nomsiz blok β global namespace
$p = new \App\Models\Product("Kitob");
$c = new \App\Services\Cart();
echo $c->qosh($p); // Kitob
}
Lekin amalda bunday qilinmaydi. Standart (va PSR-4) qoidasi: bir fayl β bitta namespace, bitta class. Yuqoridagi shakl faqat texnik imkoniyatni ko'rsatish uchun. Haqiqiy loyihada har bir class o'z faylida, fayl boshida bitta namespace bilan turadi.
Endi ikkinchi muammo: qo'lda require azobi¶
Namespace nomlar muammosini hal qildi. Lekin class'lar baribir alohida fayllarda, va har birini ishlatishdan oldin yuklash kerak. Hozircha buni require bilan qilamiz:
<?php
require 'src/Models/User.php';
require 'src/Models/Product.php';
require 'src/Services/Cart.php';
require 'src/Services/Checkout.php';
// ... yana 40 ta require ...
use App\Models\User;
$u = new User("Ali");
Loyiha kattalashgani sayin bu ro'yxat cheksiz o'sadi. Birini unutsangiz β "Class not found". Faylni boshqa papkaga ko'chirsangiz β yo'lni hamma joyda tuzatasiz. Autoloading ana shu azobni butunlay yo'q qiladi.
Autoloading nima?¶
Autoloading (avtomatik yuklash) β class birinchi marta ishlatilganda PHP uni o'zi topib, faylini yuklash mexanizmi. Ya'ni siz new User() deysiz, PHP "User class hali yuklanmagan-ku" deb sezadi va sahna ortida kerakli faylni topib ulaydi. Sizning require yozishingiz shart emas.
Buning siri β spl_autoload_register funksiyasi. Unga "qaysidir class kerak bo'lganda nima qilish kerakligini" tushuntiruvchi funksiya berasiz, PHP esa har "topilmagan class" holatida o'sha funksiyani chaqiradi.
Qo'lda autoloader yozish β spl_autoload_register¶
Eng oddiy autoloader β class nomini fayl yo'liga aylantiradigan funksiya:
<?php
spl_autoload_register(function (string $class): void {
// $class β to'liq class nomi, masalan "Models\User"
$fayl = __DIR__ . "/" . $class . ".php";
if (file_exists($fayl)) {
require $fayl;
echo "[autoload] yuklandi: {$fayl}\n";
}
});
// Bu nuqtada hech narsa require qilinmagan.
// Quyida class'ga murojaat qilamiz β PHP avtomatik yuklaydi:
$u = new \Models\User("Ali");
echo $u->ism;
Mexanizm:
- PHP
\Models\Userga duch keladi, lekin u hali yuklanmagan. - PHP ro'yxatdan o'tgan autoloader funksiyani chaqiradi va unga
"Models\User"nomini beradi. - Funksiya bu nomdan fayl yo'lini yasaydi (
__DIR__/Models\User.php) varequireqiladi. - Class endi mavjud β PHP ishni davom ettiradi.
__DIR__ β joriy fayl turgan papkaning to'liq yo'li (sehrli konstanta, 2.11-bobda ko'rgansiz). Bir vaqtda bir nechta autoloader ro'yxatga olish ham mumkin β PHP ularni navbat bilan sinab ko'radi.
\va papka belgisi: namespace'da ajratgich\, fayl yo'lida esa/. Shu sababli real autoloaderda\ni/ga almashtirish kerak bo'ladi β buni quyidagi PSR-4 misolda ko'ramiz.
PSR-4 standarti β namespace va papka tuzilishi mosligi¶
Har kim o'z autoloaderini o'zicha yozsa, har loyiha boshqacha bo'lardi. Shu sababli PHP hamjamiyati PSR-4 degan standartni kelishib oldi. (PSR β "PHP Standard Recommendation", PHP standart tavsiyasi.)
PSR-4 ning asosiy g'oyasi: namespace tuzilishi papka tuzilishini aks ettiradi. Ya'ni:
App\Models\Userclass βsrc/Models/User.phpfaylidaApp\Services\Cartclass βsrc/Services/Cart.phpfaylida- namespace'dagi
\β papkadagi/ - class nomi β fayl nomi (
.phpbilan)
Bu yerda App\ "namespace prefiksi" src/ "asosiy papka"ga bog'langan. Prefiksdan keyingi qism aynan papka yo'liga aylanadi. Mana PSR-4 ga mos autoloader:
<?php
spl_autoload_register(function (string $class): void {
$prefiks = "App\\"; // shu prefiks bilan boshlanadigan class'lar
$asosiyDir = __DIR__ . "/src/"; // shu papkada turadi
// class shu prefiks bilan boshlanmasa β bu autoloaderga tegishli emas
if (!str_starts_with($class, $prefiks)) {
return;
}
// Prefiksni olib tashlaymiz: "App\Models\User" -> "Models\User"
$nisbiy = substr($class, strlen($prefiks));
// \ -> / ga almashtirib, fayl yo'lini yasaymiz
$fayl = $asosiyDir . str_replace("\\", "/", $nisbiy) . ".php";
if (file_exists($fayl)) {
require $fayl;
}
});
Endi new App\Models\User() deganda autoloader avtomatik src/Models/User.php ni topib yuklaydi. Class qaysi papkada ekanini uning nomidan aniq bilib olish mumkin β bu loyihada yo'l topishni ulkan darajada osonlashtiradi.
Composer autoload β standartni avtomatlashtirish¶
Yaxshi xabar: PSR-4 autoloaderni qo'lda yozish ham shart emas. Composer (5.4-bobda ko'rgansiz β PHP kutubxonalar menejeri) buni siz uchun avtomatik yasaydi. Sizga faqat composer.json da qaysi prefiks qaysi papkaga mosligini aytish kifoya:
Bu sozlama: "App\ bilan boshlanadigan har qanday class src/ papkada". So'ng terminalda:
Bu buyruq vendor/autoload.php faylini (yoki yangilab) yasaydi β ichida tayyor, optimallashtirilgan PSR-4 autoloader. Endi loyihangizning kirish faylida faqat bitta qator:
<?php
require 'vendor/autoload.php'; // bitta qator β barcha class'lar avtomatik yuklanadi
use App\Models\User;
$u = new User("Ali"); // src/Models/User.php avtomatik yuklandi
composer require bilan tashqi kutubxona o'rnatganingizda ham aynan shu vendor/autoload.php o'sha kutubxona class'larini ham yuklaydi. Ya'ni bitta require β ham sizning, ham tashqi kod uchun. Bu β Laravel, Symfony va deyarli barcha zamonaviy PHP loyihalarining poydevori.
Eslatma:
composer dump-autoloadni faqat yangi class/papka qo'shganda yokicomposer.jsonningautoloadqismini o'zgartirganda qayta ishga tushirasiz. Composer mavjud bo'lganda bu β PSR-4 ni qo'lda yozishdan ko'ra ishonchli va tezroq yo'l.
Real misol: kichik loyiha tuzilishi (App\ + PSR-4 + Composer)¶
Endi hamma narsani birlashtiramiz. Mana kichik "do'kon" loyihasining tuzilishi:
dokon/
βββ composer.json
βββ index.php
βββ src/
βββ Models/
β βββ Product.php
β βββ User.php
βββ Services/
βββ Cart.php
composer.json β autoload sozlamasi:
src/Models/Product.php β App\Models\Product:
<?php
namespace App\Models;
class Product {
public function __construct(
public readonly string $nom,
public readonly float $narx,
) {}
}
src/Models/User.php β App\Models\User:
<?php
namespace App\Models;
class User {
public function __construct(
public readonly string $ism,
) {}
}
src/Services/Cart.php β App\Services\Cart (e'tibor bering: u Product ni use bilan import qiladi):
<?php
namespace App\Services;
use App\Models\Product;
class Cart {
/** @var Product[] */
private array $mahsulotlar = [];
public function qosh(Product $p): void {
$this->mahsulotlar[] = $p;
}
public function jami(): float {
$summa = 0.0;
foreach ($this->mahsulotlar as $p) {
$summa += $p->narx;
}
return $summa;
}
}
index.php β hammasini ulaydi, lekin bironta ham qo'lda require yo'q:
<?php
require 'vendor/autoload.php'; // yagona require β autoload hammasini hal qiladi
use App\Models\User;
use App\Models\Product;
use App\Services\Cart;
$xaridor = new User("Ali");
$savat = new Cart();
$savat->qosh(new Product("Kitob", 50000));
$savat->qosh(new Product("Ruchka", 5000));
echo "Xaridor: {$xaridor->ism}\n";
echo "Jami: " . number_format($savat->jami(), 0, ".", " ") . " so'm\n";
Loyihani ishga tushirish uchun bir marta composer dump-autoload, so'ng php index.php. Natija:
E'tibor bering: index.php da na Product.php, na User.php, na Cart.php qo'lda yuklandi. Har bir class birinchi ishlatilganda PSR-4 autoloader uni o'z papkasidan avtomatik topdi. 3 ta class ham, 300 ta class ham β kirish fayli baribir bitta require 'vendor/autoload.php' bilan cheklanadi. Aynan shu β professional PHP loyihasining tuzilishi.
Bu tuzilish to'g'ridan-to'g'ri 5.4 (Composer) boboga ulanadi: o'sha yerda tashqi kutubxonalarni
composer requirebilan o'rnatishni ko'rasiz β ular ham aynan shuvendor/autoload.phporqali yuklanadi. Namespace + PSR-4 + Composer β uchligi zamonaviy PHP ning poydevori.
Mashqlar¶
Oson
App\Modelsnamespace'idaBookdegan class e'lon qiling (bittanomxususiyati bilan). Uning to'liq malakali nomi qanday bo'ladi?- Boshqa faylda
App\Models\Bookniusebilan import qiling va obyekt yarating. - Namespace ichida PHP'ning
\DateTimeclass'idan obyekt yarating. Nega boshiga\qo'yish kerakligini izohlang. use App\Models\Book;daBookning oldiga\qo'yiladimi? Javobingizni asoslang.
O'rta
- Ikkita turli namespace'dan (
App\Models\UservaAuth\User) bir xil nomli class'ni bitta faylda ishlatishingiz kerak.asbilan to'qnashuvni hal qiling. App\Services\Mailerclass'i ichidaApp\Models\Userniuseqilib, konstruktorda uni parametr sifatida qabul qiling (function __construct(User $u)).spl_autoload_registerbilan oddiy autoloader yozing: class nomini olib, shu nomdagi.phpfaylni joriy papkadanrequireqilsin.- 7-mashqdagi autoloaderni PSR-4 uslubiga o'tkazing: faqat
App\prefiksli class'larnisrc/papkadan yuklasin,\ni/ga almashtirsin.
Qiyin
- PSR-4 ga mos papka tuzilishi tuzing:
App\Models\Orderclassqaysi fayldaturishi kerak?App\Services\Payment\Stripeesa? (Faqat yo'llarni yozing.) - Quyidagi
composer.jsonni to'ldiring:Shop\prefiksiapp/papkaga mos kelsin. Keyin qaysi terminal buyrug'i autoloaderni yangilashini ayting. - Kichik mini-loyiha tuzing:
src/Models/Task.php(App\Models\Task,nomvabajarildixususiyatlari) vasrc/Services/TaskList.php(App\Services\TaskList, vazifa qo'shish va sanash metodlari).index.phpda faqat bittarequire 'vendor/autoload.php'bilan ikkalasini ham ishlating. - Tasavvur qiling, Composer yo'q (uni o'rnatib bo'lmaydi). 11-mashqdagi loyihani qo'lda yozilgan PSR-4 autoloader (
spl_autoload_register) bilan ishga tushiring βvendor/autoload.phpo'rniga o'z autoloader faylingizni ulang.
Yechim β 5 (alias bilan to'qnashuvni hal qilish)
<?php
namespace App\Controllers;
use App\Models\User; // bizning User
use Auth\User as AuthUser; // tashqi User β boshqa nom bilan
class LoginController {
public function ishla(): string {
$foydalanuvchi = new User("Ali"); // App\Models\User
$autentifikator = new AuthUser("token"); // Auth\User
return $foydalanuvchi->ism ?? "?";
}
}
as bo'lmaganda PHP "Cannot use Auth\User as User because the name is already in use" deb xato berardi. as AuthUser ikkinchi class'ga shu faylda boshqa nom berdi β endi ikkalasi tinch yashaydi.
Yechim β 7 (oddiy spl_autoload_register)
<?php
spl_autoload_register(function (string $class): void {
$fayl = __DIR__ . "/" . $class . ".php";
if (file_exists($fayl)) {
require $fayl;
}
});
// Endi qo'lda require'siz:
$obj = new Hisoblagich(); // Hisoblagich.php avtomatik yuklanadi
PHP Hisoblagich ni topa olmaganda, ro'yxatdagi funksiyani chaqiradi, u esa Hisoblagich.php ni topib require qiladi. Namespace ishlatilsa, $class ichida \ bo'ladi β buni keyingi mashqda hal qilamiz.
Yechim β 8 (PSR-4 autoloader)
<?php
spl_autoload_register(function (string $class): void {
$prefiks = "App\\";
$asosiyDir = __DIR__ . "/src/";
if (!str_starts_with($class, $prefiks)) {
return; // bizning prefiksimiz emas β boshqa autoloaderga qoldiramiz
}
$nisbiy = substr($class, strlen($prefiks)); // "Models\User"
$fayl = $asosiyDir . str_replace("\\", "/", $nisbiy) . ".php";
if (file_exists($fayl)) {
require $fayl;
}
});
$u = new App\Models\User("Ali"); // src/Models/User.php avtomatik yuklanadi
Uchta qadam: (1) prefiksni tekshir, (2) prefiksdan keyingi qismni substr bilan ol, (3) \ ni / ga almashtirib fayl yo'lini yasab require qil. Composer ham aynan shu mantiqni ishlatadi β faqat optimallashtirilgan.
Yechim β 9 (PSR-4 yo'llari)
App\ prefiksi src/ ga mos deb hisoblasak:
App\Models\Orderβsrc/Models/Order.phpApp\Services\Payment\Stripeβsrc/Services/Payment/Stripe.php
Qoida: prefiksdan keyingi har bir \ β papka, oxirgi bo'lak β fayl nomi (.php bilan). Namespace tuzilishini ko'rib, class qaysi faylda ekanini darrov bilib olasiz.
Yechim β 10 (composer.json)
JSON ichida \ ni ikki marta yozish kerak (Shop\\), chunki JSON'da \ β maxsus belgi. So'ng autoloaderni yangilash buyrug'i:
Bu vendor/autoload.php ni Shop\ β app/ mosligi bilan qayta yasaydi.
Yechim β 11 (Composer bilan mini-loyiha)
composer.json:
src/Models/Task.php:
<?php
namespace App\Models;
class Task {
public function __construct(
public string $nom,
public bool $bajarildi = false,
) {}
}
src/Services/TaskList.php:
<?php
namespace App\Services;
use App\Models\Task;
class TaskList {
/** @var Task[] */
private array $vazifalar = [];
public function qosh(Task $t): void {
$this->vazifalar[] = $t;
}
public function soni(): int {
return count($this->vazifalar);
}
}
index.php:
<?php
require 'vendor/autoload.php';
use App\Models\Task;
use App\Services\TaskList;
$royxat = new TaskList();
$royxat->qosh(new Task("PHP o'rganish"));
$royxat->qosh(new Task("Mashq qilish", bajarildi: true));
echo "Vazifalar soni: " . $royxat->soni(); // Vazifalar soni: 2
Ishga tushirish: composer dump-autoload, so'ng php index.php. Bironta qo'lda require yo'q β PSR-4 autoloader ikkala class'ni ham o'z papkasidan topadi.
Yechim β 12 (Composer'siz, qo'lda PSR-4 autoloader)
Composer bo'lmasa, vendor/autoload.php o'rniga o'z autoloaderimizni yozamiz. Class fayllari (Task, TaskList) 11-mashqdagidek qoladi β faqat index.php o'zgaradi:
autoload.php (o'zimizning):
<?php
spl_autoload_register(function (string $class): void {
$prefiks = "App\\";
$asosiyDir = __DIR__ . "/src/";
if (!str_starts_with($class, $prefiks)) {
return;
}
$fayl = $asosiyDir
. str_replace("\\", "/", substr($class, strlen($prefiks)))
. ".php";
if (file_exists($fayl)) {
require $fayl;
}
});
index.php:
<?php
require 'autoload.php'; // Composer o'rniga o'z autoloaderimiz
use App\Models\Task;
use App\Services\TaskList;
$royxat = new TaskList();
$royxat->qosh(new Task("Composer'siz ishladi"));
echo "Soni: " . $royxat->soni(); // Soni: 1
Natija aynan bir xil. Bu shuni ko'rsatadi: Composer'ning vendor/autoload.php β sehr emas, balki aynan shunday PSR-4 autoloaderning optimallashtirilgan ko'rinishi. Composer bor joyda uni ishlatasiz; bo'lmaganda β bu 15 qatorlik funksiya ham yetarli.