Tarkibga o'tish

4.6 Fayl bilan ishlash va fayl yuklash

⬅️ Oldingi: 4.5 JSON bilan ishlash va oddiy API Β· 🏠 README Β· Keyingi: 5.1 Toza kod prinsiplari ➑️


Har bir jiddiy ilovada fayllar bilan ishlash bor: foydalanuvchi avatar yoki hujjat yuklaydi, dastur xatolarni log faylga yozadi, hisobot CSV ko'rinishida eksport qilinadi, sozlamalar matnli faylda saqlanadi. Ma'lumotlar bazasi (3-qism) ko'p narsani saqlaydi, lekin fayllar hali ham zarur β€” chunki rasm, PDF, video kabi "katta" ma'lumotlarni bazaga emas, diskka saqlaymiz.

Bu bobda ikki katta mavzuni ko'ramiz: birinchisi β€” diskdagi fayllarni o'qish va yozish (log, CSV, sozlama); ikkinchisi va eng muhimi β€” foydalanuvchi brauzer orqali yuborgan faylni xavfsiz qabul qilish (file upload). Fayl yuklash β€” hujum nuqtalaridan biri, shuning uchun unga alohida e'tibor beramiz: yuza qilingan kod butun saytni xavf ostiga qo'yadi.

Fayllarni o'qish va yozish

Faylga oddiy yozish β€” file_put_contents

