8-bo‘lim
Formalar va HTTP so'rov
GET va POST farqi, superglobal massivlar, forma ma'lumotini qabul qilish va PRG naqshi.
Ushbu bo‘lim mundarijasi
Shu paytgacha ma'lumotni kodning o'zida yozib kelgan edik. Endi u tashqaridan - foydalanuvchidan keladi.
Va shu daqiqadan boshlab har bir qiymatga ishonchsizlik bilan qarash kerak.
GET va POST #
| GET | POST | |
|---|---|---|
| Ma'lumot qayerda | URL da ko'rinadi | So'rov tanasida |
| Uzunlik chegarasi | ~2000 belgi | Amalda cheklanmagan |
| Xatcho'p qilish | Mumkin | Mumkin emas |
| Brauzer keshlaydi | Ha | Yo'q |
| Qayta yuborish | Xavfsiz | Takroriy amal xavfi |
| Fayl yuborish | Mumkin emas | Mumkin |
Qoida oddiy:
| Amal | Usul |
|---|---|
| Qidirish, filtrlash, saralash | GET |
| Sahifaga havola berish | GET |
| Yozish, o'chirish, to'lov | POST |
| Parol yuborish | POST |
Bu shunchaki uslub masalasi emas. Quyidagi havolani tasavvur qiling:
<a href="/ochir.php?id=5">O'chirish</a>
Nima noto'g'ri:
| Kim | Nima qiladi |
|---|---|
| Brauzer | Havolalarni oldindan yuklab qo'yishi mumkin |
| Qidiruv roboti | Barcha havolani bosib chiqadi |
| Antivirus | Xatdagi havolalarni tekshiradi |
| Foydalanuvchi | F5 bosib amalni takrorlaydi |
Ya'ni hech kim tugmani bosmasa ham, ma'lumotlaringiz o'chib ketishi mumkin. Bu haqiqiy voqea: 2006-yilda Google Accelerator veb-ilovalardagi barcha "o'chirish" havolalarini bosib chiqib, ko'p saytda ma'lumot yo'qotishga sabab bo'lgan.
To'g'ri usul - forma va POST:
<form method="post" action="/ochir.php">
<input type="hidden" name="id" value="5">
<button type="submit">O'chirish</button>
</form>
Ustiga 17-bo'limda ko'radigan CSRF tokeni ham qo'shiladi.
Superglobal massivlar #
// Har bir misol qat'iy turlar rejimida ishlaydi.
// Buyruq satrida $_GET va $_POST bo'sh bo'ladi.
// Shuning uchun ularni QO'LDA to'ldirib, mantiqni sinaymiz.
$_GET = [
"qidiruv" => "klaviatura",
"sahifa" => "2",
"tartib" => "narx_osish",
];
$_POST = [
"ism" => " Husanboy ",
"email" => "[email protected]",
"yosh" => "25",
];
echo "GET parametrlari:\n";
foreach ($_GET as $kalit => $qiymat) {
printf(" %-10s %s\n", $kalit, var_export($qiymat, true));
}
echo "\nPOST maydonlari:\n";
foreach ($_POST as $kalit => $qiymat) {
printf(" %-10s %s\n", $kalit, var_export($qiymat, true));
}
echo "\n";
echo "DIQQAT: hamma qiymat SATR - '25' emas, 25.\n";
echo "yosh turi: " . get_debug_type($_POST["yosh"]) . "\n";
GET parametrlari:
qidiruv 'klaviatura'
sahifa '2'
tartib 'narx_osish'
POST maydonlari:
ism ' Husanboy '
email '[email protected]'
yosh '25'
DIQQAT: hamma qiymat SATR - '25' emas, 25.
yosh turi: string
$_GET va $_POST da hamma narsa satrForma orqali kelgan 25 - bu int emas, "25".
HTTP protokolida turlar yo'q: hamma narsa matn.
Bu nimani anglatadi:
$yosh = $_POST["yosh"]; // "25" - satr
$yosh + 5; // 30 - PHP aylantiradi
$yosh === 25; // FALSE
soliqHisobla($yosh); // qat'iy rejimda TypeError
Istisno yo'q: input type="number" ham satr
yuboradi. Brauzerdagi tur cheklovi faqat interfeys
uchun - u foydalanuvchiga qulaylik beradi, serverga esa
hech qanday kafolat bermaydi.
Shuning uchun har bir qiymat uchun ikki qadam kerak:
| Qadam | Vosita |
|---|---|
| 1. Tekshirish | filter_var, isset |
| 2. Aylantirish | (int), (float) |
Va tartib muhim: avval tekshiring, keyin
aylantiring. Teskarisi qilinsa, (int) "abc" jimgina
0 beradi va xato yo'qoladi.
Keyingi bo'lim aynan shu haqda.
Qiymatlarni xavfsiz olish #
$_GET = ["sahifa" => "3", "tartib" => "narx", "limit" => "500"];
// Yordamchi funksiya - har bir loyihada shunday bittasi bo'ladi
function getSon(string $kalit, int $standart, int $eng_kam, int $eng_kop): int
{
$xom = $_GET[$kalit] ?? null;
if ($xom === null) {
return $standart;
}
$son = filter_var($xom, FILTER_VALIDATE_INT);
if ($son === false) {
return $standart;
}
return max($eng_kam, min($eng_kop, $son));
}
function getRoyxatdan(string $kalit, array $ruxsat, string $standart): string
{
$xom = $_GET[$kalit] ?? "";
return in_array($xom, $ruxsat, true) ? $xom : $standart;
}
$sahifa = getSon("sahifa", 1, 1, 1000);
$limit = getSon("limit", 20, 1, 100);
$tartib = getRoyxatdan("tartib", ["narx", "sana", "nom"], "sana");
printf("sahifa: %d\n", $sahifa);
printf("limit: %d (500 so'ralgan edi, 100 ga cheklandi)\n", $limit);
printf("tartib: %s\n", $tartib);
echo "\n";
echo "Endi yaroqsiz qiymatlar bilan sinaymiz:\n";
$_GET = ["sahifa" => "abc", "tartib" => "; DROP TABLE users", "limit" => "-5"];
printf("sahifa: %d (abc rad etildi)\n", getSon("sahifa", 1, 1, 1000));
printf("limit: %d (-5 chegaraga keltirildi)\n", getSon("limit", 20, 1, 100));
printf("tartib: %s (ro'yxatda yo'q - standart)\n",
getRoyxatdan("tartib", ["narx", "sana", "nom"], "sana"));
sahifa: 3
limit: 100 (500 so'ralgan edi, 100 ga cheklandi)
tartib: narx
Endi yaroqsiz qiymatlar bilan sinaymiz:
sahifa: 1 (abc rad etildi)
limit: 1 (-5 chegaraga keltirildi)
tartib: sana (ro'yxatda yo'q - standart)
getRoyxatdan funksiyasiga e'tibor bering. U kelgan
qiymatni tozalashga urinmaydi - shunchaki ruxsat
etilganlar ro'yxatida bor-yo'qligini tekshiradi.
Bu oq ro'yxat (allowlist) yondashuvi va u qora ro'yxat (blocklist) dan ancha xavfsiz:
| Yondashuv | Mantiq | Muammo |
|---|---|---|
| Qora ro'yxat | "Yomon narsalarni o'chir" | Hammasini oldindan bilib bo'lmaydi |
| Oq ro'yxat | "Faqat shularga ruxsat" | Muammo yo'q |
Yuqoridagi sinovda "; DROP TABLE users" kelgan edi.
Qora ro'yxat bilan biz DROP, ;, -- va boshqa
o'nlab narsani filtrlashimiz kerak bo'lardi - va baribir
birortasini o'tkazib yuborardik.
Oq ro'yxat bilan esa javob oddiy: bu qiymat uch ruxsatdagi biri emas, demak standart.
Bu tamoyil saralash ustunlari, fayl kengaytmalari, til kodlari - har qanday cheklangan to'plam uchun amal qiladi.
To'liq forma misoli #
Quyidagi fayl ham formani ko'rsatadi, ham uni qabul qiladi:
<?php
declare(strict_types=1);
session_start();
$xatolar = [];
$qiymatlar = ["ism" => "", "email" => "", "xabar" => ""];
if ($_SERVER["REQUEST_METHOD"] === "POST") {
// 1. Qiymatlarni olamiz va tozalaymiz
$qiymatlar["ism"] = trim($_POST["ism"] ?? "");
$qiymatlar["email"] = trim($_POST["email"] ?? "");
$qiymatlar["xabar"] = trim($_POST["xabar"] ?? "");
// 2. Tekshiramiz
if (mb_strlen($qiymatlar["ism"]) < 2) {
$xatolar["ism"] = "Ism kamida 2 belgidan iborat bo'lsin.";
}
if (!filter_var($qiymatlar["email"], FILTER_VALIDATE_EMAIL)) {
$xatolar["email"] = "Elektron pochta manzili noto'g'ri.";
}
if (mb_strlen($qiymatlar["xabar"]) < 10) {
$xatolar["xabar"] = "Xabar kamida 10 belgidan iborat bo'lsin.";
}
// 3. Xato bo'lmasa - saqlaymiz va YO'NALTIRAMIZ (PRG)
if ($xatolar === []) {
// ... bazaga yozish ...
$_SESSION["natija"] = "Xabaringiz yuborildi.";
header("Location: " . $_SERVER["PHP_SELF"], true, 303);
exit;
}
}
$natija = $_SESSION["natija"] ?? null;
unset($_SESSION["natija"]);
function e(string $matn): string
{
return htmlspecialchars($matn, ENT_QUOTES, "UTF-8");
}
?>
<!DOCTYPE html>
<html lang="uz">
<head><meta charset="UTF-8"><title>Bog'lanish</title></head>
<body>
<?php if ($natija !== null): ?>
<p class="muvaffaqiyat"><?= e($natija) ?></p>
<?php endif; ?>
<form method="post">
<label>
Ism
<input type="text" name="ism" value="<?= e($qiymatlar['ism']) ?>">
</label>
<?php if (isset($xatolar["ism"])): ?>
<span class="xato"><?= e($xatolar['ism']) ?></span>
<?php endif; ?>
<label>
Email
<input type="email" name="email" value="<?= e($qiymatlar['email']) ?>">
</label>
<?php if (isset($xatolar["email"])): ?>
<span class="xato"><?= e($xatolar['email']) ?></span>
<?php endif; ?>
<label>
Xabar
<textarea name="xabar"><?= e($qiymatlar['xabar']) ?></textarea>
</label>
<?php if (isset($xatolar["xabar"])): ?>
<span class="xato"><?= e($xatolar['xabar']) ?></span>
<?php endif; ?>
<button type="submit">Yuborish</button>
</form>
</body>
</html>
Formada value="<?= e($qiymatlar['ism']) ?>" bor.
Bu kichik detal, lekin foydalanuvchi tajribasi uchun
juda muhim.
Usiz nima bo'ladi: foydalanuvchi uzun xabar yozadi, emailda bitta harfni xato qiladi, "Yuborish" ni bosadi - va hamma narsa yo'qoladi. Qaytadan yozishga majbur bo'ladi.
e() funksiyasi esa htmlspecialchars ning qisqa
nomi. Uni har bir loyihada yozib qo'yish keng tarqalgan
amaliyot - chunki u juda ko'p ishlatiladi.
Diqqat: value atributi ichida ENT_QUOTES majburiy.
Usiz foydalanuvchi " onmouseover="alert(1) deb yozib,
atributdan chiqib ketishi mumkin.
17-bo'limda bu hujumni batafsil ko'ramiz.
So'rov haqida ma'lumot #
// Buyruq satrida bu qiymatlar bo'lmaydi - shuning uchun taqlid qilamiz
$_SERVER["REQUEST_METHOD"] = "POST";
$_SERVER["REQUEST_URI"] = "/mahsulotlar?sahifa=2&tartib=narx";
$_SERVER["HTTP_USER_AGENT"] = "Mozilla/5.0 (Windows NT 10.0)";
$_SERVER["REMOTE_ADDR"] = "203.0.113.45";
$_SERVER["HTTPS"] = "on";
$maydonlar = ["REQUEST_METHOD", "REQUEST_URI", "REMOTE_ADDR", "HTTPS"];
foreach ($maydonlar as $m) {
printf("%-18s %s\n", $m, $_SERVER[$m] ?? "(yo'q)");
}
echo "\n";
$xavfsizmi = ($_SERVER["HTTPS"] ?? "off") === "on";
echo "HTTPS orqalimi: " . ($xavfsizmi ? "ha" : "yo'q") . "\n";
echo "\n";
// URL ni qismlarga ajratish
$qismlar = parse_url($_SERVER["REQUEST_URI"]);
echo "Yo'l: " . ($qismlar["path"] ?? "") . "\n";
echo "So'rov qismi: " . ($qismlar["query"] ?? "") . "\n";
parse_str($qismlar["query"] ?? "", $parametrlar);
echo "Parametrlar:\n";
foreach ($parametrlar as $k => $v) {
printf(" %-8s %s\n", $k, $v);
}
REQUEST_METHOD POST
REQUEST_URI /mahsulotlar?sahifa=2&tartib=narx
REMOTE_ADDR 203.0.113.45
HTTPS on
HTTPS orqalimi: ha
Yo'l: /mahsulotlar
So'rov qismi: sahifa=2&tartib=narx
Parametrlar:
sahifa 2
tartib narx
$_SERVER ning ba'zi maydonlari ham ishonchsiz$_SERVER massivi ikki xil ma'lumotdan iborat:
| Ishonchli (server yozadi) | Ishonchsiz (mijoz yuboradi) |
|---|---|
REQUEST_METHOD | HTTP_USER_AGENT |
SCRIPT_NAME | HTTP_REFERER |
SERVER_NAME | HTTP_X_FORWARDED_FOR |
REMOTE_ADDR | HTTP_HOST (sozlamaga bog'liq) |
HTTP_ bilan boshlanadigan hamma narsa - bu
foydalanuvchi yuborgan sarlavha va uni xohlagancha
o'zgartirish mumkin.
Eng xavflisi - kirish nazoratini HTTP_REFERER ga
asoslash:
if (str_contains($_SERVER["HTTP_REFERER"], "mening-saytim")) {
// himoya EMAS - istalgan odam buni soxtalashtiradi
}
REMOTE_ADDR ham nozik: agar sayt proksi yoki CDN
orqasida bo'lsa, u proksi manzilini ko'rsatadi. Haqiqiy
IP X-Forwarded-For da bo'ladi - lekin uni faqat
ishonchli proksidan kelganda qabul qilish kerak.
- GET va POST uchun ikkita forma yozing, farqni URL da kuzating.
$_GETdagi qiymat turiniget_debug_typebilan tekshiring.filter_varbilan butun sonni tekshiruvchi yordamchi yozing.- Unga
"abc"bering - standart qiymat qaytdimi? - Saralash ustuni uchun oq ro'yxat yarating.
- Ro'yxatda yo'q qiymat yuboring - nima bo'ladi?
- Formani
valuebilan to'ldiring va xato holatini sinang. htmlspecialcharsniENT_QUOTESsiz sinab ko'ring.- PRG naqshini qo'llang va
F5bosib ko'ring. parse_urlvaparse_strbilan URL ni qismlarga ajrating.
Xulosa #
- GET o'qish uchun, POST o'zgartirish uchun - bu qat'iy qoida.
- GET havolasi holatni o'zgartirsa, uni brauzer yoki robot o'zi bosib yuborishi mumkin.
$_GETva$_POSTdagi hamma qiymat satr -input type="number"ham.- Tartib muhim: avval tekshiring, keyin aylantiring.
(int) "abc"jimgina0beradi - shuning uchunfilter_varishlating.- Oq ro'yxat qora ro'yxatdan har doim xavfsizroq.
- Xato bo'lganda formani kiritilgan qiymatlar bilan qayta ko'rsating.
- HTML ichida chiqarishda
htmlspecialchars($x, ENT_QUOTES, "UTF-8")ishlating. - PRG naqshi: POST -> 303 redirect -> GET; aks holda
F5amalni takrorlaydi. $_SERVERdaHTTP_bilan boshlanuvchi maydonlar foydalanuvchi yuborgan - ishonmang.- Brauzerdagi JavaScript tekshiruvi - qulaylik, himoya emas.
Keyingi bo'limda validatsiyani chuqurroq - qayta ishlatiladigan tekshiruv qatlami sifatida yozamiz.
O‘qish tarixini saqlamoqchimisiz?
Tizimga kirsangiz, tugatgan bo‘limlaringiz saqlanadi va qoldirgan joyingizdan davom etasiz.
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.