18 β Fayl formatlari, rasm va bulutli saqlash¶
β¬ οΈ Oldingi: 17 β Fayllar, oqimlar va katta ma'lumot Β· π README Β· Keyingi: 19 β SOLID prinsiplari β‘οΈ
Bu bobda: 17-bobda baytlar, deskriptorlar va oqimlarning asosini qo'ydik. Endi shu asosning ustida real dunyo formatlari bilan ishlaymiz β har kuni biznes ilovasida uchraydigan to'rt narsa: CSV (eksport/import), Excel (
.xlsxhisobot), PDF (invoice/hujjat) va rasm (yuklangan surat, thumbnail). So'ng eng muhimi β bularning hammasini qayerga saqlaymiz: lokal diskmi yoki bulutmi? Buni Flysystem abstraksiyasi bilan hal qilamiz, shunda lokaldan bulutga ko'chish kodga tegmasdan bo'ladi. CSV ni chuqur ko'ramiz:fgetcsv/fputcsv, ajratgich/qochirish belgisi, Excel uchun UTF-8 BOM, va million qatorni RAM'ga olmasdan oqim bilan o'qish/yozish. Excel uchunphpoffice/phpspreadsheet: o'qish/yozish, stil,fromArray, formula β va katta fayl ogohlantirish. PDF uchundompdf/dompdf: HTML β PDF, o'zbekcha shrift muammosi va yechimi. Rasm uchun GD: yuklangan suratni resize/thumbnail, format aylantirish (JPG/PNG/WEBP), EXIF metadata tozalash (maxfiylik). Bulut uchunleague/flysystem: yagona interfeys orqalilocal/S3disklargawrite/read/delete,writeStreambilan katta fayl. Hamma kod haqiqiyphp8.4 + composer paketlar bilan ishga tushirilib tasdiqlangan:.xlsx,
Nega bu bob alohida?¶
17-bobda biz "fayl" deganda baytlar ketma-ketligini β fopen, fread, stream_copy_to_stream β o'rgandik. Lekin amaliyotda fayl hech qachon shunchaki "baytlar" emas. U bir formatga ega: CSV β vergullar bilan ajratilgan jadval, XLSX β ichida XML bo'lgan ZIP arxiv, PDF β murakkab hujjat formati, JPEG β siqilgan piksel matritsasi. Har biriga o'z vositasi kerak.
Va bu yerda ikkita injenerlik qarori bor:
- Qaysi vosita? Yadro (
fgetcsv) yetadimi, yoki composer paketmi? Paketni qachon olib kelish kerak β bu xotira/tezlik kompromislari bilan bog'liq. - Qayerda saqlash? Lokal disk dev uchun yetadi, lekin production'da bir nechta server bo'lsa, yuklangan rasm hamma serverda bo'lishi kerak β demak bulut (object storage). Buni shunday yozish kerakki, lokaldan bulutga o'tish bitta qatorni o'zgartirish bo'lsin.
Quyidagi xarita β bu bobning skeletining birinchi yarmi: qaysi format qaysi vositaga ulanadi.
Ko'prik. Bu bob 17-bobning (fayllar, oqimlar va katta ma'lumot) bevosita davomi β oqim g'oyasini (
fopen/fwrite/fgets) shu yerda haqiqiy formatlarga qo'llaymiz. Foydalanuvchi yuklagan fayl asoslari uchun boshlovchi kitobdagi fayl yuklash bo'limi ga, lazy iteratsiya (generator) uchun esa generatorlar ga tayanamiz.
CSV chuqur: oddiy ko'rinadi, tuzoqlari ko'p¶
CSV (Comma-Separated Values) β eng keng tarqalgan ma'lumot almashish formati. "Vergul bilan ajrat, qator bilan tugat" β bolalar o'yini kabi. Lekin bir nechta nozik holat bor:
- Qiymat ichida vergul bo'lsa-chi? (
Toshkent, Yunusobod) - Qiymat ichida qo'shtirnoq bo'lsa-chi?
- Qiymat ichida yangi qator bo'lsa-chi?
- Excel o'zbekcha harflarni to'g'ri ochsin desangiz-chi?
Aynan shu sabab CSV ni qo'lda explode(',', $line) bilan parslash xato β u yuqoridagilarning hech birini hal qilmaydi. PHP yadrosida buning uchun ikki funksiya bor: fputcsv (yozish) va fgetcsv (o'qish). Ular qochirish (escaping) va o'rashni (quoting) avtomatik bajaradi.
fputcsv / fgetcsv va UTF-8 BOM¶
<?php
declare(strict_types=1);
// === CSV yozish: fputcsv ===
$tmp = tempnam(sys_get_temp_dir(), 'csv_');
$rows = [
['ism', 'shahar', 'izoh'],
['Oqil', 'Toshkent', 'oddiy'],
['Aziza', 'Samarqand', 'vergul, ichida'], // vergul -> qo'shtirnoq bilan o'raladi
['Bek', 'Buxoro', 'qosh"tirnoq'], // qo'shtirnoq -> ikkilantiriladi ("")
];
$fh = fopen($tmp, 'wb');
fwrite($fh, "\xEF\xBB\xBF"); // UTF-8 BOM (Excel o'zbekchani to'g'ri ochishi uchun)
foreach ($rows as $r) {
// PHP 8.4: $escape ni ALBATTA bering (default o'zgaradi -> deprecation)
fputcsv($fh, $r, separator: ',', enclosure: '"', escape: '\\');
}
fclose($fh);
echo "=== Xom fayl ===\n";
echo file_get_contents($tmp);
// === CSV o'qish: fgetcsv ===
echo "\n=== O'qilgan ===\n";
$fh = fopen($tmp, 'rb');
$first = fgets($fh);
if (str_starts_with((string) $first, "\xEF\xBB\xBF")) {
fseek($fh, 3); // BOM 3 bayt -> uni o'tkazib yubor
} else {
rewind($fh);
}
$header = fgetcsv($fh, 0, ',', '"', '\\');
echo 'Sarlavha: ' . implode(' | ', $header) . "\n";
while (($row = fgetcsv($fh, 0, ',', '"', '\\')) !== false) {
echo ' ' . implode(' / ', $row) . "\n";
}
fclose($fh);
unlink($tmp);
Haqiqiy chiqish (php bilan ishlatilgan):
=== Xom fayl ===
ism,shahar,izoh
Oqil,Toshkent,oddiy
Aziza,Samarqand,"vergul, ichida"
Bek,Buxoro,"qosh""tirnoq"
=== O'qilgan ===
Sarlavha: ism | shahar | izoh
Oqil / Toshkent / oddiy
Aziza / Samarqand / vergul, ichida
Bek / Buxoro / qosh"tirnoq
Diqqat qiling, fputcsv o'zi qaror qildi:
vergul, ichidaβ ichida vergul bor β butunlay"..."ga o'radi.qosh"tirnoqβ ichida qo'shtirnoq bor β o'radi va ichki"ni""ga ikkilantirdi (CSV qoidasi).- Oddiy qiymatlarga tegmadi.
O'qishda fgetcsv teskari ishni bajaradi β o'rashni yechib, ikkilangan qo'shtirnoqni qaytarib bitta qiladi. Siz hech narsa qilmaysiz. explode hech qachon bunday qila olmaydi.
PHP 8.4 muhim detali. 8.4 dan boshlab
fputcsv/fgetcsvni$escapeargumentisiz chaqirsangiz deprecation ogohlantirishi chiqadi (kelajakda default''bo'ladi). Shuning uchun bu bobda har joyda beshinchi argumentni β'\\'(yoki ataylab'') β aniq beramiz. Bu kelajakka tayyor kod.
UTF-8 BOM nima uchun? Microsoft Excel CSV ni ochganda, agar fayl boshida \xEF\xBB\xBF (BOM β Byte Order Mark) bo'lmasa, uni Windows-1252 deb taxmin qiladi va o'zbekcha/kirill harflarni buzadi. BOM ni qo'shsangiz β Excel "bu UTF-8" deb tushunadi va to'g'ri ochadi. O'qiyotganda esa o'zingiz BOM ni o'tkazib yuborishingiz kerak (yuqorida fseek($fh, 3)), aks holda birinchi ustun nomi \xEF\xBB\xBFism bo'lib qoladi.
Katta CSV: oqim bilan, RAM'ga olmasdan¶
Endi muammo: 100 000 (yoki 10 million) qatorli CSV ni yaratish yoki o'qish kerak. Sodda yondashuv β hammasini massivga yig'ib, keyin file_put_contents β bu butun ma'lumotni RAM'ga oladi va katta faylda xotira tugaydi. To'g'ri yo'l: har qatorni alohida yozish/o'qish, oqim ochiq turganda.
O'qish tomonini yanada toza qilish uchun generator ishlatamiz β u qatorlarni lazy (talab bo'lganda bittadan) beradi, butun faylni hech qachon RAM'ga olmaydi:
<?php
declare(strict_types=1);
// === Katta CSV ni OQIM bilan yozish ===
$tmp = tempnam(sys_get_temp_dir(), 'big_');
$fh = fopen($tmp, 'wb');
fputcsv($fh, ['id', 'qiymat'], ',', '"', '\\');
for ($i = 1; $i <= 100_000; $i++) {
fputcsv($fh, [$i, $i * 2], ',', '"', '\\'); // xotirada faqat bitta qator
}
fclose($fh);
echo 'Fayl hajmi: ' . round(filesize($tmp) / 1024, 1) . " KB\n";
/**
* Generator: CSV ni qatorma-qator beradi (lazy, RAM'siz).
* @return \Generator<int, array<int, string>>
*/
function csvQatorlar(string $yol): \Generator
{
$fh = fopen($yol, 'rb');
if ($fh === false) {
throw new \RuntimeException("Ochib bo'lmadi: {$yol}");
}
try {
while (($row = fgetcsv($fh, 0, ',', '"', '\\')) !== false) {
yield $row;
}
} finally {
fclose($fh); // generator tugaganda yoki uzilsa ham fayl yopiladi
}
}
// 100k qator ustidan yurib yig'indi β butun fayl RAM'ga OLINMAYDI
$jami = 0;
$sanoq = 0;
foreach (csvQatorlar($tmp) as $i => $row) {
if ($i === 0) {
continue; // sarlavhani o'tkaz
}
$jami += (int) $row[1];
$sanoq++;
}
echo "Qatorlar: {$sanoq}, yigindi: {$jami}\n";
echo 'Peak RAM: ' . round(memory_get_peak_usage(true) / 1048576, 1) . " MB\n";
unlink($tmp);
Haqiqiy chiqish:
E'tibor bering: 1.2 MB fayl, 100 000 qator β peak RAM bor-yo'g'i 2 MB. Agar file() bilan butun faylni massivga o'qiganimizda, RAM o'nlab megabaytga shishar edi. Generator + fgetcsv β bu CSV uchun "cheksiz katta" yondashuv: fayl 1 GB bo'lsa ham xotira o'zgarmaydi. Bu 17-bobdagi oqim falsafasining bevosita amaliy davomi.
finallynega muhim? Generator iste'molchi (foreach) o'rtadabreakqilsa ham,finallybloki ishlaydi vafclosechaqiriladi. Deskriptor "oqib ketmaydi".
Excel (.xlsx): PhpSpreadsheet¶
CSV stilsiz, formulasiz, bir varaqli. Mijoz "chiroyli Excel hisobot" so'rasa β qalin sarlavha, ranglar, formulalar, bir nechta varaq kerak. .xlsx aslida ichida XML fayllar bo'lgan ZIP arxiv (shuning uchun ext-zip kerak β bizda bor). Uni qo'lda yozish azob; standart yechim β phpoffice/phpspreadsheet:
<?php
declare(strict_types=1);
require __DIR__ . '/vendor/autoload.php';
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Style\Fill;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx as XlsxWriter;
use PhpOffice\PhpSpreadsheet\IOFactory;
// === XLSX YOZISH ===
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$sheet->setTitle('Hisobot');
// Sarlavha + ma'lumot bloki: fromArray (eng tez usul)
$sheet->fromArray(['Mahsulot', 'Soni', 'Narx'], null, 'A1');
$sheet->fromArray([
['Olma', 120, 8500],
['Banan', 80, 14000],
['Apelsin', 50, 17000],
], null, 'A2');
// Formula (Excel o'zi hisoblaydi)
$sheet->setCellValue('B5', '=SUM(B2:B4)');
// Oddiy stil: sarlavha qalin + sariq fon
$sheet->getStyle('A1:C1')->getFont()->setBold(true);
$sheet->getStyle('A1:C1')->getFill()
->setFillType(Fill::FILL_SOLID)
->getStartColor()->setRGB('FFE599');
$xlsx = tempnam(sys_get_temp_dir(), 'xls_') . '.xlsx';
(new XlsxWriter($spreadsheet))->save($xlsx);
echo 'XLSX yozildi: ' . round(filesize($xlsx) / 1024, 1) . " KB\n";
// === XLSX O'QISH ===
$loaded = IOFactory::load($xlsx);
$ws = $loaded->getActiveSheet();
echo 'Varaq: ' . $ws->getTitle() . "\n";
echo 'A1 = ' . $ws->getCell('A1')->getValue() . "\n";
echo 'A3 = ' . $ws->getCell('A3')->getValue()
. ', B3 = ' . $ws->getCell('B3')->getValue() . "\n";
echo 'B5 (jami) = ' . $ws->getCell('B5')->getCalculatedValue() . "\n";
$matritsa = $ws->toArray(); // butun varaq -> PHP massiv
echo 'Qatorlar: ' . count($matritsa) . "\n";
unlink($xlsx);
$spreadsheet->disconnectWorksheets(); // xotirani bo'shat
Haqiqiy chiqish (composer require phpoffice/phpspreadsheet 5.8.0 bilan):
Asosiy g'oyalar:
fromArray($data, null, 'A1')β PHP massivni hujayralarga bir martada quyadi. Eng tez yozish usuli; har hujayranisetCellValuebilan alohida yozishdan yaxshiroq.getCell('A3')->getValue()vsgetCalculatedValue()β birinchisi hujayradagi xom qiymatni (formula bo'lsa=SUM(...)matnini), ikkinchisi hisoblangan natijani beradi (250). PhpSpreadsheet formulalarni o'zi hisoblay oladi.disconnectWorksheets()β ishlatib bo'lgach chaqiring; PhpSpreadsheet ichki obyektlari bir-biriga aylanma havola qiladi, bu uni xotiradan bo'shatishga yordam beradi.
Katta fayl ogohlantirish¶
PhpSpreadsheet xotira jihatidan og'ir: har hujayra ortida to'liq obyekt (stil, format, tip) saqlanadi. Taxminan, 100 000 qatorli stilsiz varaq ham yuzlab megabayt RAM yeyishi mumkin. Demak:
| Holat | Tavsiya |
|---|---|
Chiroyli, kichik/o'rta hisobot (.xlsx, stil bilan) |
PhpSpreadsheet β to'g'ri tanlov |
| Juda katta jadval, stil shart emas | CSV ga eksport qiling (RAM'siz oqim, yuqoriga qarang) |
Juda katta .xlsx shart |
openspout/openspout (eski box/spout) β oqim asosida, doimiy past RAM, lekin stil cheklangan |
Ya'ni "har doim PhpSpreadsheet" emas. Excel'da chiroyli ko'rinish kerak bo'lsa β u. Faqat ma'lumot kerak bo'lsa va u katta bo'lsa β CSV. Oraliq: openspout.
PDF: dompdf bilan HTML β hujjat¶
Invoice, sertifikat, hisobot β bularning hammasi PDF. PHP'da PDF yaratishning eng qulay yo'li β HTML/CSS yozib, uni PDF ga aylantirish. Siz allaqachon HTML bilasiz (16-bobdagi Twig), shu bilimni qayta ishlatasiz. Standart kutubxona β dompdf/dompdf:
<?php
declare(strict_types=1);
require __DIR__ . '/vendor/autoload.php';
use Dompdf\Dompdf;
use Dompdf\Options;
// Hisobot ma'lumoti (odatda DB dan keladi)
$invoice = [
'raqam' => 'INV-2026-014',
'mijoz' => 'Oqil Imomnazarov',
'qatorlar' => [
['Xizmat: veb-sayt', 1, 5_000_000],
['Xizmat: qollab-quvvatlash', 3, 500_000],
],
];
$jami = array_sum(array_map(fn($r) => $r[1] * $r[2], $invoice['qatorlar']));
// HTML qatorlarni qur
$tr = '';
foreach ($invoice['qatorlar'] as [$nom, $soni, $narx]) {
$summa = number_format($soni * $narx, 0, '.', ' ');
$tr .= "<tr><td>{$nom}</td><td style='text-align:center'>{$soni}</td>"
. "<td style='text-align:right'>{$summa}</td></tr>";
}
$jamiFmt = number_format($jami, 0, '.', ' ');
// MUHIM: <meta charset="UTF-8"> + DejaVu Sans -> o'zbekcha to'g'ri chiqadi
$html = <<<HTML
<!DOCTYPE html><html lang="uz"><head><meta charset="UTF-8">
<style>
body { font-family: DejaVu Sans, sans-serif; font-size: 12px; color: #1e293b; }
h1 { color: #2563eb; }
table { width: 100%; border-collapse: collapse; margin-top: 12px; }
th, td { border: 1px solid #cbd5e1; padding: 6px 8px; }
th { background: #e0e7ff; text-align: left; }
.jami { font-weight: bold; background: #f1f5f9; }
</style></head><body>
<h1>Hisob-faktura {$invoice['raqam']}</h1>
<p>Mijoz: <strong>{$invoice['mijoz']}</strong></p>
<p>O'zbekcha matn: tushuncha, qo'llanma, ishonch.</p>
<table>
<thead><tr><th>Tavsif</th><th>Soni</th><th>Summa (so'm)</th></tr></thead>
<tbody>{$tr}
<tr class="jami"><td colspan="2">JAMI</td><td style="text-align:right">{$jamiFmt}</td></tr>
</tbody>
</table>
</body></html>
HTML;
$options = new Options();
$options->set('defaultFont', 'DejaVu Sans'); // o'zbek glif qamrovi
$dompdf = new Dompdf($options);
$dompdf->loadHtml($html, 'UTF-8');
$dompdf->setPaper('A4', 'portrait');
$dompdf->render();
$pdf = tempnam(sys_get_temp_dir(), 'pdf_') . '.pdf';
file_put_contents($pdf, $dompdf->output());
echo 'PDF yozildi: ' . round(filesize($pdf) / 1024, 1) . " KB\n";
echo 'Sehrli imzo: ' . file_get_contents($pdf, false, null, 0, 5) . "\n"; // %PDF-
echo "Jami: {$jamiFmt} so'm\n";
unlink($pdf);
Haqiqiy chiqish (composer require dompdf/dompdf 3.1.5 bilan):
%PDF- β bu PDF faylining "sehrli imzosi" (magic bytes); uning borligi haqiqiy PDF yaratilganini tasdiqlaydi.
O'zbekcha/UTF-8 shrift muammosi va yechimi¶
Bu β dompdf bilan eng ko'p uchraydigan tuzoq. Standart PDF shriftlari (Helvetica, Times) faqat Lotin-1 belgilarini biladi. Agar siz font-family bermasangiz yoki Helvetica ishlatsangiz, o'zbekcha o', g' yoki diakritikalar bo'sh kvadrat yoki yo'qolib chiqishi mumkin. Yechim ikki qadam:
- HTML da
<meta charset="UTF-8">valoadHtml($html, 'UTF-8')β kirishni to'g'ri o'qishi uchun. - Unicode qamrovli shrift ishlatish: dompdf ichida DejaVu Sans keladi va u o'zbek lotin (hatto kirill, diakritika) glifllarini qamraydi. Yuqoridagi kabi
font-family: DejaVu Sansqo'ying.
Agar maxsus shrift (masalan brendingiz uchun) kerak bo'lsa, .ttf faylini dompdf'ning fontDir iga ro'yxatdan o'tkazasiz. Lekin oddiy o'zbekcha hisobot uchun DejaVu Sans yetadi β yuqoridagi misol aynan shuni isbotlaydi.
mPDF eslatma.
mpdf/mpdfβ dompdf ga muqobil. Murakkab tipografiya, RTL (o'ngdan-chapga, arab/fors), yaxshiroq CSS qo'llab-quvvatlash kerak bo'lsa mPDF kuchliroq, lekin og'irroq. Oddiy invoice/hisobot uchun dompdf yengilroq va yetarli. Tanlov: dizayn murakkabligiga qarab.
Rasm: GD bilan resize, thumbnail, format aylantirish¶
Foydalanuvchi avatar yoki mahsulot surati yuklaydi β siz uni xom holda saqlamaysiz: u 5 MB, 4000Γ3000 bo'lishi mumkin. Siz uni kichraytirasiz (thumbnail), siqasiz va ko'pincha WEBP ga aylantirasiz (kichikroq hajm). Buning uchun PHP'da ext-gd (bizda bor) yetadi β qo'shimcha composer paket shart emas.
Thumbnail: imagescale va imagecopyresampled¶
<?php
declare(strict_types=1);
// "Yuklangan" rasmni simulyatsiya qilamiz (1200x800)
$asl = imagecreatetruecolor(1200, 800);
imagefill($asl, 0, 0, imagecolorallocate($asl, 37, 99, 235));
imagefilledrectangle($asl, 100, 100, 1100, 700, imagecolorallocate($asl, 255, 255, 255));
$src = tempnam(sys_get_temp_dir(), 'src_') . '.png';
imagepng($asl, $src, 6);
imagedestroy($asl);
// 1) Yuklash
[$kenglik, $balandlik] = getimagesize($src);
$img = imagecreatefrompng($src); // .jpg uchun: imagecreatefromjpeg
echo "O'lcham: {$kenglik}x{$balandlik}\n";
// 2) THUMBNAIL: nisbatni saqlab kenglikni 300 ga
$nisbat = 300 / $kenglik;
$yangiH = (int) round($balandlik * $nisbat);
// 2a) imagescale β sodda va tez (ichida resample qiladi)
$thumbA = imagescale($img, 300, $yangiH, IMG_BICUBIC);
echo 'imagescale: ' . imagesx($thumbA) . 'x' . imagesy($thumbA) . "\n";
// 2b) imagecopyresampled β to'liq nazorat (crop, joylashtirish uchun)
$thumbB = imagecreatetruecolor(300, $yangiH);
imagecopyresampled($thumbB, $img, 0, 0, 0, 0, 300, $yangiH, $kenglik, $balandlik);
echo 'resampled: ' . imagesx($thumbB) . 'x' . imagesy($thumbB) . "\n";
// 3) Saqlash (PNG siqish darajasi 9 = eng kichik)
$out = tempnam(sys_get_temp_dir(), 'th_') . '.png';
imagepng($thumbA, $out, 9);
echo 'Thumbnail fayl: ' . filesize($out) . " bayt\n";
imagedestroy($img);
imagedestroy($thumbA);
imagedestroy($thumbB);
@unlink($src);
@unlink($out);
Haqiqiy chiqish:
Ikki usul:
imagescale($img, $w, $h, IMG_BICUBIC)β eng oddiy. Faqat kichraytirish/kattalashtirish kerak bo'lsa shuni ishlating.IMG_BICUBICβ silliq natija beradi.imagecopyresampled(...)β to'liq nazorat: manba va nishon to'rtburchaklarini alohida belgilaysiz. Bu sizga crop (qirqish), markazga joylashtirish, "cover/contain" mantiqini qo'lda yozish imkonini beradi. Murakkabroq, lekin moslashuvchan.
Format aylantirish va sifat/siqish¶
Bir GD obyektini istalgan formatda saqlash mumkin β siz qaysi image* funksiyasini chaqirsangiz, o'sha format chiqadi:
<?php
declare(strict_types=1);
// $thumb mavjud GD obyekti bo'lsin (yuqoridan).
// Bir obyekt -> uch xil format/sifat:
imagejpeg($thumb, 'rasm.jpg', 75); // JPEG, sifat 0-100 (75 = yaxshi balans)
imagepng($thumb, 'rasm.png', 6); // PNG, siqish 0-9 (yo'qotuvsiz)
imagewebp($thumb, 'rasm.webp', 80); // WEBP, sifat 0-100 (odatda eng kichik hajm)
- JPEG β fotosuratlar uchun; sifat 75-85 odatda ko'z ilg'amas yo'qotish bilan kichik hajm beradi.
- PNG β shaffoflik (alpha) yoki keskin chiziqlar (logo, skrinshot) uchun; yo'qotuvsiz.
- WEBP β zamonaviy format, ko'pincha JPEG dan ham kichikroq. Veb uchun ideal; barcha zamonaviy brauzerlar qo'llab-quvvatlaydi.
Eslatma (GD build).
imagejpeg/imagewebpfaqat GD JPEG/WEBP qo'llab-quvvatlash bilan kompilyatsiya qilingan bo'lsa ishlaydi. Productiondagi odatiy PHP buildida ular bor; tekshirish uchunprint_r(gd_info())chaqiring βJPEG SupportvaWebP Supportqatorlariga qarang. Yuqoridagi thumbnail misoli PNG bilan haqiqatan ishga tushirildi va 300Γ200 thumbnail chiqardi.
EXIF metadata tozalash β maxfiylik va xavfsizlik¶
Telefonda olingan rasmda EXIF metadata bo'ladi: kamera modeli, sana, va ko'pincha GPS koordinatalari β ya'ni surat qayerda olingani. Agar foydalanuvchi yuklagan rasmni shu metadata bilan saqlasangiz, uning uy manzilini oshkor qilib qo'yishingiz mumkin. Bu β jiddiy maxfiylik teshigi.
Yaxshi xabar: GD orqali rasmni qayta saqlasangiz (yuqoridagi resize/convert), GD EXIF ni ko'chirmaydi β natija metadatasiz toza piksel bo'ladi. Ya'ni thumbnail jarayonining o'zi metadatani avtomatik tozalaydi:
<?php
declare(strict_types=1);
// EXIF ni o'qish (faqat ko'rsatish uchun; ext-exif kerak)
// $asl = exif_read_data($yuklangan); // GPS, kamera, sana bo'lishi mumkin
// GD orqali qayta saqlash -> EXIF avtomatik tushib qoladi:
$img = imagecreatefromjpeg($yuklangan); // EXIF GD obyektiga O'TMAYDI
imagejpeg($img, $tozaFayl, 85); // natija: metadatasiz toza rasm
imagedestroy($img);
// Endi $tozaFayl da GPS/kamera ma'lumoti YO'Q.
Qoida: foydalanuvchi yuklagan rasmni doim qayta kodlang (re-encode) β bu ham metadatani tozalaydi, ham potentsial zararli "polyglot" fayllarni (rasm ichiga yashiringan kod) zararsizlantiradi. Hech qachon yuklangan baytni ishonib o'sha holicha tarqatmang.
Imagick qisqa eslatma.
ext-imagick(ImageMagick o'rami) β GD ga kuchli muqobil: ko'proq format (PDF, TIFF, HEIC, SVG), yaxshiroq sifat vastripImage()bilan aniq metadata tozalash. Lekin alohida o'rnatish kerak va og'irroq. Oddiy resize/thumbnail uchun GD yetadi; professional rasm konveyeri (ko'p format, yuqori sifat) uchun Imagick'ga o'ting.
Bulutli saqlash: Flysystem abstraksiyasi¶
Mana bobning yuragi. Yuqorida CSV, XLSX, PDF, rasm yaratdik β lekin ularni qayerga saqlaymiz? Dev mashinangizda /storage/... ga file_put_contents yetadi. Lekin production'da:
- Bir nechta veb-server bor β bir serverga saqlangan rasm boshqasida ko'rinmaydi.
- Disk to'ladi, zaxira (backup) kerak, CDN kerak.
Yechim β object storage (bulut): Amazon S3 yoki S3-mos xizmat (Contabo, MinIO, DigitalOcean Spaces). Lekin agar kodingiz to'g'ridan-to'g'ri S3 SDK ga bog'lansa, dev'da lokal, prod'da S3 ishlatish uchun kodni ikkiga ajaratishingiz kerak bo'ladi. league/flysystem aynan shuni hal qiladi: yagona interfeys, ostida turli adapter lar.
Local disk bilan: write / read / delete / listContents¶
<?php
declare(strict_types=1);
require __DIR__ . '/vendor/autoload.php';
use League\Flysystem\Filesystem;
use League\Flysystem\Local\LocalFilesystemAdapter;
// --- "disk" ni quramiz: bu yerda LOCAL adapter ---
$root = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'fly_' . bin2hex(random_bytes(4));
$adapter = new LocalFilesystemAdapter($root);
$fs = new Filesystem($adapter);
// --- YAGONA interfeys: disk turidan QAT'IY NAZAR bir xil API ---
$fs->write('hisobot/2026.txt', "Salom, Flysystem!\n");
echo 'Bor: ' . ($fs->fileExists('hisobot/2026.txt') ? 'ha' : 'yoq') . "\n";
echo "O'qish: " . trim($fs->read('hisobot/2026.txt')) . "\n";
echo 'Hajm: ' . $fs->fileSize('hisobot/2026.txt') . " bayt\n";
echo 'MIME: ' . $fs->mimeType('hisobot/2026.txt') . "\n";
// listContents β papka ichidagilar
$fs->write('hisobot/q1.txt', 'data');
foreach ($fs->listContents('hisobot', false) as $item) {
echo ' list: ' . $item->path() . ' (' . $item->type() . ")\n";
}
// delete
$fs->delete('hisobot/2026.txt');
echo "O'chgach bor? " . ($fs->fileExists('hisobot/2026.txt') ? 'ha' : 'yoq') . "\n";
$fs->deleteDirectory(''); // tozalash
Haqiqiy chiqish (composer require league/flysystem 3.34.0 bilan):
Bor: ha
O'qish: Salom, Flysystem!
Hajm: 18 bayt
MIME: text/plain
list: hisobot/2026.txt (file)
list: hisobot/q1.txt (file)
O'chgach bor? yoq
Diqqat: yo'llar / bilan (hisobot/2026.txt) β Flysystem operatsion tizim farqini (Windows \) yashiradi. fileExists, read, write, delete, fileSize, mimeType, listContents β bularning hammasi har qanday adapterda bir xil ishlaydi.
Path traversal (xavfsizlik). Saqlash yo'lining bir qismi foydalanuvchidan kelganida (masalan, yuklangan fayl nomi yoki URL segmenti) β
$fs->write("yuklamalar/{$userNomi}", ...)kabi β$userNomiichidagi../segmentlari saqlash papkasidan chiqib ketishga urinishi mumkin (../../../etc/parol), bu esa root tashqarisidagi fayllarni o'qish/yozishga olib keladigan klassik path traversal zaifligi. Yaxshi xabar: Flysystem 3.x yo'lni o'zi normallashtiradi va..bilan rootdan chiqishga urinishni rad etadi βLeague\Flysystem\PathTraversalDetectedistisnosini otadi (Path traversal detected: yuklamalar/../../../etc/parol), demak adapter darajasida himoyalangansiz. Shunday bo'lsa-da, foydalanuvchi nomini ishonib qabul qilmang: uni oqartiring (sanitize) yoki o'zingiz nazorat qiladigan nom (masalanbin2hex(random_bytes(8)) . '.png') bilan almashtiring β bu istisnoga umuman bormaslikning eng toza yo'li.
Katta fayl: putStream / readStream (RAM'siz)¶
CSV/rasm katta bo'lsa, uni butunlay RAM'ga olib write($path, $content) qilish noto'g'ri. Flysystem oqim versiyasini beradi β writeStream va readStream β bu 17-bobdagi oqim falsafasining bulut darajasidagi davomi:
<?php
declare(strict_types=1);
require __DIR__ . '/vendor/autoload.php';
use League\Flysystem\Filesystem;
use League\Flysystem\Local\LocalFilesystemAdapter;
$root = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'fly_' . bin2hex(random_bytes(4));
$fs = new Filesystem(new LocalFilesystemAdapter($root));
// 5 MB li manba fayl yasaymiz
$big = tempnam(sys_get_temp_dir(), 'big_');
$h = fopen($big, 'wb');
for ($i = 0; $i < 50_000; $i++) {
fwrite($h, str_repeat('x', 100) . "\n");
}
fclose($h);
// writeStream: oqim bilan diskka -> butun fayl RAM'ga OLINMAYDI
$stream = fopen($big, 'rb');
$fs->writeStream('arxiv/katta.log', $stream);
if (is_resource($stream)) {
fclose($stream);
}
echo 'Diskka yozildi: ' . $fs->fileSize('arxiv/katta.log') . " bayt\n";
// readStream: o'qish ham oqim bilan
$in = $fs->readStream('arxiv/katta.log');
$satrlar = 0;
while (!feof($in)) {
if (fgets($in) !== false) {
$satrlar++;
}
}
fclose($in);
echo "readStream satrlar: {$satrlar}\n";
echo 'Peak RAM: ' . round(memory_get_peak_usage(true) / 1048576, 1) . " MB\n";
@unlink($big);
$fs->deleteDirectory('');
Haqiqiy chiqish:
5 MB fayl oqim bilan yozildi va o'qildi, peak RAM 12 MB atrofida (asosiy qism β paketning autoload'i). Agar S3 adapteri bo'lsa, writeStream faylni bo'laklab (multipart) yuklaydi β gigabaytli faylni ham RAM'ni shishirtirmasdan bulutga jo'natadi. Sizning kodingiz o'zgarmaydi β faqat adapter S3 bo'ladi.
S3 / object-storage adapter (config-misol)¶
S3-mos object storage'ga (Amazon S3, Contabo Object Storage, MinIO, DigitalOcean Spaces) ulanish uchun faqat adapterni almashtirasiz β yuqoridagi write/read/writeStream kodi bir harf ham o'zgarmaydi. Avval AWS adapterni o'rnatasiz:
<?php
declare(strict_types=1);
use Aws\S3\S3Client;
use League\Flysystem\Filesystem;
use League\Flysystem\AwsS3V3\AwsS3V3Adapter;
// S3-mos object storage klienti (Contabo/MinIO/Spaces uchun ham SHU)
$client = new S3Client([
'version' => 'latest',
'region' => 'eu-central-1',
// S3-MOS xizmat uchun MUHIM: o'z endpoint'ini ko'rsating
'endpoint' => 'https://eu2.contabostorage.com',
'use_path_style_endpoint' => true, // ko'pchilik S3-mos xizmatda kerak
'credentials' => [
'key' => getenv('S3_KEY'), // sirlarni .env / muhitdan oling, kodga yozmang
'secret' => getenv('S3_SECRET'),
],
]);
$adapter = new AwsS3V3Adapter($client, bucket: 'mening-bucket');
$fs = new Filesystem($adapter);
// ...va shu nuqtadan keyin KOD AYNAN local bilan bir xil:
// $fs->write('hisobot/2026.xlsx', $bytes);
// $fs->writeStream('arxiv/katta.log', $stream);
// $fs->read(...), $fs->delete(...), $fs->listContents(...)
Bu blok config-misol β uni ishga tushirish uchun haqiqiy bucket va kalit kerak (shuning uchun yuqorida local adapterni ishga tushirib ko'rsatdik, S3 ni esa konfiguratsiya namunasi sifatida beramiz). E'tibor bering:
endpoint+use_path_style_endpointβ aynan shu ikki sozlama Amazon bo'lmagan, lekin S3-mos xizmatlarga (Contabo, MinIO, DigitalOcean Spaces) ulanishga imkon beradi. Contabo Object Storage S3 API'ni gapiradi, shuning uchun bu adapter unga to'g'ridan-to'g'ri mos keladi.- Kalitlarni hech qachon kodga yozmang β
getenv/.envorqali muhitdan oling.
Nega abstraksiya β asosiy daromad¶
Tasavvur qiling, dev'da local, production'da Contabo S3 ishlatmoqchisiz. Flysystem bilan butun farq β bitta match:
<?php
declare(strict_types=1);
use League\Flysystem\Filesystem;
use League\Flysystem\Local\LocalFilesystemAdapter;
// use League\Flysystem\AwsS3V3\AwsS3V3Adapter;
function diskQur(string $muhit): Filesystem
{
$adapter = match ($muhit) {
'production' => /* new AwsS3V3Adapter($client, 'bucket') */ throw new \RuntimeException('S3 sozlang'),
default => new LocalFilesystemAdapter(__DIR__ . '/storage'),
};
return new Filesystem($adapter);
}
// Butun dastur shunchaki $disk->write(...) qiladi β qaysi disk ekanini bilmaydi.
Biznes kodingiz (UserService, InvoiceController, ...) hech qachon "S3" yoki "local" so'zini ko'rmaydi β u faqat Filesystem interfeysiga gapiradi. Lokaldan bulutga ko'chish = match ning bitta tarmog'ini yoqish. Bu β Dependency Inversion prinsipining (kod abstraksiyaga bog'lansin, konkret realizatsiyaga emas) sof amaliy ko'rinishi; xuddi 13-bobdagi DI konteyner g'oyasi kabi, lekin saqlash qatlamida.
Hammasini birlashtirish: hisobotni yaratib bulutga oqim bilan¶
Endi bobning bo'laklarini bir konveyerga ulaymiz: XLSX hisobot yaratish β uni Flysystem disk ga oqim bilan saqlash. Bu real ilovadagi tipik vazifa (masalan, oylik hisobotni generatsiya qilib bulutga qo'yish):
<?php
declare(strict_types=1);
require __DIR__ . '/vendor/autoload.php';
use League\Flysystem\Filesystem;
use League\Flysystem\Local\LocalFilesystemAdapter;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx as XlsxWriter;
$disk = new Filesystem(new LocalFilesystemAdapter(
sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'integ_' . bin2hex(random_bytes(4))
));
// 1) Hisobotni yarat
$ss = new Spreadsheet();
$ss->getActiveSheet()->fromArray([
['Oy', 'Daromad'], ['Yanvar', 1200], ['Fevral', 1500],
]);
// 2) Vaqtinchalik faylga yoz (PhpSpreadsheet writer fayl/oqim talab qiladi)
$tmp = tempnam(sys_get_temp_dir(), 'xls_') . '.xlsx';
(new XlsxWriter($ss))->save($tmp);
$ss->disconnectWorksheets();
// 3) Oqim bilan disk ga ko'chir (katta hisobotda ham RAM shishmaydi)
$stream = fopen($tmp, 'rb');
$disk->writeStream('hisobotlar/daromad-2026.xlsx', $stream);
if (is_resource($stream)) {
fclose($stream);
}
@unlink($tmp);
echo 'Diskda: ' . ($disk->fileExists('hisobotlar/daromad-2026.xlsx') ? 'ha' : 'yoq') . "\n";
echo 'Hajm: ' . $disk->fileSize('hisobotlar/daromad-2026.xlsx') . " bayt\n";
$disk->deleteDirectory('');
Haqiqiy chiqish:
Bu yerda butun bob jamlangan: format kutubxonasi (PhpSpreadsheet) faylni yaratdi, saqlash abstraksiyasi (Flysystem) uni diskka oqim bilan qo'ydi. Agar $disk ni S3 adapteri bilan qursak β shu kodning o'zi hisobotni bulutga yuklaydi. Ana shu β abstraksiyaning kuchi.
Mashqlar¶
Oson¶
- TSV eksport.
fputcsvdan foydalanib, massiv ma'lumotni vergul emas, tab (\t) bilan ajratilgan faylga yozing va qaytadan o'qing. Faylga UTF-8 BOM qo'shing. - GD o'lcham. Berilgan PNG faylning kengligi 500 px dan katta bo'lsa, uni nisbatini saqlab 500 px ga kichraytiring; aks holda o'zgartirmang.
imagescaleishlating. - Flysystem nusxa. Bitta local Flysystem disk ichida
manba/a.txtfaylini yozing, so'ng uninusxa/a.txtga ko'chiring (copymetodi) va ikkalasi ham borligini tasdiqlang.
O'rta¶
- CSV β massiv (sarlavha kalit). CSV faylni o'qib, birinchi qatorni kalit sifatida ishlatib, har qatorni assotsiativ massivga aylantiruvchi generator yozing (
['ism' => 'Oqil', 'shahar' => 'Toshkent']). RAM'siz (generator) bo'lsin. - XLSX'dan CSV. PhpSpreadsheet bilan
.xlsxni o'qib, uning birinchi varag'ini CSV ga (BOM bilan) eksport qiling.
Qiyin¶
- Markazga crop (cover) thumbnail.
imagecopyresampledbilan istalgan rasmdan kvadrat 200Γ200 thumbnail yasang: rasm to'rtburchak bo'lsa, markazidan kvadrat qirqib oling (cover rejimi β bo'sh joy qolmasin, cho'zilmasin).
Yechim β 1
<?php
declare(strict_types=1);
$tmp = tempnam(sys_get_temp_dir(), 'tsv_');
$fh = fopen($tmp, 'wb');
fwrite($fh, "\xEF\xBB\xBF"); // UTF-8 BOM
$rows = [['ism', 'shahar'], ['Oqil', 'Toshkent'], ['Aziza', 'Samarqand']];
foreach ($rows as $r) {
fputcsv($fh, $r, "\t", '"', '\\'); // ajratgich = tab
}
fclose($fh);
$fh = fopen($tmp, 'rb');
fseek($fh, 3); // BOM ni o'tkaz
while (($row = fgetcsv($fh, 0, "\t", '"', '\\')) !== false) {
echo implode(' | ', $row) . "\n";
}
fclose($fh);
unlink($tmp);
fputcsv/fgetcsv ajratgich argumentini "\t" qilsangiz β bu TSV. Hammasi (BOM, escaping) bir xil ishlaydi.
Yechim β 2
<?php
declare(strict_types=1);
function pngKichraytir(string $yol, int $maxKenglik = 500): \GdImage
{
[$w, $h] = getimagesize($yol);
$img = imagecreatefrompng($yol);
if ($w <= $maxKenglik) {
return $img; // kerak emas
}
$nisbat = $maxKenglik / $w;
$yangiH = (int) round($h * $nisbat);
$kichik = imagescale($img, $maxKenglik, $yangiH, IMG_BICUBIC);
imagedestroy($img);
return $kichik;
}
// Sinov
$src = tempnam(sys_get_temp_dir(), 's_') . '.png';
imagepng(imagecreatetruecolor(800, 600), $src);
$out = pngKichraytir($src);
echo imagesx($out) . 'x' . imagesy($out) . "\n"; // 500x375
imagedestroy($out);
unlink($src);
$w <= $maxKenglik shartini birinchi tekshirib, keraksiz qayta ishlovni oldini olamiz β kichik rasm cho'zilib sifatini yo'qotmaydi.
Yechim β 3
<?php
declare(strict_types=1);
require __DIR__ . '/vendor/autoload.php';
use League\Flysystem\Filesystem;
use League\Flysystem\Local\LocalFilesystemAdapter;
$fs = new Filesystem(new LocalFilesystemAdapter(
sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'cp_' . bin2hex(random_bytes(4))
));
$fs->write('manba/a.txt', 'salom');
$fs->copy('manba/a.txt', 'nusxa/a.txt'); // disk ichida nusxalash
echo 'Manba bor: ' . ($fs->fileExists('manba/a.txt') ? 'ha' : 'yoq') . "\n";
echo 'Nusxa bor: ' . ($fs->fileExists('nusxa/a.txt') ? 'ha' : 'yoq') . "\n";
echo "Nusxa mazmuni: " . $fs->read('nusxa/a.txt') . "\n";
$fs->deleteDirectory('');
copy() (va move()) β Flysystem metodi; S3 da ham xuddi shunday ishlaydi. Chiqish: ikkalasi ham ha, mazmun salom.
Yechim β 4
<?php
declare(strict_types=1);
/**
* CSV ni assotsiativ massivlar oqimi sifatida beradi (RAM'siz).
* @return \Generator<int, array<string, string>>
*/
function csvAssoc(string $yol): \Generator
{
$fh = fopen($yol, 'rb');
if ($fh === false) {
throw new \RuntimeException("Ochilmadi: {$yol}");
}
try {
$first = fgets($fh);
if (str_starts_with((string) $first, "\xEF\xBB\xBF")) {
fseek($fh, 3);
} else {
rewind($fh);
}
$kalitlar = fgetcsv($fh, 0, ',', '"', '\\'); // birinchi qator = kalitlar
if ($kalitlar === false) {
return;
}
while (($row = fgetcsv($fh, 0, ',', '"', '\\')) !== false) {
yield array_combine($kalitlar, $row); // kalit + qiymat -> assoc
}
} finally {
fclose($fh);
}
}
// Sinov
$tmp = tempnam(sys_get_temp_dir(), 'c_');
file_put_contents($tmp, "ism,shahar\nOqil,Toshkent\nAziza,Samarqand\n");
foreach (csvAssoc($tmp) as $qator) {
echo $qator['ism'] . ' -> ' . $qator['shahar'] . "\n";
}
unlink($tmp);
Kalit β array_combine($kalitlar, $row); bu sarlavha nomlari va qator qiymatlarini juftlaydi. Generator bo'lgani uchun million qatorli CSV da ham RAM o'zgarmaydi.
Yechim β 5
<?php
declare(strict_types=1);
require __DIR__ . '/vendor/autoload.php';
use PhpOffice\PhpSpreadsheet\IOFactory;
function xlsxToCsv(string $xlsx, string $csv): void
{
$sheet = IOFactory::load($xlsx)->getActiveSheet();
$fh = fopen($csv, 'wb');
fwrite($fh, "\xEF\xBB\xBF"); // Excel uchun BOM
foreach ($sheet->toArray() as $row) { // varaq -> qatorlar
fputcsv($fh, $row, ',', '"', '\\');
}
fclose($fh);
}
// Sinov: avval kichik xlsx yasaymiz
$ss = new \PhpOffice\PhpSpreadsheet\Spreadsheet();
$ss->getActiveSheet()->fromArray([['Oy', 'Soni'], ['Yan', 10], ['Fev', 20]]);
$x = tempnam(sys_get_temp_dir(), 'x_') . '.xlsx';
(new \PhpOffice\PhpSpreadsheet\Writer\Xlsx($ss))->save($x);
$ss->disconnectWorksheets();
$c = tempnam(sys_get_temp_dir(), 'c_') . '.csv';
xlsxToCsv($x, $c);
echo file_get_contents($c); // BOM + 3 qator CSV
unlink($x);
unlink($c);
toArray() butun varaqni PHP massivga beradi, keyin har qatorni fputcsv bilan CSV ga yozamiz. Katta xlsx uchun toArray o'rniga getRowIterator bilan qatorma-qator yurish RAM'ni tejaydi.
Yechim β 6
<?php
declare(strict_types=1);
/** Istalgan rasmdan markazdan qirqilgan kvadrat thumbnail (cover rejimi). */
function kvadratThumb(\GdImage $src, int $tomon = 200): \GdImage
{
$w = imagesx($src);
$h = imagesy($src);
$qisqa = min($w, $h); // kvadrat tomoni = qisqa o'lcham
$srcX = (int) (($w - $qisqa) / 2); // markazga joylash
$srcY = (int) (($h - $qisqa) / 2);
$thumb = imagecreatetruecolor($tomon, $tomon);
imagecopyresampled(
$thumb, $src,
0, 0, // nishon x,y
$srcX, $srcY, // manba x,y (markaz)
$tomon, $tomon, // nishon o'lcham (kvadrat)
$qisqa, $qisqa // manba o'lcham (kvadrat qirqim)
);
return $thumb;
}
// Sinov: 1200x800 rasm -> 200x200 markazdan
$asl = imagecreatetruecolor(1200, 800);
$t = kvadratThumb($asl, 200);
echo imagesx($t) . 'x' . imagesy($t) . "\n"; // 200x200
imagedestroy($asl);
imagedestroy($t);
Hiyla: manbadan qisqa tomon bo'yicha kvadrat (min($w,$h)) ni markazdan qirqib olamiz va uni nishon kvadratiga resample qilamiz. Natija β cho'zilmagan, chetlari kesilgan to'liq to'lgan kvadrat thumbnail (Instagram avatar uslubi).
Xulosa va keyingisi¶
Bu bobda amaliy I/O to'plamini yakunladik β real biznes ilovasida har kuni kerak bo'ladigan formatlar va saqlash:
- CSV β yadro (
fgetcsv/fputcsv) yetadi: u escaping/quoting ni o'zi qiladi (explodehech qachon emas). Excel uchun UTF-8 BOM, katta fayl uchun oqim + generator (100k qator β 2 MB RAM). PHP 8.4 da$escapeni aniq bering. - Excel
.xlsxβphpoffice/phpspreadsheet:fromArray, stil, formula (getCalculatedValue). Lekin xotira og'ir β juda katta uchun CSV yokiopenspout. - PDF β
dompdf/dompdf: HTML β PDF. O'zbekcha uchun<meta charset="UTF-8">+ DejaVu Sans shrift. Murakkab tipografiya uchun mPDF. - Rasm β GD (paketsiz):
imagescale/imagecopyresampledbilan thumbnail, format aylantirish (JPG/PNG/WEBP) sifat bilan, va yuklangan rasmni qayta kodlash orqali EXIF/GPS metadatani tozalash (maxfiylik). Professional konveyer uchun Imagick. - Bulutli saqlash β
league/flysystem: yagona interfeys (write/read/delete/listContents), katta fayl uchunwriteStream/readStream(RAM'siz). Adapterni almashtirib (local β S3-mos object storage: Contabo/MinIO/Spaces) kodga tegmasdan bulutga ko'chasiz β bu Dependency Inversion ning saqlash qatlamidagi ko'rinishi.
Hammasi haqiqiy php 8.4 + composer paketlar bilan ishga tushirildi: .xlsx, .pdf, thumbnail va disk fayllari chindan yaratildi, oqim misollarida peak RAM o'lchandi.
Bu β amaliy I/O bo'limining oxiri. Sizda endi HTTP (1-2), avtorizatsiya (3-4), tiplar (5-9), PSR/framework internals (10-16) va fayl/format/saqlash (17-18) bor β to'liq backend asboblar to'plami. Keyingi tabiiy qadam β ikki yo'nalishdan biri: (1) sifat va testlash β PHPUnit, PHPStan (statik tahlil), mutation testing va CI quvuri bilan kodingiz ishlashini emas, to'g'ri ekanini avtomatik isbotlash; yoki (2) performance va async β OPcache/JIT, Fibers va doimiy ishlovchi serverlar (RoadRunner/Swoole) bilan PHP'ni keyingi tezlik bosqichiga olib chiqish. Ikkala yo'l ham shu yerda qurgan mustahkam asosga tayanadi.
β¬ οΈ Oldingi: 17 β Fayllar, oqimlar va katta ma'lumot Β· π README Β· Keyingi: 19 β SOLID prinsiplari β‘οΈ