Eng oson yo'l β€” file_put_contents: faylga butun matnni bir urinishda yozadi. Fayl bo'lmasa β€” yaratadi, bo'lsa β€” ustidan yozadi (eski tarkib o'chadi). Funksiya nechta bayt yozilganini qaytaradi (xato bo'lsa false).

<?php
$satr = file_put_contents("xabar.txt", "Salom, dunyo!");
echo $satr;   // 13  (yozilgan baytlar soni)

Bir necha qatorni yozish uchun matnga \n (yangi qator belgisi) qo'shamiz. Diqqat: \n ishlashi uchun qo'shtirnoq ("...") kerak β€” bittatirnoq ('...') ichida \n shunchaki ikki belgi bo'lib qoladi.

<?php
$matn = "Birinchi qator\nIkkinchi qator\nUchinchi qator\n";
file_put_contents("royxat.txt", $matn);

Faylga qo'shib yozish β€” FILE_APPEND

Log fayl uchun ustidan yozish yaramaydi β€” har safar eski yozuvlar o'chib ketadi. FILE_APPEND bayrog'i (flag) matnni fayl oxiriga qo'shadi:

<?php
file_put_contents("amallar.log", "Foydalanuvchi kirdi\n", FILE_APPEND);
file_put_contents("amallar.log", "Hujjat yuklandi\n", FILE_APPEND);
// Fayl tarkibi:
// Foydalanuvchi kirdi
// Hujjat yuklandi

Fayldan oddiy o'qish β€” file_get_contents

file_get_contents faylning butun tarkibini bitta matn (string) ko'rinishida qaytaradi. Kichik fayllar (sozlama, shablon, kichik JSON) uchun ideal:

<?php
file_put_contents("salom.txt", "Assalomu alaykum");

$tarkib = file_get_contents("salom.txt");
echo $tarkib;   // Assalomu alaykum
echo strlen($tarkib);   // 16  (uzunligini ham bilamiz)

Diqqat β€” katta fayl: file_get_contents butun faylni xotiraga (RAM) yuklaydi. 2 GB lik fayl bilan bu xotirani to'ldirib yuboradi. Katta fayllar uchun pastdagi fgets (qator-qator) usulini ishlating.

Fayl satrlarini massivga olish β€” file

Ko'pincha faylni qatorma-qator ko'rib chiqish kerak. file funksiyasi har bir qatorni massivning bitta elementi qilib qaytaradi:

<?php
file_put_contents("shahar.txt", "Toshkent\nSamarqand\nBuxoro\n");

$qatorlar = file("shahar.txt", FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
// ["Toshkent", "Samarqand", "Buxoro"]

foreach ($qatorlar as $nomer => $shahar) {
    echo ($nomer + 1) . ". " . $shahar . "\n";
}
// 1. Toshkent
// 2. Samarqand
// 3. Buxoro

Ikki foydali bayroq: FILE_IGNORE_NEW_LINES β€” har qator oxiridagi \n ni olib tashlaydi; FILE_SKIP_EMPTY_LINES β€” bo'sh qatorlarni o'tkazib yuboradi. Ularni | (yoki) bilan birlashtiramiz.

Pastki sathda ishlash β€” fopen, fwrite, fread, fclose

file_get_contents va file_put_contents qulay, lekin ular faylni har safar to'liq ochib-yopadi. Agar faylni ochib bir necha amal bajarish (ko'p marta yozish, qism-qism o'qish) kerak bo'lsa, faylni qo'lda ochamiz: fopen faylni ochadi va resurs (handle β€” faylga "ishlov beruvchi") qaytaradi, oxirida fclose bilan yopamiz.

fopen ikkinchi argumenti β€” rejim (mode), faylni nima maqsadda ochayotganimizni bildiradi:

Rejim Ma'nosi
"r" Faqat o'qish. Fayl bo'lishi shart. Kursor boshida.
"w" Yozish. Faylni tozalaydi (yoki yaratadi). Eski tarkib yo'qoladi.
"a" Qo'shib yozish (append). Kursor oxirida; eski tarkib saqlanadi.
"r+" O'qish va yozish. Fayl bo'lishi shart. Kursor boshida.
"x" Yozish, lekin faqat fayl yo'q bo'lsa (bor bo'lsa xato).
<?php
// Yozish uchun ochamiz ("w" β€” tozalaydi)
$fayl = fopen("hisobot.txt", "w");
fwrite($fayl, "Hisobot boshi\n");
fwrite($fayl, "Qator 2\n");
fwrite($fayl, "Qator 3\n");
fclose($fayl);   // har doim yopishni unutmang!

echo file_get_contents("hisobot.txt");
// Hisobot boshi
// Qator 2
// Qator 3

fread esa fayldan belgilangan miqdordagi baytni o'qiydi:

<?php
file_put_contents("data.txt", "0123456789");

$fayl = fopen("data.txt", "r");
$qism = fread($fayl, 5);   // boshidan 5 bayt
fclose($fayl);

echo $qism;   // 01234

Katta faylni qator-qator o'qish β€” fgets + feof

Katta log yoki CSV faylni butunlay xotiraga yuklamasdan, bir qatordan o'qiymiz. fgets keyingi qatorni qaytaradi, feof (end of file) fayl oxiriga yetganimizni bildiradi:

<?php
file_put_contents("katta.log", "qator-1\nqator-2\nqator-3\n");

$fayl = fopen("katta.log", "r");
while (!feof($fayl)) {
    $qator = fgets($fayl);
    if ($qator === false) {
        break;   // o'qishda xato yoki oxir
    }
    echo "O'qildi: " . trim($qator) . "\n";
}
fclose($fayl);
// O'qildi: qator-1
// O'qildi: qator-2
// O'qildi: qator-3

Bu usul xotira-tejamkor: faylda million qator bo'lsa ham, har lahzada xotirada faqat bitta qator turadi. Gigabaytlik fayllarni shunday qayta ishlash mumkin.

CSV faylni o'qish β€” fgetcsv

CSV (Comma-Separated Values β€” vergul bilan ajratilgan qiymatlar) β€” jadval ma'lumotini saqlovchi eng keng tarqalgan format. Excel, Google Sheets va deyarli barcha dasturlar CSV import/eksport qiladi. Bitta qator β€” bitta yozuv, ustunlar vergul (yoki nuqta-vergul) bilan ajratiladi.

CSV ni explode(",", ...) bilan o'qish β€” xato: chunki qiymat ichida vergul yoki qo'shtirnoq bo'lsa, bo'linish buziladi. To'g'ri yo'l β€” fgetcsv: u qoidalarni biladi (tirnoq ichidagi vergulni ajratmaydi):

<?php
// Avval namuna CSV yaratamiz
$csv = "ism,yosh,shahar\nAli,25,Toshkent\nVali,30,Samarqand\n";
file_put_contents("odamlar.csv", $csv);

// Endi o'qiymiz
$fayl = fopen("odamlar.csv", "r");
$sarlavha = fgetcsv($fayl, escape: "");   // birinchi qator β€” ustun nomlari
echo "Ustunlar: " . implode(" | ", $sarlavha) . "\n";

while (($qator = fgetcsv($fayl, escape: "")) !== false) {
    // $qator β€” massiv: [ism, yosh, shahar]
    echo $qator[0] . " β€” " . $qator[1] . " yosh, " . $qator[2] . "\n";
}
fclose($fayl);
// Ustunlar: ism | yosh | shahar
// Ali β€” 25 yosh, Toshkent
// Vali β€” 30 yosh, Samarqand

fgetcsv har qatorni massiv qilib qaytaradi; fayl oxirida false qaytaradi, shuning uchun while shartida !== false ishlatamiz. escape: "" β€” nomli argument (1.x): PHP 8.4'da CSV'ning eskirgan "escape" qoidasini o'chiradi va PHP 9'ga tayyor, standart RFC 4180 xatti-harakatini beradi. Uni yozish β€” yaxshi odat (aks holda PHP 8.4 ogohlantirish chiqaradi).

CSV faylga yozish β€” fputcsv

Aksincha β€” massivlardan CSV yasash uchun fputcsv ishlatamiz. U vergul va tirnoqlarni o'zi to'g'ri qo'yadi (qiymatda vergul bo'lsa, avtomatik tirnoqqa oladi):

<?php
$mahsulotlar = [
    ["nomi", "narxi", "izoh"],
    ["Noutbuk", 7500000, "yangi, korobkada"],
    ["Sichqoncha", 120000, "simsiz"],
];

$fayl = fopen("mahsulotlar.csv", "w");
foreach ($mahsulotlar as $qator) {
    fputcsv($fayl, $qator, escape: "");
}
fclose($fayl);

echo file_get_contents("mahsulotlar.csv");
// nomi,narxi,izoh
// Noutbuk,7500000,"yangi, korobkada"
// Sichqoncha,120000,simsiz

E'tibor bering: "yangi, korobkada" ichida vergul borligi uchun fputcsv uni avtomatik qo'shtirnoqqa oldi. Mana shu β€” qo'lda implode(",", ...) qila olmaydigan narsa.

Fayl va papka mavjudligini tekshirish

Faylga murojaat qilishdan oldin uning borligini tekshirish kerak β€” aks holda xato chiqadi. Bir nechta tekshiruvchi funksiya bor:

<?php
file_put_contents("test.txt", "salom");

var_dump(file_exists("test.txt"));   // true   β€” fayl yoki papka bormi?
var_dump(is_file("test.txt"));       // true   β€” aynan faylmi?
var_dump(is_dir("test.txt"));        // false  β€” papkami?
var_dump(file_exists("yoq.txt"));    // false  β€” yo'q fayl
  • file_exists β€” fayl yoki papka mavjudligini umumiy tekshiradi.
  • is_file β€” aynan oddiy fayl ekanini.
  • is_dir β€” papka (directory) ekanini.

Papka yaratish va ichini ko'rish β€” mkdir, scandir

Yuklangan fayllarni saqlash uchun ko'pincha papka yaratish kerak. mkdir papka yaratadi; uchinchi argument true bo'lsa, bir necha sath papkani birato'la yaratadi (yuklamalar/2026/iyun kabi):

<?php
if (!is_dir("yuklamalar")) {
    mkdir("yuklamalar", 0755, true);   // 0755 β€” ruxsatlar, true β€” rekursiv
}

// Ichiga ikkita fayl qo'yamiz
file_put_contents("yuklamalar/a.txt", "1");
file_put_contents("yuklamalar/b.txt", "2");

// Papka ichidagi fayllar ro'yxati
$fayllar = scandir("yuklamalar");
// scandir har doim "." va ".." ni ham qaytaradi β€” ularni ajratamiz
$haqiqiy = array_diff($fayllar, [".", ".."]);
print_r(array_values($haqiqiy));
// Array ( [0] => a.txt [1] => b.txt )

scandir natijasida . (joriy papka) va .. (yuqori papka) doim bo'ladi β€” ularni array_diff bilan chiqarib tashlaymiz.

<?php
file_put_contents("eski.txt", "ma'lumot");

rename("eski.txt", "yangi.txt");   // nomini o'zgartirdi (yoki ko'chirdi)
var_dump(file_exists("yangi.txt"));   // true

unlink("yangi.txt");   // faylni o'chirdi
var_dump(file_exists("yangi.txt"));   // false

unlink faylni o'chiradi (PHP'da "delete" emas, aynan unlink deyiladi). rename nomini o'zgartiradi β€” bir papkadan boshqasiga ko'chirishda ham ishlatiladi.

Fayl yuklash (file upload)

Endi bobning eng muhim qismiga keldik: foydalanuvchi brauzer orqali yuborgan faylni qabul qilish. Avatar, hujjat, rasm β€” bularning hammasi shu jarayon orqali serverga keladi.

HTML forma β€” multipart/form-data

Oddiy forma faqat matn yuboradi. Fayl yuborish uchun formaga ikkita shart qo'yamiz: method="post" va enctype="multipart/form-data". enctype β€” forma ma'lumotini qanday "o'rab" yuborilishini bildiradi; faylsiz formalar uchun bu shart emas, lekin fayl uchun majburiy. Maydon esa <input type="file"> bo'ladi:

<form method="post" action="yuklash.php" enctype="multipart/form-data">
    Rasm tanlang:
    <input type="file" name="rasm">
    <button type="submit">Yuklash</button>
</form>

Eng ko'p uchraydigan xato: enctype="multipart/form-data" ni unutish. Usiz fayl serverga kelmaydi β€” $_FILES bo'sh bo'ladi va sababini topish qiyin bo'ladi. Fayl yuklash ishlamasa, birinchi navbatda shu satrni tekshiring.

$_FILES strukturasi

Forma yuborilganda, fayl haqidagi ma'lumot $_FILES maxsus massiviga keladi ($_POST matn uchun, $_FILES fayllar uchun). name="rasm" maydoni uchun $_FILES['rasm'] quyidagi kalitlarni saqlaydi:

<?php
// Forma yuborilgandan keyin $_FILES['rasm'] taxminan shunday bo'ladi:
$_FILES['rasm'] = [
    "name"     => "mushuk.jpg",        // foydalanuvchi qurilmasidagi asl nom
    "type"     => "image/jpeg",        // brauzer aytgan MIME tur (ISHONCHSIZ!)
    "tmp_name" => "/tmp/phpA3f9.tmp",  // serverdagi vaqtinchalik manzil
    "error"    => 0,                   // xato kodi (0 = UPLOAD_ERR_OK = muvaffaqiyat)
    "size"     => 24576,               // hajmi (bayt)
];

Eng muhim kalitlar: - tmp_name β€” fayl serverda vaqtinchalik shu yerga saqlanadi. Skript tugashi bilan o'chadi! Shuning uchun uni doimiy joyga ko'chirishimiz shart. - error β€” yuklash holati. 0 (UPLOAD_ERR_OK) bo'lsagina hammasi joyida. - size β€” hajm (bayt). O'lcham cheklash uchun kerak. - name β€” faqat asl nomni bilish uchun. Buni saqlash nomi sifatida ishonib ishlatmang (pastda ko'ramiz). - type β€” brauzer aytadi, foydalanuvchi yolg'on aytishi mumkin. Bunga ishonmang β€” haqiqiy turni finfo bilan tekshiramiz.

Xato kodlari β€” error

$_FILES['rasm']['error'] faqat 0 emas, turli holatlarni bildiruvchi kod qaytaradi. PHP ularga nom (konstanta) bergan. match (2-qism) bilan tushunarli xabarga aylantiramiz:

<?php
function yuklashXatosi(int $kod): string
{
    return match ($kod) {
        UPLOAD_ERR_OK         => "Muvaffaqiyatli yuklandi",
        UPLOAD_ERR_INI_SIZE   => "Fayl server cheklovidan katta (php.ini)",
        UPLOAD_ERR_FORM_SIZE  => "Fayl forma cheklovidan katta",
        UPLOAD_ERR_PARTIAL    => "Fayl qisman yuklandi (ulanish uzildi)",
        UPLOAD_ERR_NO_FILE    => "Hech qanday fayl tanlanmadi",
        UPLOAD_ERR_NO_TMP_DIR => "Vaqtinchalik papka topilmadi",
        UPLOAD_ERR_CANT_WRITE => "Diskka yozib bo'lmadi",
        UPLOAD_ERR_EXTENSION  => "PHP kengaytmasi yuklashni to'xtatdi",
        default               => "Noma'lum xato (kod: $kod)",
    };
}

echo yuklashXatosi(0);   // Muvaffaqiyatli yuklandi
echo "\n";
echo yuklashXatosi(4);   // Hech qanday fayl tanlanmadi
echo "\n";
echo yuklashXatosi(1);   // Fayl server cheklovidan katta (php.ini)

Real kodda doim birinchi navbatda error ni tekshiramiz β€” agar UPLOAD_ERR_OK bo'lmasa, qolgan ishni davom ettirishdan ma'no yo'q.

Faylni saqlash β€” move_uploaded_file

Vaqtinchalik fayl (tmp_name) skript tugashi bilan o'chadi. Uni doimiy saqlash uchun move_uploaded_file ishlatamiz β€” oddiy rename emas. Nega aynan u? Chunki move_uploaded_file qo'shimcha xavfsizlik tekshiruvi qiladi: fayl haqiqatan ham HTTP yuklash orqali kelganini tasdiqlaydi (hujumchi server ichidagi maxfiy faylni ko'rsatib qo'ya olmaydi).

<?php
// Bu kod faqat haqiqiy forma yuborilganda ishlaydi (bu yerda namuna)
$manba = $_FILES['rasm']['tmp_name'];
$maqsad = __DIR__ . "/yuklamalar/rasm.jpg";

if (move_uploaded_file($manba, $maqsad)) {
    echo "Fayl saqlandi: " . $maqsad;
} else {
    echo "Saqlashda xato yuz berdi";
}

__DIR__ β€” joriy fayl turgan papkaning to'liq manzili (sehrli konstanta). Maqsad manzilni shunday aniq belgilash kerak β€” nisbiy yo'lga (yuklamalar/...) ishonish chalkashlikka olib keladi.

Validatsiya va xavfsizlik

Fayl yuklash β€” hujumchilarning sevimli nuqtasi. Tekshiruvsiz forma orqali kimdir virus.php yuklab, uni brauzerda ochib serveringizni egallashi mumkin. Quyidagi har bir tekshiruv β€” majburiy himoya qatlami.

1) O'lchamni cheklash

Cheksiz hajmni qabul qilmang β€” disk to'lib qolishi yoki server yuklanib qolishi mumkin. size ni belgilangan chegara bilan solishtiramiz:

<?php
$maxHajm = 2 * 1024 * 1024;   // 2 MB (bayt hisobida)
$hajm = 1_500_000;            // namuna: $_FILES['rasm']['size'] o'rniga

if ($hajm > $maxHajm) {
    echo "Fayl juda katta! Eng ko'pi 2 MB.";
} else {
    echo "O'lcham joyida (" . round($hajm / 1024) . " KB)";
}
// O'lcham joyida (1465 KB)

PHP'da raqamlarni o'qishli qilish uchun _ ishlatish mumkin: 2_000_000 β€” ikki million. Bu faqat ko'rinish uchun, qiymatga ta'sir qilmaydi.

2) Haqiqiy turini tekshirish β€” finfo (faqat kengaytmaga ishonmang!)

Eng ko'p qilinadigan xato β€” fayl turini kengaytma (.jpg) yoki $_FILES[...]['type'] bo'yicha tekshirish. Ikkalasini ham foydalanuvchi yolg'on ko'rsatishi mumkin: virus.php ni rasm.jpg deb nomlash yoki type ni qo'lda o'zgartirish oson.

To'g'ri yo'l β€” faylning ichidagi haqiqiy mazmunini o'qib turini aniqlash. PHP'da buni finfo (file info) qiladi β€” u faylning birinchi baytlariga ("sehrli raqamlar") qarab haqiqiy MIME turni topadi:

<?php
// Namuna uchun haqiqiy kichik PNG fayl yaratamiz (1x1 piksel)
$pngBaytlar = base64_decode(
    "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg=="
);
file_put_contents("test.png", $pngBaytlar);

// Faylning HAQIQIY turini aniqlaymiz
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$haqiqiyTur = finfo_file($finfo, "test.png");
finfo_close($finfo);

echo $haqiqiyTur;   // image/png

// Ruxsat etilgan turlar ro'yxati bilan solishtiramiz
$ruxsatEtilgan = ["image/jpeg", "image/png", "image/gif", "image/webp"];

if (in_array($haqiqiyTur, $ruxsatEtilgan, true)) {
    echo "\nFayl haqiqatan rasm β€” qabul qilamiz";
} else {
    echo "\nBu rasm emas! Rad etildi.";
}

Bu yondashuv β€” oq ro'yxat (whitelist): faqat aniq ruxsat etilgan turlarni o'tkazamiz, qolganlarini rad etamiz. Qora ro'yxat (".php ni taqiqlash") yomonroq β€” chunki xavfli kengaytmalar ko'p (.php5, .phtml, .phar) va birortasini unutish oson.

3) Xavfsiz noyob fayl nomi

Foydalanuvchi bergan nomni ($_FILES[...]['name']) saqlash nomi sifatida ishlatish xavfli: - Ikki kishi rasm.jpg yuklasa β€” biri ikkinchisini o'chirib yuboradi. - Nomda ../../../etc/passwd kabi yo'l bo'lsa β€” boshqa papkaga yozib yuborishi mumkin (path traversal hujumi). - Nomda .php bo'lsa β€” bajariladigan fayl yaratiladi.

Yechim β€” nomni o'zimiz yasaymiz: tasodifiy noyob nom + faqat ruxsat etilgan kengaytma. uniqid (unique id β€” noyob identifikator) takrorlanmas satr beradi:

<?php
// Haqiqiy turdan kengaytmani o'zimiz tanlaymiz (foydalanuvchi nomidan EMAS)
$turKengaytma = [
    "image/jpeg" => "jpg",
    "image/png"  => "png",
    "image/gif"  => "gif",
    "image/webp" => "webp",
];

$haqiqiyTur = "image/png";   // namuna (finfo natijasi)
$kengaytma = $turKengaytma[$haqiqiyTur];

// Noyob, xavfsiz nom yasaymiz
$xavfsizNom = uniqid("rasm_", true) . "." . $kengaytma;
echo $xavfsizNom;   // masalan: rasm_6651a3b9c4d7e1.23456789.png

Bu nomda foydalanuvchi ma'lumotidan hech narsa qolmaydi β€” demak ../ ham, .php ham bo'lishi mumkin emas. Kengaytmani esa finfo topgan haqiqiy turdan olamiz, faylning asl nomidan emas.

4) Web-root tashqarisiga saqlash

