„Hieroglyphen“ nach Update auf PHP 5.6 in Debian Jessie oder Ubuntu 15.04

Natürlich kein Zeichensalat, sondern griechische Buchstaben (via simos.info)
Natürlich keine Hieroglyphen, sondern griechische Buchstaben (via simos.info)

Neu in der Linux-Frischeabteilung: Debian 8 „Jessie“ und Ubuntu 15.04 „Vivid Verved“, beide innerhalb der letzten Tage erschienen. Mit dem Upgrade auf die eine oder andere Distribution landet auch eine neue PHP-Version auf der Platte. Lieferten Ubuntu 14.04 und 14.10 noch PHP 5.5 aus, das schon fast zwei Jahre alte Debian Wheezy sogar noch PHP 5.4, so sind beide Distributionen nun bei PHP 5.6 angelangt. Vor allem Debian Wheezy Jessie wird demnächst sicher auf zahlreichen Servern landen, die im Netz hängen und Websites ausliefern, so dass Admins sich mit den Neuerungen auseinandersetzen müssen. Aber auch auf meinem Ubuntu-Notebook, wo ich immer eine LAMP- (Linux, Apache, MySQL, PHP) Testumgebung mit Kopien von mir betreuter Websites bereithalte, hat sich der PHP-Versionssprung sichtbar ausgewirkt: Alle Legacy-Websites, die noch mit ISO-Kodierung (beispielsweise iso-8859-1 oder das aktualisierte iso-8859-15 für Westeuropa) laufen, zeigen plötzlich „Hieroglyphen“ – genauer gesagt, jenen Zeichensalat, der zustande kommt, wenn ISO-kodierte Umlaute und Sonderzeichen mit UTF-8 dekodiert werden. Dann wird beispielsweise aus „Rücktritt als Präsident“ die Zeile „R�cktritt als Pr�sident“.

Die Dekodierung einer HTML-Seite findet im Browser statt; der sollte sich tunlichst danach richten, was individuell im Kopf des jeweiligen HTML-Dokuments festgelegt wurde, egal ob griechisch, kyrillisch, lateinisch oder eben Unicode. Es gibt mehrere Möglichkeiten, den Zeichensatz zu notieren. Die Zeile <meta charset=“iso-8859-1″ /> im Head-Bereich des HTML-Dokuments weist den Browser beispielsweise an, den Inhalt als ISO-kodiert zu interpretieren.

Wenn aber der Webserver selbst eine Zeichenkodierung mitsendet, dann gehorcht der Browser und ignoriert den auf der HTML-Seite vorgefundene Meta-Tag. Ob dies der Fall ist, lässt sich anhand des HTML-Headers überprüfen, den der Webserver als Antwort schickt. Beim Mitlesen der Kommunikation zwischen Browser und Webserver hilft ein Entwickler-Tool wie Firebug. Dort wechselt man auf den Reiter „Netzwerk“ und lädt die Seite. Ein Klick auf das Plus-Zeichen neben der GET-Anforderung für die Seite listet nun unter „Header“ auf, was vom Browser angefragt und – in diesem Fall wichtiger –  vom Webserver geantwortet wurde.

HTML-Header im Firebug
HTML-Header im Firebug

Aus dem Screenshot ist ersichtlich, dass der Webserver als Content-Type nicht nur den (korrekten) Dokumententyp, nämlich „text/html“, sondern auch den Zeichensatz „UTF8“ mitgeschickt hat, obwohl im HTML-Dokument als Meta-Charset iso-8859-1 notiert ist. Daher also die „Hieroglyphen“.

Der erste Impuls bei der Fehlersuche mag darin bestehen, die Webserver-Konfiguration, bei einem typischen LAMP-System also diejenige des Apachen, zu überprüfen. Dort ist die Direktive AddDefaultCharset dafür verantwortlich, einen Standard-Zeichensatz festzulegen und im Header mitzusenden. Allerdings ist die Zeile „AddDefaultCharset UTF-8“ bei Debian und Ubuntu (zu finden in der Datei /etc/apache2/conf-available/charset.conf) standardmäßig auskommentiert, also unwirksam. Am Apachen kann’s somit in diesem Fall nicht liegen.

„Übeltäter“ ist in Wirklichkeit, wie eingangs schon verraten, PHP, genauer gesagt: die fortgeschrittene Unterstützung von PHP 5.6 für UTF-8. Damit lag es früher im Argen; PHP arbeitete standardmäßig mit dem ISO-Zeichensatz, und viele String-Funktionen waren nicht „Multibyte safe“. Die PHP-Dokumentation verrät zu diesem Thema im Kapitel „Migration von PHP 5.5 auf PHP 5.6“ unter Neue Features:

default_charset wird nun als Standardzeichencodierung für die Funktionen htmlentities(), html_entity_decode() und htmlspecialchars() herangezogen […] Die Standardeinstellung hierfür ist UTF-8

