18-bo‘lim
JSON va API
JSON kodlash va dekodlash, REST API yaratish, HTTP holat kodlari va tashqi API ga so'rov yuborish.
Ushbu bo‘lim mundarijasi
Mobil ilova, JavaScript va boshqa xizmatlar bilan gaplashish uchun sahifa emas, ma'lumot kerak. Bugun bu ma'lumot deyarli har doim JSON shaklida uzatiladi.
JSON kodlash #
// Har bir misol qat'iy turlar rejimida ishlaydi.
const JSON_BAYROQLAR = JSON_UNESCAPED_UNICODE
| JSON_UNESCAPED_SLASHES
| JSON_THROW_ON_ERROR;
$mahsulot = [
"id" => 7,
"nom" => "Klaviatura",
"narx" => 350000,
"omborda" => true,
"turkum" => null,
"teglar" => ["aksessuar", "simsiz"],
"havola" => "https://softromeda.uz/mahsulot/7",
];
echo "Bayroqlarsiz:\n";
echo json_encode($mahsulot) . "\n";
echo "\nTo'g'ri bayroqlar bilan:\n";
echo json_encode($mahsulot, JSON_BAYROQLAR) . "\n";
echo "\nO'qishga qulay shaklda:\n";
echo json_encode($mahsulot, JSON_BAYROQLAR | JSON_PRETTY_PRINT) . "\n";
echo "\n";
// ASCII bo'lmagan belgilar bilan farq.
// "\u{02BB}" - o'zbek tutuq belgisi (4-bo'limda ko'rgan edik).
$uz = ["shahar" => "O\u{02BB}zbekiston", "belgi" => "\u{1F418}"];
echo "UNESCAPED_UNICODE siz: " . json_encode($uz) . "\n";
echo "UNESCAPED_UNICODE bilan: " . json_encode($uz, JSON_BAYROQLAR) . "\n";
echo "\n";
echo "Birinchi qatorda belgilar \\uXXXX kodiga aylandi - JSON to'g'ri,\n";
echo "lekin uni odam o'qiy olmaydi va fayl hajmi ortadi.\n";
Bayroqlarsiz:
{"id":7,"nom":"Klaviatura","narx":350000,"omborda":true,"turkum":null,"teglar":["aksessuar","simsiz"],"havola":"https:\/\/softromeda.uz\/mahsulot\/7"}
To'g'ri bayroqlar bilan:
{"id":7,"nom":"Klaviatura","narx":350000,"omborda":true,"turkum":null,"teglar":["aksessuar","simsiz"],"havola":"https://softromeda.uz/mahsulot/7"}
O'qishga qulay shaklda:
{
"id": 7,
"nom": "Klaviatura",
"narx": 350000,
"omborda": true,
"turkum": null,
"teglar": [
"aksessuar",
"simsiz"
],
"havola": "https://softromeda.uz/mahsulot/7"
}
UNESCAPED_UNICODE siz: {"shahar":"O\u02bbzbekiston","belgi":"\ud83d\udc18"}
UNESCAPED_UNICODE bilan: {"shahar":"Oʻzbekiston","belgi":"🐘"}
Birinchi qatorda belgilar \uXXXX kodiga aylandi - JSON to'g'ri,
lekin uni odam o'qiy olmaydi va fayl hajmi ortadi.
5-bo'limda aytgan edik: array_filter kalitlarni
saqlaydi. Endi uning oqibatini ko'ramiz.
$sonlar = [1, 2, 3, 4, 5];
$juftlar = array_filter($sonlar, fn($x) => $x % 2 === 0);
json_encode($juftlar); // {"1":2,"3":4}
json_encode(array_values($juftlar)); // [2,4]
Birinchi natija obyekt, ikkinchisi massiv. JavaScript tomonida bu katta farq:
data.forEach(...) // obyektda ishlamaydi
data.length // obyektda undefined
Qoida: API dan ro'yxat qaytarayotgan bo'lsangiz, undan
oldin har doim array_values() chaqiring.
Bo'sh massiv bilan ham xuddi shu muammo bor:
| PHP qiymati | JSON |
|---|---|
[] | [] |
(object) [] | {} |
Filtrlangan [] | [] |
Agar API doim obyekt qaytarishi kerak bo'lsa,
JSON_FORCE_OBJECT bayrog'i bor - lekin uni butun
javobga emas, aniq joyga qo'llang.
JSON dekodlash #
$xom = '{"id":7,"nom":"Klaviatura","narx":350000,"omborda":true,"teglar":["a","b"]}';
// Assotsiativ massiv sifatida (ikkinchi argument true)
$massiv = json_decode($xom, true, 512, JSON_THROW_ON_ERROR);
echo "Massiv sifatida:\n";
printf(" nom: %s\n", $massiv["nom"]);
printf(" narx: %d (turi: %s)\n", $massiv["narx"], get_debug_type($massiv["narx"]));
printf(" omborda: %s (turi: %s)\n",
var_export($massiv["omborda"], true), get_debug_type($massiv["omborda"]));
echo "\n";
// Obyekt sifatida (ikkinchi argument false yoki berilmagan)
$obyekt = json_decode($xom, false, 512, JSON_THROW_ON_ERROR);
echo "Obyekt sifatida:\n";
printf(" nom: %s\n", $obyekt->nom);
printf(" sinf: %s\n", $obyekt::class);
printf(" teg 1: %s\n", $obyekt->teglar[0]);
echo "\n";
// Buzuq JSON
$buzuqlar = ['{"a":1,}', "{'a':1}", '{"a":}', "salom", ""];
echo "Buzuq JSON larni tekshirish:\n";
foreach ($buzuqlar as $b) {
try {
json_decode($b, true, 512, JSON_THROW_ON_ERROR);
echo " " . str_pad("'{$b}'", 12) . " OK\n";
} catch (JsonException $x) {
echo " " . str_pad("'{$b}'", 12) . " " . $x->getMessage() . "\n";
}
}
Massiv sifatida:
nom: Klaviatura
narx: 350000 (turi: int)
omborda: true (turi: bool)
Obyekt sifatida:
nom: Klaviatura
sinf: stdClass
teg 1: a
Buzuq JSON larni tekshirish:
'{"a":1,}' Syntax error
'{'a':1}' Syntax error
'{"a":}' Syntax error
'salom' Syntax error
'' Syntax error
API javobini shakllantirish #
final class ApiJavob
{
public function __construct(
public readonly int $kod,
public readonly array $tana,
) {}
public static function ok(array $malumot): self
{
return new self(200, ["ok" => true, "data" => $malumot]);
}
public static function yaratildi(array $malumot): self
{
return new self(201, ["ok" => true, "data" => $malumot]);
}
public static function xato(int $kod, string $xabar, array $tafsilot = []): self
{
$tana = ["ok" => false, "error" => ["xabar" => $xabar]];
if ($tafsilot !== []) {
$tana["error"]["maydonlar"] = $tafsilot;
}
return new self($kod, $tana);
}
public function chiqar(): string
{
// Haqiqiy kodda: http_response_code($this->kod);
// header("Content-Type: application/json; charset=utf-8");
return sprintf(
"HTTP %d\n%s",
$this->kod,
json_encode($this->tana, JSON_BAYROQLAR | JSON_PRETTY_PRINT)
);
}
}
$javoblar = [
ApiJavob::ok(["id" => 7, "nom" => "Klaviatura"]),
ApiJavob::yaratildi(["id" => 12]),
ApiJavob::xato(404, "Mahsulot topilmadi"),
ApiJavob::xato(422, "Ma'lumot noto'g'ri", [
"narx" => "Musbat son bo'lishi kerak",
"nom" => "Bo'sh bo'lmasin",
]),
];
foreach ($javoblar as $j) {
echo $j->chiqar() . "\n\n";
}
HTTP 200
{
"ok": true,
"data": {
"id": 7,
"nom": "Klaviatura"
}
}
HTTP 201
{
"ok": true,
"data": {
"id": 12
}
}
HTTP 404
{
"ok": false,
"error": {
"xabar": "Mahsulot topilmadi"
}
}
HTTP 422
{
"ok": false,
"error": {
"xabar": "Ma'lumot noto'g'ri",
"maydonlar": {
"narx": "Musbat son bo'lishi kerak",
"nom": "Bo'sh bo'lmasin"
}
}
}
Yuqoridagi sinf har bir javobga bir xil tuzilma beradi:
{"ok": true, "data": {...}}
{"ok": false, "error": {"xabar": "..."}}
Bu mijoz tomonini ancha soddalashtiradi - bitta funksiya barcha javoblarni qayta ishlaydi.
Validatsiya xatolari uchun 422 Unprocessable Entity
ishlatiladi: so'rov shakli to'g'ri, lekin mazmuni
qoidalarga mos emas. 400 Bad Request esa so'rovning
o'zi buzuq bo'lganda (masalan JSON tahlil qilinmasa).
Xato javobida qaysi maydon xato ekanini ko'rsating - mijoz uni formada tegishli joyga chiqara oladi.
Va eng muhimi: ok maydoniga tayanmang. Mijoz
avvalo HTTP holat kodini tekshirsin. ok maydoni
faqat qulaylik uchun.
Kichik REST API #
<?php
// public/api.php
declare(strict_types=1);
require __DIR__ . "/../vendor/autoload.php";
header("Content-Type: application/json; charset=utf-8");
$usul = $_SERVER["REQUEST_METHOD"];
$yol = parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH) ?? "/";
$qism = array_values(array_filter(explode("/", trim($yol, "/"))));
// POST/PUT tanasini o'qish - $_POST JSON ni TUSHUNMAYDI
$tana = [];
if (in_array($usul, ["POST", "PUT", "PATCH"], true)) {
$xom = file_get_contents("php://input");
try {
$tana = $xom === "" ? [] : json_decode($xom, true, 512, JSON_THROW_ON_ERROR);
} catch (JsonException) {
http_response_code(400);
echo json_encode(["ok" => false, "error" => ["xabar" => "JSON buzuq"]]);
exit;
}
}
// Marshrutlash
$javob = match (true) {
$usul === "GET" && $qism === ["api", "mahsulotlar"]
=> ApiJavob::ok($ombor->royxat()),
$usul === "GET" && count($qism) === 3 && $qism[1] === "mahsulotlar"
=> ($m = $ombor->top((int) $qism[2])) !== null
? ApiJavob::ok($m)
: ApiJavob::xato(404, "Topilmadi"),
$usul === "POST" && $qism === ["api", "mahsulotlar"]
=> ApiJavob::yaratildi($ombor->qosh($tana)),
$usul === "DELETE" && count($qism) === 3 && $qism[1] === "mahsulotlar"
=> $ombor->ochir((int) $qism[2])
? new ApiJavob(204, [])
: ApiJavob::xato(404, "Topilmadi"),
default => ApiJavob::xato(404, "Bunday manzil yo'q"),
};
http_response_code($javob->kod);
if ($javob->kod !== 204) {
echo json_encode($javob->tana, JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR);
}
$_POST JSON tanasini o'qimaydiBu juda ko'p vaqt yo'qotadigan chalkashlik.
$_POST faqat ikki holatda to'ladi:
Content-Type | $_POST to'ladimi |
|---|---|
application/x-www-form-urlencoded | Ha |
multipart/form-data | Ha |
application/json | Yo'q - bo'sh qoladi |
JavaScript dan fetch bilan JSON yuborganingizda
$_POST bo'sh bo'ladi va kod "ma'lumot kelmadi"
deb o'ylaydi.
To'g'ri usul - xom tanani o'qish:
$xom = file_get_contents("php://input");
$tana = json_decode($xom, true, 512, JSON_THROW_ON_ERROR);
Diqqat: php://input ni faqat bir marta o'qish
mumkin (ba'zi sozlamalarda). Shuning uchun uni
o'zgaruvchiga saqlab qo'ying.
Va PUT/PATCH/DELETE uchun $_POST hech qachon
to'lmaydi - usuldan qat'i nazar php://input kerak.
Tashqi API ga so'rov #
<?php
declare(strict_types=1);
function apiSorov(string $url, array $tana = [], array $sarlavhalar = []): array
{
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 10, // MAJBURIY - aks holda abadiy kutadi
CURLOPT_CONNECTTIMEOUT => 5,
CURLOPT_SSL_VERIFYPEER => true, // HECH QACHON false qilmang
CURLOPT_SSL_VERIFYHOST => 2,
CURLOPT_FOLLOWLOCATION => false, // yo'naltirishni o'zingiz boshqaring
CURLOPT_HTTPHEADER => array_merge(
["Content-Type: application/json", "Accept: application/json"],
$sarlavhalar
),
]);
if ($tana !== []) {
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS,
json_encode($tana, JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR));
}
$javob = curl_exec($ch);
$kod = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$xato = curl_error($ch);
curl_close($ch);
if ($javob === false) {
throw new RuntimeException("So'rov yuborilmadi: {$xato}");
}
if ($kod >= 400) {
throw new RuntimeException("API xato qaytardi: HTTP {$kod}");
}
return json_decode((string) $javob, true, 512, JSON_THROW_ON_ERROR);
}
CURLOPT_SSL_VERIFYPEER => false - hech qachonInternetda "sertifikat xatosi" muammosiga eng ko'p beriladigan "yechim" shu. U xato.
Bu sozlama TLS ni butunlay ma'nosiz qiladi: siz shifrli kanalda gaplashasiz, lekin kim bilan gaplashayotganingizni tekshirmaysiz. O'rtadagi odam hujumi ochiq qoladi.
Haqiqiy sabab odatda eskirgan CA sertifikatlar to'plami. To'g'ri yechim:
curl_setopt($ch, CURLOPT_CAINFO, "/yol/cacert.pem");
cacert.pem ni curl.se/docs/caextract.html dan olish
mumkin, yoki php.ini da curl.cainfo ni ko'rsating.
Yana uch muhim sozlama:
| Sozlama | Nima uchun |
|---|---|
CURLOPT_TIMEOUT | Usiz so'rov abadiy kutishi mumkin |
CURLOPT_CONNECTTIMEOUT | Ulanish bosqichi uchun alohida |
CURLOPT_FOLLOWLOCATION => false | Yo'naltirish ichki tarmoqqa olib borishi mumkin |
Birinchisi eng ko'p muammo keltiradi: taymautsiz bitta sekin tashqi API butun saytingizni to'xtatib qo'yadi.
- Massivni
json_encodebilan kodlang va bayroqlarni solishtiring. - O'zbek harflarini
JSON_UNESCAPED_UNICODEsiz kodlang. array_filternatijasini JSON ga aylantiring - massivmi yoki obyekt?array_values()qo'shib qayta sinang.json_decodenitruevafalseikkinchi argument bilan chaqiring.- Buzuq JSON bering va
JsonExceptionni tuting. - Bir xil shaklda javob qaytaruvchi
ApiJavobsinfini yozing. - 404 va 422 uchun to'g'ri holat kodini bering.
fetchbilan JSON yuborib,$_POSTbo'shligini tekshiring.php://inputdan o'qib, muammoni hal qiling.
Xulosa #
- JSON kodlashda
JSON_UNESCAPED_UNICODE,JSON_UNESCAPED_SLASHESvaJSON_THROW_ON_ERRORishlating. - Filtrlangan massiv JSON da obyektga aylanadi -
array_values()ni unutmang. json_decodening ikkinchi argumentitruebo'lsa massiv,falsebo'lsa obyekt qaytadi.JSON_THROW_ON_ERRORsiz buzuq JSONnullberadi va uni haqiqiynulldan ajratib bo'lmaydi.- API javobi butun loyiha bo'ylab bir xil shaklda bo'lsin.
- HTTP holat kodini to'g'ri bering - xatoda ham
200qaytarmang. 401- kim ekaningiz noma'lum;403- ma'lum, lekin huquq yo'q.- Validatsiya xatosi uchun 422, buzuq so'rov uchun 400.
- REST da URL resurs, HTTP usuli esa amal - URL da fe'l bo'lmasin.
$_POSTJSON tanasini o'qimaydi -php://inputdan o'qing.- cURL da
CURLOPT_TIMEOUTmajburiy,SSL_VERIFYPEER => falseesa taqiqlangan.
Keyingi bo'limda PHP 8 ning zamonaviy imkoniyatlarini ko'rib chiqamiz.
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.