Ik heb even wat hulp nodig want kom er niet uit. Heb inmiddels veel hierover gelezen, maar dat helpt me niet.
Heb een tabel aangemaakt in mySQL:
- Collatie: utf8mb4_unicode_ci
- 2 velden met de collatie utf8mb4_unicode_ci
In een formulier heb ik een bootstrap 3 tekstveld en een instantie van CKeditor 4. Doormiddel van een Ajax call zorg ik dat de data wordt opgeslagen in de database:
Wanneer ik het woord ëèn via het tekstveld en eveneens via CKeditor op sla in de database zie ik met PHPmyAdmin het woord als volgt terug in de database:
Tekstveld: ëèn
CKeditor: ëèn
Wanneer ik de data weer open in het formulier met een select opdracht zie ik in de CKeditor wel weer gewoon ëèn staan, maar in het tekstveld zie ik net als rechtstreeks in de database staan ëèn.
1) Hoe zou ik dit in de database moeten zien staan? Als de collatie van zowel de tabel als het veld op utf8mb4_unicode_ci staat zou ik denken gewoon ëèn.
2) Hoe kan het dat het resultaat van het tekstveld en de CKeditor verschillend is.
Mogelijk zijn al je problemen al opgelost indien je direct na het maken van een connectie een character encoding instelt met behulp van set_charset().
Indien je dit repareert heeft dit mogelijk wel implicaties voor data die reeds in je database zit, die is dan mogelijk dubbel geëncodeerd.
1) Hoe zou ik dit in de database moeten zien staan? Als de collatie van zowel de tabel als het veld op utf8mb4_unicode_ci staat zou ik denken gewoon ëèn.
Zoals aangegeven, collatie is iets anders dan character encoding. Indien je phpMyAdmin gebruikt om deze data te bekijken dan zou dit er gewoon goed uit moeten zien, mist dit op de goede manier is ingevoerd en de juiste character encoding staat ingesteld.
2) Hoe kan het dat het resultaat van het tekstveld en de CKeditor verschillend is.
Je connect op twee verschillende plaatsen op twee verschillende manieren met je database. Een manier (phpMyAdmin) is waarschijnlijk goed, en de ander (je eigen code/applicatie) waarschijnlijk niet.
Maar als je dit dus repareert dan ziet nieuw geinserte data er waarschijnlijk overal goed uit, maar eerder geinserte data mogelijk niet.
Nieuw record met deze charset toegevoegd, maar krijg nog steeds te zien: ëèn
Waar krijg je dit te zien, hoe geeft je dit weer, en hoe ziet de rest van dat document er uit? Zitten hier ook meta-tags in? En/of PHP-headers?
Je kunt een hele simpele test uitvoeren om te zien of de tekst "één" juist geëncodeerd is opgeslagen. Voer op deze kolom de HEX()-functie uit in MySQL. Hier zou C3A9C3A96E uit moeten komen. Vervolgens zou je ook aan de PHP-kant de hexadecimale waarde (die verder onafhankelijk is van de gebruikte character encoding) kunnen controleren met behulp van bin2hex(), eventueel in combinatie met strtoupper() zodat je rechtstreeks een vergelijking met de waarde van HEX() kunt doen. Deze zouden beide hetzelfde moeten zijn, anders zijn er vertalingen uitgevoerd.
Overigens, je gebruikte volgens mij utf8mb4 tabellen? Dan zou dat ook in set_charset() moeten staan. utf8 (in mysql) bestrijkt een andere karakter-bereik dan utf8mb4.
edit: uitgaande van je collatie. Voer anders eens een SHOW CREATE TABLE uit op de bewuste tabel, wat voor character encoding wordt daar weergegeven? Mogelijk wijkt de definitie van je tabel af.
In wezen, of liever gezegd, idealiter, zouden alle encoderingen in de pas moeten lopen:
- de definities van de tabellen en kolommen
- de data in deze kolommen zelf
- de set_charset() direct na het maken van de connectie
- headers of meta-tags in het document (dit zou UTF-8 moeten zijn)
En collation boeit eigenlijk niet zoveel op dit moment.
Dank je wel Thomas voor je reactie. Ik ga dit uitproberen. Ik ken de HEX functie niet, was al even op zoek, maar lukt niet om deze uit te voeren in PHPmyAdmin.
SELECT HEX(colname); Wanneer ik hier de kolomnaam opgeef krijg ik een foutmelding. Kan ik hier ook WHERE toepassen?
Gebruik inderdaad utf8mb4. De pagina die ik uitvoer om het resultaat te bekijken is een combinatie van HTML en PHP. Helemaal bovenin heb ik de meta-tag staan:
Ik dacht dat het voldeed als ik als meta-tag UTF-8 aangaf, ook wanneer ik als collatie utf8mb4 gebruik. Is het beter op tabel en kolom niveau een andere collatie dan utf8mb4 gebruiken?
> SELECT HEX(colname); Wanneer ik hier de kolomnaam opgeef krijg ik een foutmelding. Kan ik hier ook WHERE toepassen?
Uiteraard, SELECT HEX(<kolomnaam>) FROM <tabelnaam> zou moeten werken.
En als je een specifiek record zoekt met een id dan kun je hier een WHERE-conditie aan hangen.
> Ik dacht dat het voldeed als ik als meta-tag UTF-8 aangaf, ook wanneer ik als collatie utf8mb4 gebruik
Nee, je moet nog steeds set_charset() gebruiken, anders gebruikt MySQL wellicht standaard latin1, en dan gaan er dingen mis. Het hoe en het waarom staat hier (interne link) wellicht nog wat beter uitgelegd. Het komt erop neer dat je zelf verantwoordelijkheid draagt om data op de goede manier aan te leveren, en hierbij is het gewoon het handigste dat je zelf expliciet een character encoding instelt, en niet uitgaat van een (mogelijk afwijkende) default. Dat zou namelijk later, als andere applicaties data uit deze database gebruiken en wel op de goede manier een connectie maken, voor problemen kunnen zorgen.
> Is het beter op tabel en kolom niveau een andere collatie dan utf8mb4 gebruiken?
utf8mb4 is een character encoding, utf8mb4_general_ci is een collation. Dit zijn twee compleet verschillende dingen.
Collation heeft een ander doel dan character encoding, zoals in een bovenstaande link al staat uitgelegd. Tenzij je in deze kolommen zoekt en/of teksten vergelijkt, of dingen sorteert maakt de collation echt niet zoveel uit.
Daarbij kan het best zo zijn dat op database- of tabel-niveau de default collation al utf8mb4_general_ci is, en deze wordt ook geïmpliceerd door de character encoding, want elke encoding heeft een default collation, wat je als volgt kunt zien:
Maar je tabel is utf8mb4, gebruik dan ook utf8mb4 als encoding, en niet utf8. utf8 is ook niet hetzelfde als UTF-8.
Probeer nadat je dit hebt aangepast de tekst eens opnieuw in te voeren. En dan niet via phpMyAdmin, maar via jouw applicatie, en controleer dan de HEX()-waarde opnieuw.
Kijk ook eens aan de clientzijde (PHP) wat er daar van gemaakt wordt m.b.v. strtoupper(hex2bin($row['column']));.
Dit zou hetzelfde moeten zijn als aan de serverzijde.
Wat voor webserver/PHP-versie gebruik je trouwens? Mogelijk voegt je webserver extra headers toe? Kijk eens in je netwerk-tab wat de character encoding van de response is? Anders stel deze expliciet in met behulp van:
Dus:
1. zorg dat alle character encoderingen kloppen en gelijk* lopen;
2. voer dan informatie in via je applicatie;
3. controleer of het resultaat klopt.
Misschien helpt het ook dat je in je formulieren een accept-charset attribuut met als waarde UTF-8 toevoegt.
* gelijk lopen wil zeggen:
- een UTF-8 header middels een PHP-header
- eventueel een meta-tag met UTF-8
- set_charset() icm utf8, utf8mb4, of wat van toepassing is op je database of tabel
- data die ook echt ingevoerd is met bovenstaande instellingen
Maar je tabel is utf8mb4, gebruik dan ook utf8mb4 als encoding, en niet utf8. utf8 is ook niet hetzelfde als UTF-8.
Probeer nadat je dit hebt aangepast de tekst eens opnieuw in te voeren. En dan niet via phpMyAdmin, maar via jouw applicatie, en controleer dan de HEX()-waarde opnieuw.
Volgende gedaan:
mysqli_set_charset($conn,"utf8mb4");
MySQL heeft verschillende smaken voor UTF-8 zoals hierboven al eerder voorbij kwam: utf8, utf8mb4, utf16, utf16le, utf32 et cetera. Deze vormen alle "(deel)implementaties" van de universele standaard genaamd UTF-8.
utf8mb4 is MySQL-specifiek. Dit kun je dus niet instellen in je HTML-document als character encoding!
De huidige meta tag klopt dus niet, en was dus blijkbaar ook totaal niet van invloed op het eindresultaat. Het is (/wordt steeds) waarschijnlijk(er) dat er dus via de webserver een andere standaard character encoding staat ingesteld. Die heb je nu dus blijkbaar expliciet overschreven met je header()-aanroep in PHP.
Léés ook echt wat er staat:
- eventueel een meta-tag met UTF-8
Ik schets een opzet die goed werkt, en probeer te motiveren waarom dit een van de makkelijkere manieren is, maar dit alles komt redelijk nauw. Je kunt niet zomaar strooien met allerlei termen en dan hopen dat het goedkomt.
Wat je dus, nogmaals, ten overvloede, zou moeten controleren, na het (tijdelijk) weghalen van deze header()-call is de character encoding die staat ingesteld in de response. Of raadpleeg de documentatie van je webserver, wat deze ook zou moge zijn, want hier heb je nog steeds niets over verteld.
Het is wat mij betreft echt heel erg belangrijk dat je begrijpt wat er gebeurt en ook wat er misgaat, dit is geen reeks van magische incantaties die je achtereenvolgens aanroept en dat het dan vervolgens miraculeus werkt. Alles heeft een specifieke betekenis en is van invloed op de werking.
Voordat je (grote hoeveelheden) data in je database gaat plempen moet dit alles kloppen en als een zonnetje lopen, want anders moet je straks een héleboel poep gaan schuiven om dit alles weer recht te breien.
Het is ook heeeeeeel belangrijk dat je set_charset() ook echt overal consequent aanroept. Dit dient te gebeuren direct nadat je een connectie maakt, en ook overal waar je een connectie maakt. Dus niet alleen in documenten waar je informatie uit de database ophoest, maar ook en vooral in de fragmenten waar je data naar de database wegschrijft.
Ik geef je nogmaals de link naar een eerdere reactie (interne link) waarin staat uitgelegd dat set_charset() in wezen het contract vormt tussen PHP en de database. Daarin staat beschreven dat jij de verantwoordelijkheid hebt om de data op de juiste manier aan te leveren, en vervolgens doet MySQL haar best om de data in de gewenste vorm terug te geven. Als jij je daar niet aan houdt, of andere regels en best practises voor character encoderingen met voeten treedt, dan kun je op geen redelijke manier verwachten dat dit het gewenste resultaat oplevert. Of als een oud collega van mij placht te zeggen: shit in, shit out.
Misschien ter verduidelijking: in de afgelopen reacties heb ik geprobeerd stap voor stap toe te werken naar het moment dat er dingen misgaan. Als in de database het karakter é de HEX-waarde C3A9 heeft, en als je met PHP deze opgehaalde waarde weergeeft met strtoupper(hex2bin($row['column'])) en ook C3A9 teruggeeft, dan houdt dat dus in dat:
- deze waarde op de juiste manier in de database zit;
- deze waarde op de juiste manier wordt opgehaald (zonder vertalingen tussen encoderingen).
Indien dit karakter vervolgens op de verkeerde wijze wordt weergegeven ligt dit 100% aan de wijze waarop het HTML-document wordt geserveerd.
Immers, de hexadecimale representatie van data staat los van enige character encoding, en dit is dus bij uitstek een geschikt middel om na te gaan waar er dingen misgaan / niet langer kloppen.
Dit schaakspel was dus een (hele) lange stapsgewijze eliminatie van dingen die fout kunnen gaan in dit proces.