Imkon bo'lsa, yuklangan fayllarni veb-saytning ochiq papkasidan (public/, htdocs/) tashqarida saqlang. Sababi: agar hujumchi nazoratdan o'tib .php fayl yuklasa ham, u brauzerda ochib ishga tushira olmaydi β€” chunki fayl veb orqali ko'rinmaydigan joyda. Foydalanuvchiga ko'rsatish kerak bo'lsa, faylni PHP skript orqali "uzatamiz" (readfile bilan), to'g'ridan-to'g'ri havola bermaymiz.

<?php
// Yomon: web orqali to'g'ridan-to'g'ri ochiladigan papka
//   /var/www/html/uploads/   ← .php yuklansa, ishga tushadi!

// Yaxshi: web-root TASHQARISIDAGI papka
$saqlashPapka = __DIR__ . "/../storage/uploads/";
echo "Fayllar shu yerda saqlanadi: " . $saqlashPapka;
// Bu papka brauzerdan to'g'ridan-to'g'ri ochilmaydi

To'liq misol β€” xavfsiz rasm yuklash

Endi barcha tekshiruvlarni bitta funksiyada birlashtiramiz. Bu β€” real loyihada ishlatsa bo'ladigan namuna: o'lcham, haqiqiy MIME tur, noyob nom va xavfsiz saqlash. Funksiya muvaffaqiyatda saqlangan fayl nomini, xatoda xato matnini qaytaradi:

