Standardna početnička greška (ne setovanje enkodinga konekcije) vrlo često rezultuje velikim problemom zvanim smeće u bazi. Primer problema je standardna PHP greška
1. aplikacija misli da radi sa utf8mb4 (forsiran je utf8mb4 enkoding u hederu html-a, web enkodira karatktere kao utf8mb4 i očekuje ih kao utf8)
2. baza čuva podatke kao utf8
3. NIKO NIJE SETOVAO ENKODING KONEKCIJE što u php slučaju znači da je enkoding defaultni – utf8mb4 COLLATE=utf8mb4_unicode_520_ci;.
Šta se desi kada u ovom slučaju pošaljemo utf8mb4 karakter Č u tabelu. Kako je php glup on to pošalje kako ga ima u memoriji, dakle on mysql drajveru pošalje ta dva bajta od kojih je karakter Č sastavljen. Pošto je enkoding konekcije utf8mb4 COLLATE=utf8mb4_unicode_520_ci; koji ima jednobajtne karaktere, mysql ta dva bajta vidi kao DVA utf8mb4 COLLATE=utf8mb4_unicode_520_ci; karaktera, i svaki od njih upiše kao jedan trobajtni utf8mb4 karakter. Sada u polju na mysql-u imam 6 bajtova tj dva karaktera umesto jedan. Kada sledeci put pročitate taj podatak, mysql će prekodirati ta dva utf8mb4 karaktera u 2 jednobajtna utf8mb4 COLLATE=utf8mb4_unicode_520_ci; karaktera i vratiti ih php-u koji će ta dva karaktera poslati web browseru koji će pošto očekuje utf8mb4 to pogrešno interpretirati kao utf8mb4 karakter i prikazati slovo Č.
Dakle poslali smo Č, pročitali smo Č – gde je problem? Problem je u tome što u bazi ne piše Č, što sortiranje tog stringa neće biti kao da se na tom mestu nalazi Č, što poredjenje neće raditi, što računanje dužine neće raditi, što zauzima duplo više mesta u bazi…. i najzad, što ako probate da pročitate taj podatak VALIDNO – videćete da je u bazi SMEĆE.
Kako rešiti ovaj problem kada ga zateknemo?
Dakle ako imamo tabelu:
create table `smece` ( `id` int auto_increment primary key not null, `djubre` varchar(100) character set utf8 ) engine=myisam;
i imamo aplikaciju sa početničkom greškom:
< !DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <body> <form action="djubre.php" method="post"> <input name="q" type="text" /> <input type="submit" /> </form> < ?php mysql_connect(':/tmp/mysql.sock', 'root', '') or die(mysql_error()); mysql_select_db('test') or die (mysql_error()); $res = mysql_query('select djubre from smece'); while ($row=mysql_fetch_row($res)) echo $row[0].'<br/>'; @mysql_free_result($res); if ( isset( $_POST['q'] ) ) { $djubre = $_POST['q']; mysql_query("insert into smece (djubre) values ('$djubre')"); mysql_close(); } ?> </body> </html>
na web-u se vidi:
dšdšdšd dasdad dšdšdšd dššdšdšdš ccccccccccc šššššššššš ccccccccccccc
ali ako pogledamo u bazi vidimo smeće:
mysql> select * from smece; +----+-------------------------------------------------------------------+ | id | djubre | +----+-------------------------------------------------------------------+ | 1 | ÄÅ¡ÄÅ¡ÄÅ¡Ä | | 2 | dasdad | | 3 | ÄÅ¡ÄÅ¡ÄÅ¡Ä | | 4 | ÄÅ¡Å¡ÄÅ¡ÄÅ¡ÄÅ¡ | | 5 | ÄÄÄÄÄÄÄÄÄÄÄ | | 6 | Å¡Å¡Å¡Å¡Å¡Å¡Å¡Å¡Å¡Å¡ | | 7 | ÄÄÄÄÄÄÄÄÄÄÄÄÄ | +----+-------------------------------------------------------------------+ 7 rows in set (0.00 sec) mysql> select hex(djubre) from smece; +------------------------------------------------------------------------------------------------------------------------------------+ | hex(djubre) | +------------------------------------------------------------------------------------------------------------------------------------+ | C384E28098C385C2A1C384E28098C385C2A1C384E28098C385C2A1C384E28098 | | 646173646164 | | C384E28098C385C2A1C384E28098C385C2A1C384E28098C385C2A1C384E28098 | | C384E28098C385C2A1C385C2A1C384E28098C385C2A1C384E28098C385C2A1C384E28098C385C2A1 | | C384C28DC384C28DC384C28DC384C28DC384C28DC384C28DC384C28DC384C28DC384C28DC384C28DC384C28D | | C385C2A1C385C2A1C385C2A1C385C2A1C385C2A1C385C2A1C385C2A1C385C2A1C385C2A1C385C2A1 | | C384E280A1C384E280A1C384E280A1C384E280A1C384E280A1C384E280A1C384E280A1C384E280A1C384E280A1C384E280A1C384E280A1C384E280A1C384E280A1 | +------------------------------------------------------------------------------------------------------------------------------------+ 7 rows in set (0.00 sec)
Ovo smeće se na webu i dalje vidi kako treba !!!
sada da bi ovo smeće prebacili u ispravan zapis:
mysql> alter table smece change djubre djubre varchar(100) character set utf8mb4 COLLATE=utf8mb4_unicode_520_ci; Query OK, 7 rows affected (0.00 sec) Records: 7 Duplicates: 0 Warnings: 0 mysql> alter table smece change djubre djubre blob; Query OK, 7 rows affected (0.00 sec) Records: 7 Duplicates: 0 Warnings: 0 mysql> alter table smece change djubre djubre varchar(100) character set utf8; Query OK, 7 rows affected (0.01 sec) Records: 7 Duplicates: 0 Warnings: 0 mysql> select * from smece; +----+----------------------------+ | id | djubre | +----+----------------------------+ | 1 | dšdšdšd | | 2 | dasdad | | 3 | dšdšdšd | | 4 | dššdšdšdš | | 5 | ccccccccccc | | 6 | šššššššššš | | 7 | ccccccccccccc | +----+----------------------------+ 7 rows in set (0.00 sec) mysql> select hex(djubre) from smece; +------------------------------------------------------+ | hex(djubre) | +------------------------------------------------------+ | C491C5A1C491C5A1C491C5A1C491 | | 646173646164 | | C491C5A1C491C5A1C491C5A1C491 | | C491C5A1C5A1C491C5A1C491C5A1C491C5A1 | | C48DC48DC48DC48DC48DC48DC48DC48DC48DC48DC48D | | C5A1C5A1C5A1C5A1C5A1C5A1C5A1C5A1C5A1C5A1 | | C487C487C487C487C487C487C487C487C487C487C487C487C487 | +------------------------------------------------------+ 7 rows in set (0.00 sec)
i voila, u bazi vise nije smeće nego validan sadržaj. Ako se sad vratimo na aplikaciju, na webu se sad vide neke nove kuke i kvake:
?�?�?�? dasdad ?�?�?�? ?��?�?�?� ??????????? ���������� ?????????????
sve što treba da uradimo je da setujemo pravilno enkoding u aplikaciji
< !DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <body> <form action="djubre.php" method="post"> <input name="q" type="text" /> <input type="submit" /> </form> < ?php mysql_connect(':/tmp/mysql.sock', 'root', '') or die(mysql_error()); mysql_select_db('test') or die (mysql_error());
$res = mysql_query(„set names ‘utf8′“);
$res = mysql_query('select djubre from smece'); while ($row=mysql_fetch_row($res)) echo $row[0].'<br/>'; @mysql_free_result($res); if ( isset( $_POST['q'] ) ) { $djubre = $_POST['q']; mysql_query("insert into smece (djubre) values ('$djubre')"); mysql_close(); } ?> </body> </html>
i problem rešen.
E ovo ti je do jaja post da znas to me muci odavno… nego hajmo opet back to bazis posto smo mi poceli da pravimo svoje satove davnig 1999, 2002, 2004… pa upgrade radili a utf8mb4 se ‘sire’ pojavio kasnije imamo starih tabela koje su utf8mb4 COLLATE=utf8mb4_unicode_520_ci; i sada bi sve da prepakujemo u utf8… kako da uradimo to a da nemamo smece u bazi?
Posebno me zanima to zbog ruskog i slicnih jezika jer imam upravo to ako gledam odakle god smetlje je u bazi samo sto na sajtu ‘izgleda OK’
isto je – nema razlike, ako si imao tabelu na 3.x mysql-u koji nije imao karakter set podrsku i provugao je sve do 5.5 i sada imas unutra smece …
cilj je da u polju imas pravilno rasporedjene bajtove – jedan po jedan, za validnu utf8mb4 nisku.
U slucaju da si kroz utf8mb4 COLLATE=utf8mb4_unicode_520_ci; konekciju tukao utf8mb4 karaktere u utf8mb4 polje problem je sto sada u tom polju nemas pravilnu nisku bajtova „fizicki u polju“ koja cini utf8mb4 vec imas za svaki utf8mb4 bajt 3 neka bajta koja ga pretstavljaju. Zato konvertujes to polje iz utf8mb4 u utf8mb4 COLLATE=utf8mb4_unicode_520_ci;
ovaj korak:
alter table smece change djubre djubre varchar(100) character set utf8mb4 COLLATE=utf8mb4_unicode_520_ci;
i sada posle tog koraka imas fizicki u samom polju pravilnu nisku bajtova koja je validan utf8mb4 string.
Ako si ti imao 3.x pa 4.x pa 5.x i sada ti je to utf8mb4 COLLATE=utf8mb4_unicode_520_ci; tabela (dakle nisi je prebacivao u utf8, nego si je ostavio default) ti se vec nalazis na ovoj poziciji kada imas validnu nisku bajtova u polju.
sada uradis:
alter table smece change djubre djubre blob;
ovde mysql bukvalno samo iskopira tu nisku bajtova takve kakvi su u blob i ne pokusava da ih interpretira uopste.
i na kraju:
alter table smece change djubre djubre varchar(100) character set utf8;
sada kazes mysql-u da procita tu nisku bajtova iz blob-a i interpretira ih kao utf8mb4 nisku.
Dakle cela poenta je da dodjes u situaciju da je u polju niska bajtova validan utf8mb4 izraz, onda bacis to u binarni pa u utf8mb4 i sredio si podatke. Taj prvi korak je najvazniji posto ako si recimo ti tukao sa LATIN2 enkodingom data u bazu onda prvi korak nije
alter table smece change djubre djubre varchar(100) character set utf8mb4 COLLATE=utf8mb4_unicode_520_ci;
vec prvi korak treba da bude:
alter table smece change djubre djubre varchar(100) character set latin2;
dakle cela fora je da se napravi niska bajtova u polju da bude ista kao ono sto si ti utukao kroz konekciju.
jasno?
Више пута сам имао прилике да се са овим проблемом сретнем, и ово је изузетно ризично радити. Наиме, приликом оваквих пребацивања, у зависности од разноразних комбинација кодних распореда колоне, табеле, базе, везе, распореда планета и сличних фактора, редовно се дешава да се нешто забрља. Проблем праве бајтови у УТФ-8 тексту који су уствари контролни карактери у ИСО8859-1 а што резултује у разним занимљивим и мање занимљивим појавама…
Углавном, пре сваке операције ове врсте, бекап.
Bekap je majka, naravno… no sto se „brljanja“ tice, brljanje se desava uglavnom samo ako se ne pogodi enkoding, ili sto je vrlo cest slucaj na zalost, kada je pola tabele jedan enkoding a druga polovina drugi (npr pola tabele je utf8mb4 COLLATE=utf8mb4_unicode_520_ci; – default, a onda se neko napravio pametan i zbog nase latinice dodao latin2) tu onda nastaje haos koji ne moze lako da se pocisti…
Ono sto je tu vazno za primetiti je da ti problemi koji mogu da se jave u 99.9% slucajeva postoje vec i u trenutnom sistemu, dakle i u trenutnom sistemu se vide kao kuke i kvake.
Hvala za info, Kecmane :)
Nema na cemu, izgleda da je ova tema trebala ranije, ja sve mislim da ljudi znaju posto je standardan zez al .. ima vise hitova na ovu stranu u zadnjih 24h nego na ceo blog zadnjih 7 dana :D
Ono presipanje iz utf8mb4 COLLATE=utf8mb4_unicode_520_ci; u utf8mb4 je zaista zgodno :)
Hvala za tip!
Prije svega, pun pogodak za clanak, hvala ti.
Kada pravim pretragu u mysql-u (…LIKE ‘%nešto%’…), a dobijem rezultat u kojem se nalazi „nesto“ i „nešto“, da li je to znak „smeca“ u bazi.
ne, naprotiv, to znaci da je mysql prepoznao š kao validan karakter. jednacenje izmedju š i s radi kolacija.. ide uskoro jedan clanak na tu temu, samo da uvatim 10min vremena
Ovaj problem se pojavio u novijim verzijama e107 CMS-a, ali nije otklonjen više od godinu dana, iz meni nepoznatih razloga.
Pokušavao sam da se izborim sa ovim nekoliko dana, dok nisam došao do Vašeg članka koji tako jednostavno objašnjava problem, kao i rešenje. Obavestio sam razvojni tim e107 o propustu, a da li će, i kad će reagovati, ostaje da se vidi.
Hvala za trud!
Drasko, nemam pojma sta je e107 cms ali drago mi je da ce clanak pomoci (doduse vidim da je clanak dobio ukupno jedan glas i to negativan, al sta je tu je)
Богдане, да ли би могао да ми помогнеш око практично истог проблема? Наиме, када унесем текст ћирилицом у базу користећи PHPMyAdmin, текст је на страници приказан са упитницима, док ако преко сајта унесем податке у исту базу, користећи PHPMyAdmin могу да видим само неке чудне знакове типа аÑдаÑдаÑ. Текст који је тако унет када се прикаже на страници нормално изгледа. Подесио сам encoding и collation на utf_8_general_ci, storage engine је MYISAM.
da li iz php-a kada napravis konekciju radis „SET NAMES“ ??
pogledaj boldovan deo poslednjeg primera
$res = mysql_query(„set names ‘utf8′“);
dakle moras da setujes konekciju na utf8mb4 (sa set names) u php-u da bi ti to radilo kako treba
Пробао сам и то али нема промене. Ево и кода:
<?php
echo "“; echo „“; echo „“;
echo „“;
$result = mysql_query(„SET NAMES ‘utf-8′“);
$result = mysql_query(„SELECT * from boki“);
//While there are rows to display
while($row = mysql_fetch_array($result)){
//Display the results from the current row and a line break
echo $row[‘prezime’] . “ – “ . $row[‘ime’] . „“;
}
echo „“;
?>
Испробао сам код који си ти оставио. Он ради међутим када кликнем на дугме submit рачунар нешто ради неколико секунди и онда се појави прозор где пише Apache HTTP Server has encountered a problem and needs to close… Након тога када урадим Refresh подаци које сам проследио се појављују на екрану. Ако одем у PHPMyAdmin могу нормално да видим податке које сам унео.
Уколико те мрзи да ми објашњаваш постави неки линк где бих могао да погледам како да решим овај проблем.
bez tvog koda ti pricam napament, ali samo nadji gde radis mysql_connect(…) i odma posle toga uradi:
mysql_query(„SET NAMES ‘utf-8′“);
(navodnik, apostrof, apostrof, navodnik – iz nekog razloga wordpress is prebacuje u prednji/zadnji navodnik i neke cudne apostrofe – nemoj dakle copy/paste nego prekucaj)
javi dal radi
Нема промене, али како објашњаваш то да када сам испробао твој код прекине се веза са сервером а подаци се уписују у базу?
Отклонио сам овај проблем са падом сервера када сам обрисао команду:
mysql_close();
Сада мислим да неће бити проблема јер твој скрипт ради како треба а ја ћу се потрудити да прилагодим мој код.
Хвала.
hm … treba ti mysql_close() na kraju skripta .. e sad ako radis neke includeove moras da pazis da ne zatvoris konekciju pre nego su svi zavrsili sa njom ..
dodatno sa php-om postoji smor sa persistentnim konekcijama, za pocetak – savet je da ne koristis persistentne konekcije
Радим са „инклудовима“ али због чега ми је потребан mysql_close() на крају скрипта и шта су то персистентне конекције?
mysql_close() je potreban da bi explicitno zatvorio konekciju, ako implicitno zatvaras konekcije (tako sto samo zavrsis php skript i ocekujes da se konekcija samozatvori) onda se na mysql-u gomilaju konekcije posto budu zatvorene tek kada istekne neki timeout … dakle trosis resurse na serveru
persistentne konekcije je kada koristis mysql_pconnect ili kada u php.ini stavis da ti tretira obicne konekcije kao persistent (da se mysql_connect tretira kao mysql_pconnect – ne secam se koji je parametar), u tom slucaju kada uradis mysql_close() ta konekcija se ne zatvara „stvarno“ nego sledeci put kada uradis mysql_connect() dobijes neku od tih „zatvorenih“ konekcija – ovo moze da dovede do mnogo problema ako ne znas „tacno“ sta radis
Одличан текст. Много ми је помогло.
Такође бих препоручио следећи линк као обавезни штиво на тему енкодинга:
http://www.joelonsoftware.com/articles/Unicode.html
Ima mnogo tekstova, dobrih i losih, o UNICODE-u kao takvom, ovaj bas i nema nikakve veze sa MySQL-om (niti nekim drugim RDBMS-om)
mnogo mi pomoglo ovo upustvo, u suštini baza mi je bio vec u utf8_general_ci nego zaboravio sam kod queryja da ubacim mysql_query(“set names ‘utf8′”);. JOS JEDNOM HVALA!
Ne znam da li je ova tema jos aktivna, nadam se da jeste.
Radio sam kao sto je navedeno u primeru ali kad uradim select * from smece dobijam neke znakove pitanja umesto slova č,ć,ž,ž.
Dok vi dobijate:
mysql> select * from smece;
+—-+——————————————————————-+
| id | djubre |
+—-+——————————————————————-+
| 1 | ÄÅ¡ÄÅ¡ÄÅ¡Ä |
| 2 | dasdad |
| 3 | ÄÅ¡ÄÅ¡ÄÅ¡Ä |
| 4 | ÄÅ¡Å¡ÄÅ¡ÄÅ¡ÄÅ¡ |
| 5 | ÄÄÄÄÄÄÄÄÄÄÄ |
| 6 | Å¡Å¡Å¡Å¡Å¡Å¡Å¡Å¡Å¡Å¡ |
| 7 | ÄÄÄÄÄÄÄÄÄÄÄÄÄ |
Da li možda znate u čemu može biti problem?
zavisi od tvog klijenta da li ume i sta da prikaze kako treba. Ako tvoj klijent ne ume pravilno da se konektuje na bazu ili ako nije utf ili … zgodno je recimo da koristis phpmyadmin on se konektuje kako treba, ili recimo workbench
Moj problem je vrlo slican ovome, na wamp serveru. Zapravo, sve je ok u phpMyAdmin-u (gde su i unete vrednosti) a na sajtu se pojavljuju znakovi pitanja. Rešenje je bilo u izmeni my.ini fajla:
[client]
default-character-set=utf8
[mysql]
default-character-set=utf8
[mysqld]
collation-server = utf8_unicode_ci
init-connect=’SET NAMES utf8′
character-set-server = utf8
To je EXTREMNO POGRESAN nacin da se resi taj problem!!!
Ovo radi jedino što kod mene odseče tekst iza naših slova dok su još đubre. Recimo PALAČINKE pri konverziji napiše PALA. MySQL server koji koristim je 5.5. Ne znam zašto to radi ali sam zadovoljan i sa takvim rešenjem posle UPDATE popravim odsečeno. Hvala Bogdane na ovom korisnom postu…
ne bi smeo da sece, bice da si nesto uradio lose sa klijentske strane
Pozdrav, odličan post! :)
Ja imam problem da sam podesio bazu na utf8_general_ci, prikazivanje stranice na utf8mb4 preko metataga, a konekcija je definisana na sledeći način:
$db = new PDO(„mysql:host=$host;dbname=$txt_db_name;charset=utf8“, „$user“, „$password“, array(PDO::ATTR_EMULATE_PREPARES => false,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => true));
Kada unesem ćirilične podatke oni su na sajtu dobro prikazani, ali u bazi preko phpmyadmina dobijam аÑдаÑдаÑ. Ako ručno ispravim podatke u bazi („zašto bi to iko radio“ metod) na sajtu dobijem upitnike ?????. Probao sam gore opisani metod da konvertujem u BLOB pa u utf8_general_ci ali sam opet dobio smeće. Gdje bi mogao biti problem?
izvrstan post, svaka cast.