Gefällt dir dieser Artikel?

PHP, MySQL und der Ärger mit UTF-8

erschienen in der Kategorie Webdesign, am 28.03.2012
Schnatterente
Immer wieder liest man im Netz von Problemen mit PHP, MySQL und UTF-8. Der Webserver soll Daten aus der MySQL Datenbank lesen und auf der, in UTF-8 codierten, Webseite darstellen. Trotz der Tatsache, dass die Daten auch in der Datenbank im UTF-8 Format gespeichert sind, werden die Umlaute falsch dargestellt.

Das Fragezeichen des Grauens
Tückisch an diesem Problem ist vor allem auch die Tatsache, dass der Fehler auf dem eigenen Rechner (Lokalhost) oft gar nicht auftritt. So kann es vorkommen, dass man in aller Seelenruhe, auf dem lokal gehosteten Webserver, eine Homepage baut, dann irgendwann glaubt fertig zu sein, und die Seite voller Vorfreude auf den Webspace hochlädt. Dachte man bis eben noch endlich fertig zu sein, wird man nun eines Besseren belehrt und mit bösartigen Fragezeichen, statt Umlauten, beworfen.

Des Fehlers Ursprung begründet sich in der Tatsache, dass die meisten deutschen Provider davon ausgehen, dass ihre ebenfalls deutschen Kunden Webseiten bauen, die mit der ISO-8859-1 Codierung arbeiten. Daher haben sie ihre Web- und Datenbankserver so konfiguriert, dass sie mit dieser Codierung optimal klarkommen.

Zur Fehlerbehebung und vor allem zur Fehlervermeidung (für jene, die nicht auf diese Webseite gestoßen sind, weil sie gerade mit dem beschriebenen Problem kämpfen), sei Folgendes empfohlen: Vor dem ersten Zugriff (Query) auf die Datenbank sollte folgende Codezeile ausgeführt werden:
mysqli_query($dbcon, "SET NAMES 'utf8'");

Dies sorgt dafür, dass der Datenbankserver weiß, dass er sowohl bei Datenbankabfragen als auch bei Inserts oder Updates mit UTF-8-codierten Daten arbeiten soll. Der Befehl muss nicht vor jeder Query ausgeführt werden, es reicht, wenn man ihn vor der Ersten ausführt oder am besten direkt, nachdem man die Datenbankverbindung aufgebaut hat.
$dbcon = mysqli_connect($dbhost,$dbuser,$dbpass);
mysqli_select_db($dbcon, $dbname);
mysqli_query($dbcon, "SET NAMES 'utf8'");
...
mysqli_query(...);
...
mysqli_close($dbcon);

In vielen Foren wird, statt dieser Lösung, empfohlen mit den PHP-Funktionen utf8_encode und utf8_decode zu arbeiten, dies halte ich aber für einen ziemlich (wartungs-) aufwendigen Workaround, der das Problem auch nicht wirklich bei der Wurzel packt.

Hier noch ein paar allgemeine Tipps für eine saubere UTF-8 Webseiten-Umsetzung
  • im HTML Head Bereich angeben, dass die Seite UTF-8 kodiert ist:
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  • im PHP Header ebenfalls:
    header("Content-Type: text/html; charset=utf-8");
  • alle PHP Dateien UTF-8 kodiert abspeichern

Tschüssikowski, Fragezeichen des Grauens!

UTF-8 Server-Einstellungen

Update 05.02.2014: Weil diesbezüglich gerade eine Frage per Mail rein kam, hier noch ein paar Infos zur Linux-Server-Konfiguration:

Wer seinen eigenen Webserver betreibt, kann sich die oben genannten Punkte (abgesehen vom HTML-Meta-Tag) sparen, indem er die entsprechenden Einstellungen als Standard festlegt.

Apache-Einstellungen: In der Konfigurationsdatei (in der Regel /etc/apache2/http.conf oder /etc/apache2/apache2.conf) des Apache-Webservers fügt man die folgende Zeile hinzu (oder ändert die Codierung, falls die Zeile schon vorhanden ist):
AddDefaultCharset UTF-8


PHP-Einstellungen: In der Datei php.ini (unter /etc/php5/apache2/ zu finden) ändert man die folgenden Parameter, bzw. fügt sie hinzu, falls noch nicht vorhanden:
default_charset = "UTF-8"
[iconv]
iconv.input_encoding = UTF-8
iconv.internal_encoding = UTF-8
iconv.output_encoding = UTF-8

[exif]
exif.encode_unicode = UTF-8

[mssql]
mssql.charset = "UTF-8"

Falls vor einem Eintrag noch ein Semikolon steht, muss dieses entfernt werden (sonst ist er auskommentiert).

MySQL-Konfiguration: In der der Datei /etc/mysql/my.cnf fügt man folgende Einstellungen ein:
[client]
default-character-set=utf8