<?php

/**
 * Yuklangan rasmni tekshirib, xavfsiz saqlaydi.
 * Muvaffaqiyatda: ['ok' => true, 'fayl' => 'nom.jpg']
 * Xatoda:        ['ok' => false, 'xato' => 'sabab']
 */
function rasmYukla(array $fayl, string $saqlashPapka): array
{
    // 1) Yuklash xatosini tekshiramiz
    if ($fayl["error"] !== UPLOAD_ERR_OK) {
        return ["ok" => false, "xato" => "Yuklashda xato (kod: {$fayl['error']})"];
    }

    // 2) O'lcham cheklovi β€” 2 MB
    $maxHajm = 2 * 1024 * 1024;
    if ($fayl["size"] > $maxHajm) {
        return ["ok" => false, "xato" => "Fayl 2 MB dan katta"];
    }

    // 3) HAQIQIY MIME turini aniqlaymiz (kengaytmaga ishonmaymiz!)
    $ruxsat = [
        "image/jpeg" => "jpg",
        "image/png"  => "png",
        "image/gif"  => "gif",
        "image/webp" => "webp",
    ];

    $finfo = finfo_open(FILEINFO_MIME_TYPE);
    $tur = finfo_file($finfo, $fayl["tmp_name"]);
    finfo_close($finfo);

    if (!isset($ruxsat[$tur])) {
        return ["ok" => false, "xato" => "Faqat rasm fayllari (jpg, png, gif, webp)"];
    }

    // 4) Papka mavjudligini ta'minlaymiz
    if (!is_dir($saqlashPapka)) {
        mkdir($saqlashPapka, 0755, true);
    }

    // 5) Noyob xavfsiz nom + to'g'ri kengaytma
    $kengaytma = $ruxsat[$tur];
    $yangiNom = uniqid("img_", true) . "." . $kengaytma;
    $toliqYol = rtrim($saqlashPapka, "/") . "/" . $yangiNom;

    // 6) Vaqtinchalik fayldan doimiy joyga ko'chiramiz
    if (!move_uploaded_file($fayl["tmp_name"], $toliqYol)) {
        return ["ok" => false, "xato" => "Faylni saqlab bo'lmadi"];
    }

    return ["ok" => true, "fayl" => $yangiNom];
}

