14-bo‘lim

Nomlar fazosi va avtoyuklash

namespace, use, PSR-4 avtoyuklash, Composer asoslari va loyihani fayllarga to'g'ri taqsimlash.

🕑 12 daqiqa o‘qish 📄 754 so‘z 👁 1 marta ko‘rilgan
Ushbu bo‘lim mundarijasi
  1. Nom to'qnashuvi muammosi
  2. use bilan import
  3. Avtoyuklagichni qo'lda yozish
  4. Loyiha tuzilmasi
  5. Xulosa

Loyiha o'sganda ikki muammo paydo bo'ladi: sinf nomlari to'qnashadi va har bir faylni qo'lda require qilish imkonsiz bo'lib qoladi.

Ikkalasini ham nomlar fazosi va avtoyuklash hal qiladi.

Nom to'qnashuvi muammosi #

Ikkita kutubxonada bir xil nomli sinf bo'lsa nima bo'ladi? Nomlar fazosisiz - to'qnashuv va xato.

Nomlar fazosi bilan esa ular bemalol birga yashaydi. Quyida uch alohida fayl:

PHP
<?php
// src/Jurnal/Yozuvchi.php
declare(strict_types=1);

namespace Ilova\Jurnal;

final class Yozuvchi
{
    public function yoz(string $x): string
    {
        return "[fayl jurnali] {$x}";
    }
}
PHP
<?php
// src/Hisobot/Yozuvchi.php     <- AYNAN SHU NOM, boshqa fazo
declare(strict_types=1);

namespace Ilova\Hisobot;

final class Yozuvchi
{
    public function yoz(string $x): string
    {
        return "[hisobot] {$x}";
    }
}
PHP
<?php
// public/index.php
declare(strict_types=1);

require __DIR__ . "/../vendor/autoload.php";

$a = new \Ilova\Jurnal\Yozuvchi();
$b = new \Ilova\Hisobot\Yozuvchi();

echo $a->yoz("server ishga tushdi"), "\n";   // [fayl jurnali] server ishga tushdi
echo $b->yoz("oylik savdo"), "\n";           // [hisobot] oylik savdo

Ikkala sinf ham Yozuvchi deb ataladi, lekin ularning to'liq nomi har xil:

Qisqa nomTo'liq nom
YozuvchiIlova\Jurnal\Yozuvchi
YozuvchiIlova\Hisobot\Yozuvchi

PHP uchun sinfning haqiqiy identifikatori - to'liq nom. Qisqa nom shunchaki qulaylik.

Har bir faylda bitta nomlar fazosi

namespace birinchi bajariluvchi qator bo'lishi kerak - undan oldin faqat declare(strict_types=1); kelishi mumkin.

PHP
<?php
declare(strict_types=1);

namespace Ilova\Jurnal;

final class Yozuvchi { ... }

Texnik jihatdan figurali qavsli shakl ham bor (namespace X { ... }) va u bitta faylga bir necha fazoni sig'dirishga imkon beradi. Lekin u amalda deyarli ishlatilmaydi - avtoyuklash bilan mos kelmaydi.

Qoida: bitta fayl - bitta sinf - bitta nomlar fazosi. Aynan shu qoida keyingi bo'limdagi avtoyuklashni mumkin qiladi.

$obyekt::class sintaksisi (PHP 8) obyektning to'liq sinf nomini beradi. PHP 8 gacha get_class($obyekt) yozilardi.

use bilan import #

To'liq nomni har safar yozish charchatadi. use uni fayl boshida bir marta e'lon qilish imkonini beradi:

PHP
<?php
// src/Xizmat/BuyurtmaXizmati.php
declare(strict_types=1);

namespace Ilova\Xizmat;

use Ilova\Model\Foydalanuvchi;
use Ilova\Model\Buyurtma as Zakaz;    // taxallus
use DateTimeImmutable;                 // ichki sinfni ham import qilamiz

final class BuyurtmaXizmati
{
    public function yarat(Foydalanuvchi $f, int $id): string
    {
        $b = new Zakaz($id);
        $vaqt = new DateTimeImmutable("2024-09-01 10:00:00");

        return sprintf("#%d - %s (%s)", $b->id, $f->ism, $vaqt->format("d.m.Y"));
    }
}

PHP nomni qanday hal qilishini quyidagi kod modellashtiradi:

PHP
// PHP nomni hal qilish qoidalari (soddalashtirilgan)
function nomniHal(string $yozilgan, string $joriyFazo, array $importlar): string
{
    // 1. \ bilan boshlansa - bu MUTLAQ nom, o'zgartirilmaydi
    if (str_starts_with($yozilgan, "\\")) {
        return ltrim($yozilgan, "\\") . "   (mutlaq)";
    }

    // 2. Birinchi bo'lak import qilinganmi?
    $boshi = explode("\\", $yozilgan)[0];
    if (isset($importlar[$boshi])) {
        $qolgan = substr($yozilgan, strlen($boshi));
        return $importlar[$boshi] . $qolgan . "   (use orqali)";
    }

    // 3. Aks holda - joriy fazoga qo'shiladi
    return $joriyFazo . "\\" . $yozilgan . "   (joriy fazodan)";
}

$joriyFazo = "Ilova\\Xizmat";
$importlar = [
    "Foydalanuvchi" => "Ilova\\Model\\Foydalanuvchi",
    "Zakaz"         => "Ilova\\Model\\Buyurtma",
];

$yozuvlar = [
    "Foydalanuvchi",
    "Zakaz",
    "Yordamchi",
    "Ichki\\Sinf",
    "\\DateTimeImmutable",
];

printf("%-22s %s\n", "KODDA YOZILGAN", "PHP QANDAY TUSHUNADI");
echo str_repeat("-", 62) . "\n";

foreach ($yozuvlar as $y) {
    printf("%-22s %s\n", $y, nomniHal($y, $joriyFazo, $importlar));
}
Natija
KODDA YOZILGAN         PHP QANDAY TUSHUNADI
--------------------------------------------------------------
Foydalanuvchi          Ilova\Model\Foydalanuvchi   (use orqali)
Zakaz                  Ilova\Model\Buyurtma   (use orqali)
Yordamchi              Ilova\Xizmat\Yordamchi   (joriy fazodan)
Ichki\Sinf             Ilova\Xizmat\Ichki\Sinf   (joriy fazodan)
\DateTimeImmutable     DateTimeImmutable   (mutlaq)
Nomlar fazosi ichida ichki funksiyalarga \ kerak

Nomlar fazosi ichida strlen("abc") yozsangiz, PHP avval Ilova\Xizmat\strlen ni qidiradi, topmasa globalga tushadi.

Sinflar uchun esa bunday emas: new DateTime() nomlar fazosi ichida Ilova\Xizmat\DateTime degani va u topilmaydi.

NimaGlobal ga tushadimi
Funksiya (strlen)Ha
Doimiy (PHP_EOL)Ha
Sinf (DateTime)Yo'q - xato beradi

Shuning uchun ikki yechim bor:

PHP
$d = new \DateTimeImmutable();     // 1. oldiga \ qo'yish
PHP
use DateTimeImmutable;             // 2. faylning boshida import
$d = new DateTimeImmutable();

Ikkinchisi afzal - u fayl boshida barcha bog'liqliklarni ko'rsatadi.

Funksiyalar uchun \ qo'yish tezlikni biroz oshiradi (qidiruv bosqichi tushib qoladi), lekin bu mikro-optimallashtirish - kod o'qishligi muhimroq.

PSR-4: nomlar fazosi fayl yo'liga mos keladi KODDA namespace App\Model; final class Foydalanuvchi {} App\Model\Foydalanuvchi Prefiks: App\ Qolgani: Model\Foydalanuvchi FAYL TIZIMIDA "autoload": { "psr-4": {"App\\": "src/"} } src/Model/Foydalanuvchi.php App\ -> src/ ga almashadi, qolgani yo'l bo'ladi Katta-kichik harf muhim Windows da foydalanuvchi.php ishlaydi, Linux serverda esa YO'Q. Fayl nomi sinf nomi bilan aynan bir xil bo'lsin. Avtoyuklash nima beradi Sinf birinchi marta ishlatilganda PHP uni o'zi topib yuklaydi. Ishlatilmagan sinflar esa umuman yuklanmaydi - tezlik va tartib.
Nom va yo'l orasidagi qat'iy moslik - avtoyuklashning butun siri

Avtoyuklagichni qo'lda yozish #

PHP
// Composer nima qilishini tushunish uchun oddiy PSR-4 avtoyuklagich
$xaritalash = [
    "App\\"  => "/loyiha/src/",
    "Test\\" => "/loyiha/tests/",
];