[mysql]
default-character-set=utf8

[mysqld]
collation-server = utf8_general_ci
init-connect='SET NAMES utf8'
character-set-server = utf8

Das nimmt einem auch das Ausführen der oben gezeigten Query ab. Somit muss man sich bei der Implementierung keine Gedanken mehr um die Codierung machen.

Nachdem die Änderungen vorgenommen wurden, müssen die betreffenden Server neu gestartet werden (# /etc/init.d/apache2 restart, # /etc/init.d/mysql restart).

Geschnatter

49 Kommentare, selbst mitschnattern << < Seite 5/7 > >>
Marie, am 10.04.2015 um 11:32 Uhr
Bin jetzt erst auf die Seite aufmerksam und bei der Lösung meines Problems fündig geworden.
Eine Frage habe ich aber noch. Bei mir werden Umlaute am Wortanfang (nicht in der Mitte) nicht korrekt dargestellt Das große Ä = "Raute mit Fragezeichen und Anführungszeichen unten"
Woran kann das liegen?
Antwort: Siehe E-Mail. :-)
AWessel, am 16.04.2015 um 10:34 Uhr
Mille grazie!

Musste noch rausfinden, wie ich das PDO verklicker, aber dann hat es wie ein Charm geworked.

(Danke für den Link mit dem Mondwasser. Hab mir direkt zwei Sixpacks bestellt, weil die Fläschchen so super in meine Louis-Vuitton-Tasche passen. Aber alleine schon der Vollmondgeschmack ist jeden Euro wert.
Ist es normal, dass ich nach dem Trinken immer extrem beharrte Arme habe und einen kaum zu bändigenden Appetit auf rohes Fleisch?!)
Antwort: Ganz normal. Ist halt Mondwasser. (Warst du das, der heute Nacht um Zwölf so laut geheult hat?)
Fox, am 16.05.2015 um 21:44 Uhr
Vielen vielen Dank!
Das hat mich aus der Verzeiflung gerettet ;)
Bachsau, am 29.06.2015 um 12:14 Uhr
Das alles wird aber niemandem helfen, solang man das Thema Zeichensatz nicht wirklich verstanden hat. Ist die eigene PHP-Datei nicht in UTF-8 kodiert, oder sind die Daten die man von woanders her an MySQL schickt nicht in UTF-8 kodiert, dann hat man mit diesem Befehl das gleiche Problem wie vorher, nur umgekehrt.
Zaphod, am 18.07.2015 um 12:28 Uhr
Funktioniert nicht mit polnisch z.B. Wyładować
Antwort: Wenn das nicht funktioniert, kann das eigentlich nur daran liegen, dass der String Zeichen enthält, die nicht im gewählten Zeichensatz enthalten sind.
Soll heißen: Check noch mal durch, ob du wirklich überall UTF-8 eingestellt hast und ob dein String in UTF-8 kodiert ist bzw. kodiert werden kann.

(Wenn dein Beispielwort hier richtig angezeigt wird, hast du schon ein Beispiel dafür, dass es funktioniert. Die Seite läuft nämlich auch auf PHP+MySQL mit UTF-8.)
Daniel, am 28.08.2015 um 08:32 Uhr
Hallo,

habe es ausprobiert, das problem ist das mir php jetzt nen parameter Fehler raushaut:

Warning: mysqli_query() expects at least 2 parameters, 1 given in C:\[...]\functions.php on line 31

da mysql mittlerweile veraltet ist, habe ich msqli verwendet.
Antwort:
Hi Daniel, ja, man sollte mysqli- statt mysql-Funktionen verwenden. (Seit PHP 5.5 gelten die mysql-Anweisungen als veraltet.) Der wesentliche Unterschied besteht darin, dass du bei den mysqli-Kommandos immer die jeweilige Datenbank-Connection mit angeben musst. Ich vermute das hast du nicht gemacht und deswegen meckert PHP jetzt auch, dass nur ein Parameter (statt mindestens zwei) übergeben wurde.

Es reicht also nicht, einfach überall "mysql" durch "mysqli" zu ersetzen. Du musst die Befehle entsprechend um die Connection erweitern:
$dbcon = mysqli_connect($dbhost,$dbuser,$dbpass);
mysqli_select_db($dbcon, $dbname);
mysqli_query($dbcon, "SET NAMES 'utf8'");
mysqli_query($dbcon, "SELECT * FROM Beispiel");
...
mysqli_close($dbcon);


Der Beitrag hier war noch aus der Zeit vor PHP 5.5. Ich habe das Beispiel oben jetzt angepasst. Danke für den Hinweis.
Sven, am 10.09.2015 um 10:33 Uhr
Supi! Danke! :)

Vor allem der Tipp, dass alle Dateien im richtigen Zeichensatz abgespeichert werden müssen, hat die Schuppen von den Augen fallen lassen! :D