// Haqiqiy ishlatilishi (forma yuborilganda):
//
// if ($_SERVER["REQUEST_METHOD"] === "POST" && isset($_FILES["rasm"])) {
//     $natija = rasmYukla($_FILES["rasm"], __DIR__ . "/../storage/uploads/");
//     if ($natija["ok"]) {
//         echo "Saqlandi: " . $natija["fayl"];
//     } else {
//         echo "Xato: " . $natija["xato"];
//     }
// }

echo "Funksiya tayyor (real forma bilan ishlaydi)";

Bu funksiyaning kuchi β€” har bir bosqichda to'xtab tekshirishida: xato bo'lsa, darrov tushunarli sabab bilan qaytadi va keyingi xavfli amalni bajarmaydi. Aynan shunday "darvoza"lar yuza kodni professional koddan ajratib turadi.

Bonus β€” oddiy log yozuvchi

Bobni amaliy vosita bilan yakunlaymiz: ilovangizdagi muhim hodisalarni faylga yozuvchi kichik log funksiyasi. Sana-vaqt bilan, FILE_APPEND rejimida β€” xuddi real loyihalardagidek:

<?php
function log_yoz(string $xabar, string $sath = "INFO"): void
{
    $vaqt = date("Y-m-d H:i:s");
    $qator = "[{$vaqt}] {$sath}: {$xabar}" . PHP_EOL;
    file_put_contents("ilova.log", $qator, FILE_APPEND);
}

