Ein Blick auf die Datenbank-Tabellen via phpMyAdmin ist häufig verwirrend für den Endnutzer. MyISAM, InnoDB und dazu noch Abkürzungen wie _ci
oder mb4
bei Kollation und Zeichensatz. Was genau stelle ich da eigentlich ein und was bedeutet das, und was macht WordPress dabei im Hintergrund? In diesem Artikel schaue ich mir das mal genauer an.
Der Auslöser für diese Geschichte ist eine E-Mail-Korrespondenz mit einem Kollegen, der mir mitteilte, dass meine WordPress-Installation bei seiner Migration fehlerhafte Umlaute/Sonderzeichen produzierte und er lieferte folgende Erklärung dafür:
MySQL (und MariaDB) verwenden unter dem Namen UTF-8 leider nur einen UTF-8 Subset. Die komplette UTF-8 Unterstützung gibt es nur bei der Verwendung von „utf8mb4“. Ein schöner Artikel dazu ist hier:
https://www.hydroxi.de/utf8-vs-utf8mb4/
Deshalb arbeiten die meisten WordPress Installationen, auf die ich Zugriff habe mit:
define( 'DB_CHARSET', 'utf8mb4' );
Ich recherchierte daher, ob an dieser These etwas dran ist und schrieb dann folgendes zurück:
Die Erklärung in dem Artikel ist nur leider falsch. Ja, es ist ein Subset, aber das ist kein reparierter Fehler, sondern einfach eine Weiterentwicklung von UTF-8. Bei UTF8MB4 steht das MB4 für Multibyte und eben 4. Für das Speichern der Zeichen werden also statt 3 nun 4 Zeichen benutzt, damit wurden zum einen Japanische/Chinesische Zeichen möglich und Emojis, beides benötigt 4 Bytes.
Hier eine bessere Erklärung vom MySQL-Team direkt:
http://mysqlserverteam.com/mysql-8-0-when-to-use-utf8mb3-over-utf8mb4/
utf8mb4
wird übrigens dann von WordPress automatisch benutzt, wenn der Server beziehungsweise die MySQL/MariaDB-Version, um genau zu sein, dies unterstützt. Egal was in der wp-config.php
steht.
if ( 'utf8' === $charset && $this->has_cap( 'utf8mb4' ) ) { $charset = 'utf8mb4'; } if ( 'utf8mb4' === $charset && ! $this->has_cap( 'utf8mb4' ) ) { $charset = 'utf8'; $collate = str_replace( 'utf8mb4_', 'utf8_', $collate ); }
Siehe: WordPress auf Github
Die Funktion has_cap
checkt dabei die Versionsnummer von MySQL. Da der utf8mb4
-Support mit Version 5.5.3 (bzw. 5.0.9) kam, wird so die Unterstützung zurückgemeldet:
case 'utf8mb4': // @since 4.1.0 if ( version_compare( $version, '5.5.3', '<' ) ) { return false; } if ( $this->use_mysqli ) { $client_version = mysqli_get_client_info(); } else { $client_version = mysql_get_client_info(); } /* * libmysql has supported utf8mb4 since 5.5.3, same as the MySQL server. * mysqlnd has supported utf8mb4 since 5.0.9. */ if ( false !== strpos( $client_version, 'mysqlnd' ) ) { $client_version = preg_replace( '/^\D+([\d.]+).*/', '$1', $client_version ); return version_compare( $client_version, '5.0.9', '>=' ); } else { return version_compare( $client_version, '5.5.3', '>=' ); }
Es ist somit egal, ob in der wp-config.php
utf8
(Kurzform für utf8mb3
) oder utf8mb4
steht, denn der Wert wird je nach Version der Datenbank entsprechend korrigiert.
***
Die faszinierende Geschichte hinter dem MB4-Support in WordPress wird übrigens in diesem Artikel (bzw. dem Video darin) erklärt. Da es hier nicht primär darum geht, erspare ich mir eine eigene Erklärung dazu. Ich kann aber jedem, der sie noch nicht kennt, empfehlen Artikel und Video anzuschauen. Es lohnt sich definitiv!
https://poststatus.com/the-trojan-emoji/
Ich habe viele Jahre WordPress nur genutzt, später angefangen es zu erweitern. Erst durch Themes, später auch durch Plugins. Viel zu spät habe ich angefangen, den Core-Code selbst zu lesen und so meine Informationen zu gewinnen. Und sehr häufig finden sich dort Fehler, aber auch spannende Hinweise.
So geht der obige Code noch weiter:
if ( 'utf8mb4' === $charset ) { // _general_ is outdated, so we can upgrade it to _unicode_, instead. if ( ! $collate || 'utf8_general_ci' === $collate ) { $collate = 'utf8mb4_unicode_ci'; } else { $collate = str_replace( 'utf8_', 'utf8mb4_', $collate ); } }
Und wir erfahren, dass utf8mb4_general_ci
veraltet ist und besser utf8mb4_unicode_ci
benutzt werden sollte. Das _ci
am Ende steht übrigens für case insensitive
, die Groß-/Kleinschreibung wird also ignoriert.
Der Code geht noch weiter und zeigt eine weitere Empfehlung:
// _unicode_520_ is a better collation, we should use that when it's available. if ( $this->has_cap( 'utf8mb4_520' ) && 'utf8mb4_unicode_ci' === $collate ) { $collate = 'utf8mb4_unicode_520_ci'; }
Wenn utf8mb4_unicode_520_ci
vorhanden ist, dann ist diese Kollation zu präferieren. Der Test geht auch hier über die MySQL-Versionsnummer. Ist diese höher oder gleich 5.6 wird diese Kollation unterstützt:
case 'utf8mb4_520': // @since 4.6.0 return version_compare( $version, '5.6', '>=' );
Bei Kollation geht es um Sortierung. Der Unterschied von _general
zu _unicode
ist, dass letzteres komplexere Ersetzungen miteinbezieht (Kombinierte Zeichen, bestimmte Sonderzeichen). Zum Beispiel, dass „ß“ bei Sortierungen „ss“ gleichgesetzt ist.
Durch die komplexere Bearbeitung ist _unicode
jedoch langsamer als _general
. Wenn man auf diese Präzision verzichten kann, ist die Performance ggf. vorzuziehen.
Der Unterschied von _unicode
zu _unicode_520
ist die Versionsnummer der unterstützen Unicode-Variante. _unicode
entspricht 4.0.0. Alle weiteren Varianten enthalten die Versionsnummer im Namen _unicode_520
entspricht dann Versionsnummer 5.2.0.
Nachlesen kann man all dies in der MySQL-Doku:
https://dev.mysql.com/doc/refman/5.7/en/charset-unicode-sets.html
https://dev.mysql.com/doc/refman/5.7/en/charset-collation-names.html
Ich hoffe, dass die Einstellungen nun etwas verständlicher geworden sind und das Lesen des WordPress-Core vielleicht nicht mehr ganz so mystifiziert ist.
Habe ich irgendwo Quatsch geschrieben? Oder hast Du eine Frage zum Thema, die hier nicht abgedeckt ist? Dann schreibt mir gerne einen Kommentar! Ich versuche es dann hier zu beantworten oder mach daraus einen eigenen Artikel, wenn ich das Thema spannend finde. Danke schon mal dafür!