Erst dachte ich, es liegt an mir - ich bin eben kein “Softwareentwickler”, sondern Dilettant (wie ich hoffe, im ursprünglichen Sinne). Nachdem jetzt aber auch ein Projekt wie Serendipity davon “gebissen” wurde, lohnt es sich vielleicht doch, ein paar Worte dazu zu schreiben, die anderen eine lange Google-Recherche ersparen.
Kurzum: Wenn nach einem PHP-Update (bspw. von Debian Squeeze mit PHP 5.3.3 auf Debian Wheezy mit PHP 5.4.35) Webapplikationen plötzlich verrückt spielen und Texte verschwinden, liegt das an einem geänderten Default der PHP-Funktion htmlspecialchars()
.
Die Langfassung
Nach einem Update meines Servers auf das aktuelle Debian Anfang diesen Jahres stellte ich fest, dass u.a. bei votetakers.de manche Teile der Seite einfach verschwunden waren. Eine genauere Betrachtung ergab, dass es sich dabei exakt um solche Teile handelte, die (a) dynamisch generierte Inhalte enthalten, aus denen mit der Funktion htmlspecialchars()
unerwünschter HTML-Code entfernt wird (indem <>
in &lt;&gt;
umgewandelt wird), und bei denen (b) im entsprechenden Text Umlaute vorkamen.
Nach erster Ratlosigkeit stellte ich fest, dass die einzige Änderung an dieser Funktion zwischen PHP 5.3 und PHP 5.4 der geänderte Default für den verwendeten Zeichensatz war. Vollständig besteht der Aufruf von htmlspecialchars()
nämlich nicht nur in der Übergabe des zu konvertierenden Textes in der Art htmlspecialchars('Ich bin ein Tästtext!')
, sondern enthält u.a. auch noch Flags und das Encoding:
string
htmlspecialchars ( string
$string [, int
$flags = ENT_COMPAT | ENT_HTML401
[, string
$encoding = ‘UTF-8’ [, bool $double_encode = true ]]] )
Dabei hat sich der Default von ISO-8859-1 auf UTF-8 geändert:
If omitted, the default value of the encoding varies depending on the PHP version in use. In PHP 5.6 and later, the default_charset configuration option is used as the default value. PHP 5.4 and 5.5 will use UTF-8 as the default. Earlier versions of PHP use ISO-8859-1.
Also habe ich testweise alle Aufrufe von htmlspecialchars()
im Code ergänzt um passende Flags (zwingend, weil der Parameter im Funktionsaufruf vor $encoding
steht und daher mit übergeben werden muss, wenn man $encoding
setzen will) und das Encoding ‘ISO-8859-1’ … und danach tat es wieder.
Die Ursache dafür dürfte wohl darin liegen, dass htmlspecialchars()
sinnigerweise einen leeren String zurückgibt, wenn der übergebene String - im entsprechenden Encoding - unzulässige Zeichen enthält:
If the input string contains an invalid code unit sequence within the given encoding an empty string will be returned, unless either the ENT_IGNORE or ENT_SUBSTITUTE flags are set.
Das führt dann zum “Verschwinden” der entsprechenden Text-Teile in der Ausgabe. Böse Falle.
Gut, man hätte vielleicht den Funktionsaufruf direkt richtig machen können, und warum das Problem überhaupt auftritt, obwohl die Datenbank als Collation “UTF-8’ hat, weiß ich auch nicht - vielleicht weil die Webseiten als Encoding ISO-8859-1 haben? Wie auch immer: so ist das nicht nett. Und noch weniger nett, dass es (vor PHP 5.6) keine Möglichkeit gibt, einen Default zu setzen, so dass man jeden (!) Aufruf von htmlspecialchars()
mit Flags und Encoding “aufpeppen” muss - oder man ersetzt htmlspecialchars()
sinnvollerweise direkt durch eine eigene Funktion, wie auch in einem Kommentar zur PHP-Dokumentation geraten wird.
Nachdem ich im März im Blog von Felix Pfefferkorn auf dieses Problem stieß und jetzt auch die Serendipity-Developer den Code kurz vor dem geplanten Release-Candidate noch einmal umkrempeln mussten, ist es wohl an der Zeit, dazu ein paar Worte zu schreiben - offenbar bin ich wenigstens nicht der einzige Dilettant (im besten Sinne, natürlich ).
Nachwort
Ja, ich stehe dazu: ich hasse die Probleme mit Charset-Konversionen, überall - sowohl das unselige Problem der verschiedenen Zeilenende-Markierung in Windows und Unixen als auch Dinge wie das BOM (“Byte Order Mark”) am Anfang einer Textdatei, die dann durch ältere Software nicht mehr geöffnet werden kann, als auch die ganze unselige Hin-und-Her-Konvertiererei, oft ohne dass man der Datei ansehen könnte, welches Encoding sie denn nun genau hat. Am Ende stehen dann “kaputte” Postings, E-Mails oder Webseiten.
Und ich stehe auch dazu, dass ich den Umgang von PHP (und Apache und …) mit Zeichensätzen, insbesondere das Problem, wie man richtig mit Formulareingaben umgeht, einfach bisher nicht verstehe. Aber dazu wollte ich ohnehin bei Gelegenheit mal etwas schreiben.