log_yoz("Server ishga tushdi");
log_yoz("Foydalanuvchi #42 kirdi", "INFO");
log_yoz("Bazaga ulanib bo'lmadi", "ERROR");

echo file_get_contents("ilova.log");
// [2026-06-11 10:30:00] INFO: Server ishga tushdi
// [2026-06-11 10:30:00] INFO: Foydalanuvchi #42 kirdi
// [2026-06-11 10:30:00] ERROR: Bazaga ulanib bo'lmadi

PHP_EOL β€” operatsion tizimga mos yangi qator belgisi (Windows va Linux farq qiladi). date("Y-m-d H:i:s") β€” joriy sana va vaqtni o'qishli formatda beradi (sana-vaqt mavzusi alohida bobda batafsil).

Mashqlar

Oson

  1. file_put_contents bilan ism.txt fayliga o'z ismingizni yozing, keyin file_get_contents bilan o'qib ekranga chiqaring.
  2. Bir matnni uch qatorga (\n bilan) file_put_contents orqali yozing, so'ng file funksiyasi bilan massivga o'qib, qatorlar sonini (count) chiqaring.
  3. fopen ni "w" rejimida ishlatib, faylga fwrite bilan ikki qator yozing va fclose bilan yoping.
  4. file_exists bilan biror fayl bor-yo'qligini tekshiring; bo'lmasa "fayl yo'q" deb chiqaring.
  5. FILE_APPEND bayrog'idan foydalanib, bitta faylga uch marta uch xil qator qo'shing, keyin to'liq o'qib ko'ring.