Die angesprochene Direktive default_charset konfiguriert aber nicht nur den Zeichensatz, mit dem PHP intern arbeitet, sondern sorgt auch dafür, dass dieser Standard-Zeichensatz im HTTP-Header mitgesendet wird. In PHP regelt das die Direktive default_charset. Problem identifiziert. Es gibt nun zwei Lösungswege:

Variante 1: Eigenen Header senden
Oftmals ist es ein einfacher Hack, seinen PHP-Code so abzuändern, dass die Anwendung einen eigenen Header mit dem gewünschten Zeichensatz absendet. Normalerweise nimmt PHP dem Programmierer die Arbeit ab und schickt selbst einen Header, bevor die Ausgabe beginnt. Man muss also seinen eigenen Header abfeuern, bevor PHP selbst den eigenen Header senden möchte:

<?php
[...]
header('Content-Type: text/html; charset=ISO-8859-1');
[...]
?>

Falls man Sessions verwendet, muss man aufpassen, dass erst die Session gestartet wird und dann der Header folgt.

Variante 2: Zeichensatz per ini.set ändern

Auch dies ist ein Hack im eigenen PHP-Code, er sollte zu Beginn des Scriptes notiert werden:

<?php
if (FALSE === ini_set('default_charset', 'iso-8895-15')) {
     die('Durfte PHP-Konfiguration nicht modifizieren');
}
[...]
?>

Wenn Sie Ihren Server selbst administrieren, sollte dies funktionieren. Ein fremder Web-Hoster hingegen kann es unterbinden, dass die Standard-PHP-Konfiguration überschrieben wird. In diesem Fall gibt ini_set FALSE zurück und die Ausführung des Scriptes wird in obigem Beispiel mit einer Fehlermeldung abgebrochen.

Variante 3: Die Wurzel des Übels beheben
Um PHP das vorlaute Verhalten komplett abzugewöhnen, muss man an die PHP-Konfigurationsdatei ran; sie ist in PHP 5.6 unter /etc/php5/apache2/php.ini zu finden, wenn PHP standardmäßig als Apache-Modul installiert ist. Seit Ubuntu 16.04, das erstmals PHP 7 installiert, ist die PHP-Version Bestandteil des Pfades zur php.ini. Dort gilt es, die Zeile

default-charset = "UTF-8"

nicht etwa auszukommentieren, so wie sie auch in der Konfigurationsdatei von PHP 5.5 auskommentiert war, sondern alles hinter dem Gleichheitszeichen abzuschneiden:

default-charset =

Nach einem Neustart oder Reload des Apache-Webservers sind die Hieroglyphen verschwunden. Allerdings stellt sich nun die Frage, welche Standardzeichenkodierung PHP verwendet, wenn default_charset auf einen leeren Wert gesetzt wird. Die PHP-Dokumentation gibt da keine Antwort, sondern nur eine Warnung auf den Weg:

default_charset auf einen leeren Wert zu setzen, wird nicht empfohlen.

Mangels klarer Ansage lautet meine Empfehlung, in der php.ini die Direktive internal_encoding auf UTF-8 zu setzen. Auf diese Weise wird wieder klar, mit welchem Zeichensatz PHP intern bei Multibyte-Funktionen arbeitet, und unseren Legacy-Code im Singlebyte-Format iso-8859-1 kümmert das sowieso nicht.

Noch ein Nachsatz: Natürlich ist es so, dass UTF-8 wegen seiner Universalität in jedem Fall als Kodierung vorzuziehen ist. Darum geht es hier aber nicht. Wer noch mit alten, ISO-kodierten Websites arbeiten muss, kommt mit dieser kleinen Bearbeitung der php.ini weiterhin über die Runden.

5 comments on “„Hieroglyphen“ nach Update auf PHP 5.6 in Debian Jessie oder Ubuntu 15.04”

  1. Danke – ich finde, der Webserver sollte nur dann ein Encoding mitgeben, wenn die Webseite das will. DefaultEncoding, egal ob im Apache oder in PHP, bringt bei mir die eine oder die andere Webseite durcheinander, diese Lösung hier hätte ich ohne den Artikel lange gesucht.

    PS, „Vor allem Debian Wheezy wird demnächst“ … soll bestimmt „Jessie“ heißen.

  2. Danke für die klare Anleitung, hat mir sehr geholfen.
    Pfad der php5.ini ist aber beim mir:
    /etc/php5/apache2
    lsb_release -a sagt:
    Distributor ID: Debian
    Description: Debian GNU/Linux 8.8 (jessie)
    Release: 8.8
    Codename: jessie
    Hinweis:
    apache2 nach den Änderungen neu starten.

  3. @Bertram: Danke für den Hinweis, hatte einen Dreher im Pfad zur php.ini – ist jetzt korrigiert. Bei der Gelegenheit auch gleich noch ein Update mit den Pfaden zur php.ini nach dem neuen System, wie es Ubuntu seit 16.04 macht und Debian bestimmt auch in der in Kürze erscheinenden neuen stabilen Version „Stretch“ machen wird.

Schreibe einen Kommentar