function yolniHisobla(string $sinf, array $xaritalash): ?string
{
    foreach ($xaritalash as $prefiks => $katalog) {
        if (!str_starts_with($sinf, $prefiks)) {
            continue;
        }

        // Prefiksni katalog bilan almashtiramiz
        $qolgan = substr($sinf, strlen($prefiks));
        $yol = $katalog . str_replace("\\", "/", $qolgan) . ".php";

        return $yol;
    }
    return null;
}

$sinflar = [
    "App\\Model\\Foydalanuvchi",
    "App\\Xizmat\\Tolov\\Karta",
    "Test\\Model\\FoydalanuvchiTest",
    "Boshqa\\Kutubxona\\Sinf",
];

printf("%-34s %s\n", "SINF", "FAYL YO'LI");
echo str_repeat("-", 74) . "\n";

foreach ($sinflar as $s) {
    $yol = yolniHisobla($s, $xaritalash);
    printf("%-34s %s\n", $s, $yol ?? "(xaritalashda yo'q)");
}

echo "\n";
echo "Haqiqiy avtoyuklagich shunday ro'yxatdan o'tkaziladi:\n";
echo "  spl_autoload_register(function (string \$sinf): void {\n";
echo "      \$yol = yolniHisobla(\$sinf, \$xaritalash);\n";
echo "      if (\$yol !== null && is_file(\$yol)) {\n";
echo "          require \$yol;\n";
echo "      }\n";
echo "  });\n";
Natija
SINF                               FAYL YO'LI
--------------------------------------------------------------------------
App\Model\Foydalanuvchi            /loyiha/src/Model/Foydalanuvchi.php
App\Xizmat\Tolov\Karta             /loyiha/src/Xizmat/Tolov/Karta.php
Test\Model\FoydalanuvchiTest       /loyiha/tests/Model/FoydalanuvchiTest.php
Boshqa\Kutubxona\Sinf              (xaritalashda yo'q)

Haqiqiy avtoyuklagich shunday ro'yxatdan o'tkaziladi:
  spl_autoload_register(function (string $sinf): void {
      $yol = yolniHisobla($sinf, $xaritalash);
      if ($yol !== null && is_file($yol)) {
          require $yol;
      }
  });
Composer - amalda hech kim avtoyuklagichni qo'lda yozmaydi

Yuqoridagi kod nima sodir bo'layotganini ko'rsatish uchun. Amalda composer.json yoziladi:

JSON
{
    "autoload": {
        "psr-4": {
            "App\\": "src/"
        }
    }
}

Keyin bir marta:

KOD
composer dump-autoload

Va loyihaning kirish faylida bir qator:

PHP
require __DIR__ . "/vendor/autoload.php";

Shundan keyin src/ ichidagi har qanday sinf avtomatik topiladi.

Composer yana bir ish qiladi - kutubxona o'rnatadi:

BuyruqNima qiladi
composer require monolog/monologKutubxona qo'shadi
composer installcomposer.lock bo'yicha o'rnatadi
composer updateYangi versiyalarga yangilaydi
composer dump-autoload -oAvtoyuklashni tezlashtiradi

composer.lock ni git ga qo'shing - u har bir mashinada aynan bir xil versiyalar o'rnatilishini kafolatlaydi. vendor/ papkasini esa .gitignore ga qo'ying.

Loyiha tuzilmasi #

PHP
$tuzilma = [
    "loyiha/" => [
        "public/"  => ["index.php" => "yagona kirish nuqtasi", ".htaccess" => "yo'naltirish"],
        "src/"     => [
            "Model/"    => ["Foydalanuvchi.php" => "App\\Model\\Foydalanuvchi"],
            "Xizmat/"   => ["TolovXizmati.php" => "App\\Xizmat\\TolovXizmati"],
            "Kontroller/" => ["BoshKontroller.php" => "App\\Kontroller\\BoshKontroller"],
        ],
        "shablon/" => ["bosh.php" => "HTML shabloni"],
        "config/"  => ["sozlama.php" => "sozlamalar"],
        "tests/"   => ["FoydalanuvchiTest.php" => "Test\\FoydalanuvchiTest"],
        "vendor/"  => ["autoload.php" => "Composer yaratadi - git ga QO'SHILMAYDI"],
        "composer.json" => "bog'liqliklar ro'yxati",
        ".env"     => "maxfiy sozlamalar - git ga QO'SHILMAYDI",
    ],
];

function chiz(array $t, int $daraja = 0): void
{
    foreach ($t as $nom => $ichi) {
        $chekinish = str_repeat("    ", $daraja);
        if (is_array($ichi)) {
            echo "{$chekinish}{$nom}\n";
            chiz($ichi, $daraja + 1);
        } else {
            printf("%s%-22s %s\n", $chekinish, $nom, "# {$ichi}");
        }
    }
}

chiz($tuzilma);
Natija
loyiha/
    public/
        index.php              # yagona kirish nuqtasi
        .htaccess              # yo'naltirish
    src/
        Model/
            Foydalanuvchi.php      # App\Model\Foydalanuvchi
        Xizmat/
            TolovXizmati.php       # App\Xizmat\TolovXizmati
        Kontroller/
            BoshKontroller.php     # App\Kontroller\BoshKontroller
    shablon/
        bosh.php               # HTML shabloni
    config/
        sozlama.php            # sozlamalar
    tests/
        FoydalanuvchiTest.php  # Test\FoydalanuvchiTest
    vendor/
        autoload.php           # Composer yaratadi - git ga QO'SHILMAYDI
    composer.json          # bog'liqliklar ro'yxati
    .env                   # maxfiy sozlamalar - git ga QO'SHILMAYDI
Faqat public/ veb orqali ochiq bo'lsin

Bu xavfsizlikning eng muhim tuzilmaviy qaroridir.

Veb-server hujjat ildizi (document root) public/ ga qaratilsin. Shunda src/, config/, .env va vendor/ brauzerdan umuman ochilmaydi.

Noto'g'ri sozlamada nima bo'ladi:

ManzilNima ochiladi
saytim.uz/.envBaza paroli va API kalitlari
saytim.uz/config/sozlama.phpSozlamalar
saytim.uz/composer.jsonIshlatilayotgan kutubxonalar
saytim.uz/.git/configButun git tarixi

Birinchi qator - eng ko'p uchraydigan va eng og'ir zaiflik. .env fayli oddiy matn va PHP uni bajarmaydi - brauzer uni shunchaki ko'rsatadi.

Tekshirish juda oson: saytingizga /.env qo'shib oching. Agar mazmun ko'rinsa - darhol tuzating va barcha parollarni almashtiring.

Apache uchun public/.htaccess, Nginx uchun esa root /loyiha/public; sozlamasi kerak.

Amaliy topshiriq
  1. Ikki xil nomlar fazosida bir xil nomli sinf yarating.
  2. Ularni to'liq nom bilan ishlatib ko'ring.
  3. use bilan import qiling va qisqa nom bilan chaqiring.
  4. as bilan taxallus bering.
  5. Nomlar fazosi ichida new DateTime() yozing - xato chiqdimi?
  6. Oldiga \ qo'shib tuzating.
  7. PSR-4 qoidasiga ko'ra App\Model\Buyurtma qaysi faylda bo'lishini ayting.
  8. spl_autoload_register bilan oddiy avtoyuklagich yozing.
  9. composer.json yarating va dump-autoload ni bajaring.
  10. Saytingizga /.env bilan murojaat qilib, ochilmasligini tekshiring.

Xulosa #

  • Nomlar fazosi bir xil nomli sinflarning birga yashashiga imkon beradi.
  • Qoida: bitta fayl - bitta sinf - bitta nomlar fazosi, namespace birinchi qatorda.
  • use bilan import qiling; as taxallus beradi.
  • Nomlar fazosi ichida funksiyalar globalga tushadi, sinflar esa yo'q.
  • new DateTime() nomlar fazosi ichida ishlamaydi - \DateTime yoki use kerak.
  • PSR-4: nomlar fazosi prefiksi katalogga, qolgan qismi yo'lga aylanadi.
  • Fayl nomi sinf nomi bilan aynan bir xil bo'lsin - Linux katta-kichik harfni ajratadi.
  • Composer avtoyuklashni ham, kutubxonalarni ham boshqaradi.
  • composer.lock ni git ga qo'shing, vendor/ ni esa qo'shmang.
  • Veb-server ildizi public/ ga qaratilsin - aks holda .env brauzerdan ochiladi.

Keyingi bo'limda xatolar va istisnolarni to'g'ri boshqarishni o'rganamiz.

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.