Saytingizga mijoz sharh qoldirdi, oxiriga tabassum emoji'sini qo'ydi va "Saqlash" tugmasini bosdi. Natijada sharh yo saqlanmaydi, yo emoji o'rnida to'rtta so'roq belgisi paydo bo'ladi, yoki matn aynan shu joydan kesilib qoladi. Bunday holat ko'pincha eski yoki noto'g'ri sozlangan MySQL bazasida uchraydi va uning sababi deyarli har doim bitta: jadval yoki ulanish kodirovkasi utf8mb4 emas. Bu maqolada muammoning ildizini, o'zbek harflariga qanday ta'sir qilishini va bazani xavfsiz o'tkazish tartibini ko'rib chiqamiz.
MySQL'dagi "utf8" aslida to'liq UTF-8 emas
UTF-8 standartida bitta belgi 1 baytdan 4 baytgacha joy egallaydi. Lotin harflari va raqamlar 1 bayt, kirill harflari (jumladan ў, қ, ғ, ҳ) 2 bayt, ko'plab maxsus belgilar 3 bayt, emoji va ba'zi kam ishlatiladigan ieroglif va belgilar esa 4 bayt oladi. MySQL tarixan utf8 nomi ostida faqat 3 baytgacha bo'lgan belgilarni saqlaydigan qisqartirilgan variantni joriy qilgan, hozirgi hujjatlarda u utf8mb3 deb ham ataladi. To'liq UTF-8 ni qo'llab-quvvatlaydigan kodirovka esa utf8mb4 deb nomlanadi.
Demak, jadval utf8 bilan yaratilgan bo'lsa, 4 baytli belgi kelganda MySQL uni saqlay olmaydi. Qat'iy rejimda (STRICT_TRANS_TABLES) so'rov xato bilan qaytadi, yumshoq rejimda esa belgi so'roq belgisiga almashtiriladi yoki qator kesiladi. Log faylida odatda shunday xabar ko'rinadi:
SQLSTATE[HY000]: General error: 1366 Incorrect string value: '\xF0\x9F\x98\x80' for column 'comment' at row 1
Bu yerdagi \xF0\x9F\x98\x80 baytlari aynan tabassum emoji'sining UTF-8 dagi ko'rinishidir.
O'zbek harflari bilan bog'liq haqiqiy muammo qayerda
Ko'pchilik o'zbekcha oʻ, gʻ harflari ham emoji kabi buziladi deb o'ylaydi, lekin bu biroz boshqacha. Oddiy klaviaturadagi apostrof bilan yozilgan o' va g' 1 baytli belgilardan iborat, maxsus ʻ (U+02BB) belgisi esa 3 bayt oladi, kirill ў va қ esa 2 bayt. Shuning uchun ular utf8 jadvalda ham to'g'ri saqlanadi, utf8mb4 ga o'tish esa bu muammoni ham, emoji muammosini ham bir yo'la yopadi. O'zbek harflari odatda boshqa sabab bilan buziladi: jadval umuman latin1 yoki cp1251 kodirovkasida yaratilgan bo'ladi yoki PHP bazaga ulanishda kodirovkani ko'rsatmaydi. Bunday holatda kirill matn "????" ko'rinishida saqlanadi va uni keyin tiklashning iloji bo'lmaydi.
Hozirgi holatni qanday tekshirish mumkin
O'zgartirishdan oldin baza, jadvallar va ustunlar qaysi kodirovkada ekanini aniq bilib olish kerak. Buni phpMyAdmin orqali yoki SQL so'rovi bilan qilish mumkin. Quyidagi so'rov bazadagi har bir jadvalning collation qiymatini ko'rsatadi, uning nomining boshi kodirovkani bildiradi:
SELECT TABLE_NAME, TABLE_COLLATION
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = 'example_db';
SHOW VARIABLES LIKE 'character_set%';
SHOW FULL COLUMNS FROM comments;
Agar natijada latin1_swedish_ci yoki utf8_general_ci ko'rsangiz, o'tkazish kerak. utf8mb4_unicode_ci yoki utf8mb4_0900_ai_ci ko'rinsa, jadvallar tartibda, muammo ulanish sozlamasida bo'lishi mumkin.
Bazani utf8mb4 ga o'tkazish tartibi
Har qanday o'zgartirishdan oldin bazaning to'liq zaxira nusxasini oling. Hosting panelida bu bir necha tugma bilan qilinadi, SSH bo'lsa esa mysqldump ishlatiladi. Zaxira kodirovkasi ham aniq ko'rsatilishi muhim, aks holda dump faylning o'zi buzilib qolishi mumkin:
mysqldump --default-character-set=utf8mb4 -u example_user -p example_db > example_db_backup.sql
Shundan keyin baza va har bir jadval uchun o'tkazish buyruqlari bajariladi. CONVERT TO jadvaldagi barcha matn ustunlarini yangi kodirovkaga o'tkazadi va mavjud ma'lumotni qayta kodlaydi:
ALTER DATABASE example_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
ALTER TABLE comments CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
ALTER TABLE products CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
Indeks uzunligi bilan bog'liq xato
Eski MySQL versiyalarida (5.6 va undan oldingi, ayniqsa COMPACT qator formatida) o'tkazish paytida Specified key was too long; max key length is 767 bytes xatosi chiqishi mumkin. Sababi: utf8mb4 da har belgiga 4 bayt hisoblanadi, shuning uchun VARCHAR(255) ustunidagi indeks 1020 baytni talab qiladi. MySQL 5.7 va undan yangi versiyalarda DYNAMIC qator formati bilan chegara 3072 baytgacha oshgan va bu muammo deyarli uchramaydi. Eski serverda esa indekslangan ustunni VARCHAR(191) ga qisqartirish odatiy yechim hisoblanadi.
PHP ulanishida kodirovkani ko'rsatish
Jadvallarni o'tkazish yarim ish xolos. Agar ilova bazaga ulanganda kodirovkani aytmasa, server standart qiymatni ishlatadi va emoji yana buziladi. PDO uchun DSN qatoriga charset qo'shiladi, mysqli uchun esa alohida funksiya chaqiriladi:
// PDO
$pdo = new PDO('mysql:host=localhost;dbname=example_db;charset=utf8mb4', $user, $pass);
// mysqli
$db = new mysqli('localhost', $user, $pass, 'example_db');
$db->set_charset('utf8mb4');
WordPress'da bu sozlama wp-config.php faylida turadi: define('DB_CHARSET', 'utf8mb4');. Laravel'da config/database.php faylidagi charset va collation qiymatlari standart holatda allaqachon utf8mb4 bo'ladi, faqat eski loyihalarda tekshirib qo'yish kerak. Eski kodda uchraydigan SET NAMES utf8 qatorlarini ham SET NAMES utf8mb4 ga almashtirish lozim.
Amaliy misol: Toshkentdagi gul do'koni
Tasavvur qiling, Toshkentdagi gul yetkazib berish xizmati misol.uz saytida buyurtma formasi bor. Mijozlar tabrik matniga ko'pincha 🌹 va 🎉 kabi emoji qo'shishadi. Sayt bir necha yil oldin yaratilgan, orders jadvali utf8_general_ci da. Natijada emoji'li buyurtmalar bazaga tushmay qolgan, operator esa ularni umuman ko'rmagan. Dasturchi avval zaxira nusxa oldi, so'ng orders va order_items jadvallarini CONVERT TO bilan o'tkazdi, PDO ulanishiga charset=utf8mb4 qo'shdi va test buyurtma yubordi. Shundan keyin tabrik matnlari emoji bilan birga to'liq saqlana boshladi.
Katta jadvalni o'tkazish bir necha daqiqa davom etib, jadvalni bloklashi mumkin, shuning uchun buni tashrif kam paytda qiling. sayt.uz hostingida bazalar bilan phpMyAdmin orqali ishlash mumkin, panel esa sayt.uz kabinetidan ochiladi.
O'tkazishdan keyin nimalarni tekshirish kerak
- Bitta yozuvga emoji, kirill ў, қ va lotin
o',g'qo'shib saqlang va sahifada qanday ko'rinishini tekshiring. - HTML sahifada
<meta charset="utf-8">borligiga ishonch hosil qiling, aks holda brauzer matnni boshqacha talqin qilishi mumkin. - Qidiruv va saralash to'g'ri ishlayotganini ko'ring, chunki
collationo'zgarishi bilan taqqoslash qoidalari ham o'zgaradi. - Agar o'tkazishdan oldin matn allaqachon "????" bo'lib saqlangan bo'lsa, uni kodirovka o'zgartirish bilan tiklab bo'lmaydi, faqat zaxira nusxadan qayta yuklash yordam beradi.
Oxirgi bandni alohida ta'kidlash kerak: utf8mb4 kelajakdagi ma'lumotni himoya qiladi, lekin o'tmishda yo'qolgan belgilarni qaytarmaydi. Shuning uchun yangi loyiha yaratayotganda bazani va jadvallarni darhol utf8mb4_unicode_ci bilan yaratish eng to'g'ri yo'l. Savollar bo'lsa, sayt.uz texnik yordamiga tiket orqali murojaat qilishingiz mumkin.