Fatal Error durch meinen Normalizer-Code

Es fing ganz unscheinbar an. Eine Benachrichtigung von WordPress.org, über einen neuen Kommentar an einem Trac-Ticket.

Aber das Ticket sprach davon, dass es einen Fatal Error geben würde, wenn die intl-Extension nicht installiert sei. Doof nur, dass der Code dafür ursprünglich von mir stammt. Das konnte doch nicht wirklich sein? Also schnell mal mit recherchieren, woran das liegt …

Der Kommentar lautete wie folgt:

Hi, I just wanted to leave a comment here that it seems some people are having problems with not having Normalizer class available in their environment, which is problematic for some plugins that call remove_accents more often (like WooCommerce, ref issue https://github.com/woocommerce/woocommerce/issues/35471).

Besides, there are some core workflows that may end up with a fatal error as well. E.g. when the admin tries to create a new user with accented characters, sanitize_user calls remove_accents and if they don’t have Normalizer, it will throw a fatal.

I suppose it should be possible to fail more gracefully, or only use Normalizer if get_loaded_extensions includes intl?

Der Code hat so einen Check in Form eines function_exists-Check jedoch schon:

		// Unicode sequence normalization from NFD (Normalization Form Decomposed)
		// to NFC (Normalization Form [Pre]Composed), the encoding used in this function.
		if ( function_exists( 'normalizer_normalize' ) ) {
			if ( ! normalizer_is_normalized( $string, Normalizer::FORM_C ) ) {
				$string = normalizer_normalize( $string, Normalizer::FORM_C );
			}
		}

Das Issue auf Github enthält das Backtrace und dies startete mit folgender Zeile:
Fatal error: Uncaught Error: Class "Normalizer" not found in /home/konezovd/public_html/wp-includes/formatting.php:1605

Zeile 1605 entsprach also der Zeile if ( ! normalizer_is_normalized( $string, Normalizer::FORM_C ) ) { – nur bedeutete dies, dass die function_exists-Prüfung ein true ergeben haben musste. Wie konnte das sein? Denn der Issue-Reporter hatte die intl-Extension eben genau nicht installiert auf dem Server.

Ich kam auf die gleiche Idee wie der Trac-Kommentator Peter Fabian und überlegte, was diesen Fall erzeugen konnte und kam auf die Idee eines Polyfill. Also schaute ich nach, ob es Plugins gibt, die eine so benannte Funktion definiert haben. Uns siehe da, es gab 241 Plugins, die das taten. Hauptsächlich Plugins, die den Symfony-Polyfill nutzten. Und ganz oben in der Liste war “Updraft Plus”. Da war doch was! Genau. In dem Issue wurden auch vorbildlich die aktiven Plugins aufgeführt (ein gutes Beispiel, warum das eine gute Idee ist). Denn in dieser Liste war auch Updraft Plus zu finden. Nun musste ich nur noch 1 und 1 zusammenzählen.

Sergey Biryukov gab dann die technische Erklärung:

It appears that the plugin polyfills the normalizer_normalize() function, but the Normalizer class has a different namespace there, p\Normalizer, so Normalizer::FORM_C produces a fatal error.

Der Funktionsaufruf war also gar nicht das Problem, sondern die Definition der Normalisation-Form (hier NFC) über die Definition einer Konstante in der Klasse. Da der Namespace nicht passte, konnte nicht darauf zugegriffen werden, aber die Funktion wurden trotzdem als vorhanden erkannt. So entstand der Fehler.

Da das Ticket ja in einem beendeten Milestone lag, wurde für den Fix ein neues Ticket eröffnet. Die Lösung war dann noch einfacher als gedacht. Die Definition von NFC als Normalisierungsform wäre gar nicht nötig gewesen, da sie sowieso die Standardeinstellung ist.

Jetzt muss diese Theorie nur nochmal getestet werden. Also erst einmal das Problem reproduzieren. Was gar nicht so einfach ist, denn die meisten Hoster folgen der Empfehlung des Hosting-Teams und haben ihr PHP bereits mit der --enable-intl Option kompiliert.

Faszinierend wie der Fehler durch ein ungünstiges Zusammenspiel von eigentlich sinnvollen Funktionen zustande kam. Weder Core- noch Plugin-Entwickler:innen konnten das einfach testen und erahnen, oder? Falls du eine Idee hast, wie solche Probleme erkannt werden können, freue ich mich über einen Kommentar!

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert