Seid WordPress 2.5 bietet das Blogging-System seinen Erweiterungen (Plugins) die neue Möglichkeit an, mit so genannten ShortCodes Inhalte einfach und dynamisch zu modifizieren. Dazu muss eine Modifikations-Funktion auf einen entsprechenden ShortCode registriert werden, welcher dann automatisch aufgerufen wird. Ein ShortCode kann dabei noch zusätzliche Parameter definieren, die dann dieser Funktion zur Auswertung durchgereicht werden (Beispiel mit zwei Parameter: [ShortCode Param1=Wert1 Param2=Wert2]
). Eines der berühmtesten WordPress-Erweiterungen, welche in der neusten Version diese Technologie einsetzt, ist die Gallery-Verwaltung NextGEN Gallery. Und mit ihrem Einsatz fällt ein kleiner Fehler in WordPress auf: ShortCodes kombiniert mit sehr langen Beiträgen führen bei der Anzeige gerne zu einem leeren Ergebnis.
Dies sieht sehr verdächtig nach einem Speicher-Problem aus. Doch wo genau tritt der Fehler auf und was kann man dagegen unternehmen? Ein kleiner Suchtrip durch den Quellcode von WordPress (zum Zeitpunkt dieses Artikels in der Version 2.7 ) zeigte bald, dass der Inhalt einer meiner langen Artikel in der Funktion wpautop()
in der Datei ./wp-includes/formatting.php verloren ging. Und zwar genau in der vorletzte Zeile der Funktion:
$pee = preg_replace('/<p>s*?(' . get_shortcode_regex() . ')s*</p>/s', '$1', $pee); // don't auto-p wrap shortcodes that stand alone
(WordPress 2.7, Datei formatting.php, Funktion wpautop()
, Zeile 153)
Diese Funktion sorgt dafür, dass vor der Ausgabe der Beiträge Zeilenumbrüche in HTML-Zeilenumbrüche (<br />
) und HTML-Paragraphen (<p />
) umgewandelt werden. Sie wird vor der Ersetzung der ShortCodes aufgerufen. Die letzte Ersetzung sucht nun allein stehende ShortCodes und entfernt mögliche (automatisch hinzugefügte) Paragraphen um diese. Die hier aufgerufene Methode get_shortcode_regex()
liefert dabei das Suchmuster für die definierten ShortCodes zurück. Diese befindet sich in der Datei ./wp-includes/shortcodes.php ab der Zeile 165. Der hier zurück gelieferte reguläre Ausdruck sucht nach einem Muster in der folgenden Form:
In diesem Ausdruck werden zwei nicht-gierige (non-greedy, lazy) Quantoren eingesetzt. Dies bedeutet, es wird sehr viel Backtracking betrieben: die RegEx-Engine überspringt im ersten Schritt die Zeichen, welche auf solche nicht-gierigen Quantoren passen, da ja an dieser Stelle versucht wird, so wenig wie möglich einzufangen. Trotzdem merkt sich die Engine, wo sie notfalls nachschauen muss, falls der ganze restliche Text so erstmal nicht in das Suchmuster passt. Der “ganze Rest” bei großen Beiträgen mit mehreren nicht-gierigen Quantoren kann ganz schön viel sein, was die Historie gerne anwachsen lässt. Wir dürfen nicht vergessen: die RegEx-Engine geht am Ende nach und nach wieder ein Schritt zurück und prüft erneut nach, ob sie ein Ergebnis findet (Backtracking). Um den Aufwand zu minimieren, bietet es sich an, den zu überprüfenden “Rest” lokal einzuschränken. In unserem Fall wird der Zeichenbereich der Parameterpaare mit dem Endzeichen ”]
” eines Tags begrenzt. Ändern wir nun den roten Abschnitt (.*?)
zu ([^]]*?)
, trennen wir somit die Non-Greedy-Bereiche (Parameterpaare und mögliche Zwischeninhalte) und minimieren den Aufwand und somit den Speicherverbrauch. Unser überlanger Artikel müsste wieder erscheinen.
Mit einer kleine Schönheitsoperation können wir noch die (in meinen Augen) unnütze grüne, nicht-einfangende Klammerung um den eingefangenen optionale Schrägstrich entfernen: (?:(/))?
zu (/)?
. Die modifizierte Methode get_shortcode_regex()
sieht nun wie folgt aus:
function get_shortcode_regex() { global $shortcode_tags; $tagnames = array_keys($shortcode_tags); $tagregexp = join( '|', array_map('preg_quote', $tagnames) ); return '[('.$tagregexp.')b([^]]*?)(/)?](?:(.+?)[/1])?'; }
(WordPress 2.7, Funktion get_shortcode_regex()
, Zeile 165 in Datei shortcodes.php)
(Problems in WordPress with long posts and plugins like NextGEN Gallery)
JAN