O'rta

  1. Quyidagi CSV ni faylga yozing va fgetcsv bilan qator-qator o'qib, har bir talabaning ismi va bahosini chiqaring: ism,baho / Ali,90 / Vali,75 / Guli,85.
  2. Bir nechta mahsulot massivini ([nomi, narxi]) fputcsv bilan CSV faylga yozing.
  3. scandir bilan bir papkadagi fayllar ro'yxatini oling va . hamda .. ni chiqarib tashlab, qolganini ko'rsating.
  4. Forma orqali kelgan faylning ($_FILES['hujjat']) error kodini olib, match bilan tushunarli xabarga aylantiruvchi funksiya yozing (UPLOAD_ERR_OK, UPLOAD_ERR_NO_FILE, UPLOAD_ERR_INI_SIZE ni qamrang).
  5. Berilgan hajm (bayt) 2 MB dan katta yoki kichikligini tekshiruvchi funksiya yozing; natijani KB da ham ko'rsating.

Qiyin

  1. finfo yordamida fayl haqiqiy MIME turini aniqlang va u ["image/jpeg", "image/png"] ro'yxatida bor-yo'qligini tekshiring (oq ro'yxat). Test uchun kichik PNG fayl yarating.
  2. Foydalanuvchi bergan fayl nomidan mustaqil ravishda, uniqid va haqiqiy MIME turdan olingan kengaytma bilan xavfsiz noyob nom yasovchi funksiya yozing.
  3. To'liq xavfsiz rasm yuklash funksiyasini yozing: o'lcham (max 1 MB), finfo bilan MIME tekshiruvi, noyob nom va move_uploaded_file. Natijada ['ok' => bool, ...] qaytsin.
  4. Sana-vaqtli log funksiyasi yozing (log_yoz($xabar, $sath)), u har bir yozuvni FILE_APPEND bilan yangi qatorga qo'shsin; keyin uni 4-5 marta chaqirib, log faylni o'qib ko'rsating.
  5. Kichik "sozlama" tizimi yozing: key=value ko'rinishidagi qatorlardan iborat faylni file bilan o'qib, massivga (['kalit' => 'qiymat']) aylantiruvchi funksiya tuzing.
Yechim β€” 6 (CSV o'qish)
<?php
$csv = "ism,baho\nAli,90\nVali,75\nGuli,85\n";
file_put_contents("baholar.csv", $csv);

$fayl = fopen("baholar.csv", "r");
$sarlavha = fgetcsv($fayl, escape: "");   // birinchi qatorni (ustun nomlarini) o'tkazib yuboramiz

while (($qator = fgetcsv($fayl, escape: "")) !== false) {
    echo $qator[0] . ": " . $qator[1] . " ball\n";
}
fclose($fayl);
// Ali: 90 ball
// Vali: 75 ball
// Guli: 85 ball

fgetcsv har qatorni massiv qilib qaytaradi; $qator[0] β€” ism, $qator[1] β€” baho. Fayl oxirida false qaytgani uchun while to'xtaydi.

Yechim β€” 9 (xato kodlari)
<?php
function yuklashHolati(int $kod): string
{
    return match ($kod) {
        UPLOAD_ERR_OK       => "Muvaffaqiyatli yuklandi",
        UPLOAD_ERR_NO_FILE  => "Fayl tanlanmadi",
        UPLOAD_ERR_INI_SIZE => "Fayl ruxsat etilgan hajmdan katta",
        default             => "Boshqa xato (kod: $kod)",
    };
}

echo yuklashHolati(UPLOAD_ERR_OK) . "\n";       // Muvaffaqiyatli yuklandi
echo yuklashHolati(UPLOAD_ERR_NO_FILE) . "\n";  // Fayl tanlanmadi
echo yuklashHolati(99);                         // Boshqa xato (kod: 99)

match β€” switch ning zamonaviy, qisqa varianti (2-qism). Har bir holatni aniq qiymatga moslaydi; mos kelmaganini default ushlaydi.

Yechim β€” 12 (xavfsiz noyob nom)
<?php
function xavfsizNom(string $haqiqiyTur, string $prefiks = "fayl_"): ?string
{
    $turlar = [
        "image/jpeg" => "jpg",
        "image/png"  => "png",
        "image/gif"  => "gif",
        "image/webp" => "webp",
    ];

    // Ruxsat etilmagan tur β€” null qaytaramiz
    if (!isset($turlar[$haqiqiyTur])) {
        return null;
    }

    $kengaytma = $turlar[$haqiqiyTur];
    return uniqid($prefiks, true) . "." . $kengaytma;
}

echo xavfsizNom("image/png") . "\n";    // masalan: fayl_6651...png
echo xavfsizNom("image/jpeg") . "\n";   // masalan: fayl_6651...jpg
var_dump(xavfsizNom("application/x-php"));   // NULL (ruxsat yo'q)

Diqqat: kengaytmani faqat haqiqiy MIME turdan olamiz, foydalanuvchi yuborgan fayl nomidan emas. Shu sabab .php yoki ../ umuman nomga tushmaydi.

Yechim β€” 13 (to'liq xavfsiz yuklash)
<?php
function rasmYukla(array $fayl, string $papka): array
{
    // 1) Yuklash xatosi
    if ($fayl["error"] !== UPLOAD_ERR_OK) {
        return ["ok" => false, "xato" => "Yuklash xatosi: {$fayl['error']}"];
    }

    // 2) O'lcham β€” max 1 MB
    if ($fayl["size"] > 1 * 1024 * 1024) {
        return ["ok" => false, "xato" => "Fayl 1 MB dan katta"];
    }

    // 3) Haqiqiy MIME tur (oq ro'yxat)
    $ruxsat = ["image/jpeg" => "jpg", "image/png" => "png"];
    $finfo = finfo_open(FILEINFO_MIME_TYPE);
    $tur = finfo_file($finfo, $fayl["tmp_name"]);
    finfo_close($finfo);

    if (!isset($ruxsat[$tur])) {
        return ["ok" => false, "xato" => "Faqat JPG yoki PNG"];
    }

    // 4) Papka + noyob nom
    if (!is_dir($papka)) {
        mkdir($papka, 0755, true);
    }
    $nom = uniqid("img_", true) . "." . $ruxsat[$tur];
    $yol = rtrim($papka, "/") . "/" . $nom;

    // 5) Ko'chirish
    if (!move_uploaded_file($fayl["tmp_name"], $yol)) {
        return ["ok" => false, "xato" => "Saqlab bo'lmadi"];
    }

    return ["ok" => true, "fayl" => $nom];
}

Tartib muhim: avval arzon tekshiruvlar (xato kodi, o'lcham), keyin qimmatroq (finfo faylni o'qiydi), eng oxirida ko'chirish. Bironta tekshiruv o'tmasa, keyingi xavfli amal umuman bajarilmaydi.

Yechim β€” 15 (sozlama o'qigich)
<?php
// Namuna sozlama fayl yaratamiz
file_put_contents("sozlama.txt", "sayt=Mening Saytim\ntil=uz\ndebug=true\n");

function sozlamaOqi(string $fayl): array
{
    $natija = [];
    $qatorlar = file($fayl, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);

    foreach ($qatorlar as $qator) {
        // "=" bo'yicha faqat BIRINCHI marta bo'lamiz (qiymatda = bo'lishi mumkin)
        if (!str_contains($qator, "=")) {
            continue;   // noto'g'ri qatorni o'tkazamiz
        }
        [$kalit, $qiymat] = explode("=", $qator, 2);
        $natija[trim($kalit)] = trim($qiymat);
    }

    return $natija;
}

$sozlama = sozlamaOqi("sozlama.txt");
print_r($sozlama);
// Array ( [sayt] => Mening Saytim [til] => uz [debug] => true )

echo $sozlama["til"];   // uz

Muhim nuqta: explode("=", $qator, 2) dagi 2 β€” eng ko'pi 2 bo'lakka bo'l, demak qiymat ichidagi = saqlanadi. Bu .env fayllari ishlatadigan haqiqiy usul.