Probleme mit ShortCodes

Seid Wor­d­Press 2.5 bie­tet das Blogging-System sei­nen Erwei­te­run­gen (Plugins) die neue Mög­lich­keit an,  mit so genann­ten Short­Codes Inhal­te ein­fach und dyna­misch zu modi­fi­zie­ren. Dazu muss eine Modifikations-Funktion auf einen ent­spre­chen­den Short­Code regis­triert wer­den, wel­cher dann auto­ma­tisch auf­ge­ru­fen wird. Ein Short­Code kann dabei noch zusätz­li­che Para­me­ter defi­nie­ren, die dann die­ser Funk­ti­on zur Aus­wer­tung durch­ge­reicht wer­den (Bei­spiel mit zwei Para­me­ter: [ShortCode Param1=Wert1 Param2=Wert2]). Eines der berühm­tes­ten WordPress-Erweiterungen, wel­che in der neus­ten Ver­si­on die­se Tech­no­lo­gie ein­setzt, ist die Gallery-Verwaltung Next­GEN Gal­le­ry. Und mit ihrem Ein­satz fällt ein klei­ner Feh­ler in Wor­d­Press auf: Short­Codes kom­bi­niert mit sehr lan­gen Bei­trä­gen füh­ren bei der Anzei­ge ger­ne zu einem lee­ren Ergebnis.

Dies sieht sehr ver­däch­tig nach einem Speicher-Problem aus. Doch wo genau tritt der Feh­ler auf und was kann man dage­gen unter­neh­men? Ein klei­ner Such­trip durch den Quell­code von Wor­d­Press (zum Zeit­punkt die­ses Arti­kels in der Ver­si­on 2.7 ) zeig­te bald, dass der Inhalt einer mei­ner lan­gen Arti­kel in der Funk­ti­on wpautop() in der Datei ./wp-includes/formatting.php ver­lo­ren ging. Und zwar genau in der vor­letz­te Zei­le der Funktion:

$pee = preg_replace('/<p>s*?(' . get_shortcode_regex() . ')s*</p>/s', '$1', $pee); // don't auto-p wrap shortcodes that stand alone

(Wor­d­Press 2.7, Datei formatting.php, Funk­ti­on wpautop(), Zei­le 153)
Die­se Funk­ti­on sorgt dafür, dass vor der Aus­ga­be der Bei­trä­ge Zei­len­um­brü­che in HTML-Zeilenumbrüche (<br />) und HTML-Paragraphen (<p />) umge­wan­delt wer­den. Sie wird vor der Erset­zung der Short­Codes auf­ge­ru­fen. Die letz­te Erset­zung sucht nun allein ste­hen­de Short­Codes und ent­fernt mög­li­che (auto­ma­tisch hin­zu­ge­füg­te) Para­gra­phen um die­se. Die hier auf­ge­ru­fe­ne Metho­de get_shortcode_regex() lie­fert dabei das Such­mus­ter für die defi­nier­ten Short­Codes zurück. Die­se befin­det sich in der Datei ./wp-includes/shortcodes.php ab der Zei­le 165. Der hier zurück gelie­fer­te regu­lä­re Aus­druck sucht nach einem Mus­ter in der fol­gen­den Form:

ShortCodes: Problematischer RegEx

Short­Codes: Pro­ble­ma­ti­scher RegEx

In die­sem Aus­druck wer­den zwei nicht-gierige (non-greedy, lazy) Quan­to­ren ein­ge­setzt. Dies bedeu­tet, es wird sehr viel Back­tracking betrie­ben: die RegEx-Engine über­springt im ers­ten Schritt die Zei­chen, wel­che auf sol­che nicht-gierigen Quan­to­ren pas­sen, da ja an die­ser Stel­le ver­sucht wird, so wenig wie mög­lich ein­zu­fan­gen. Trotz­dem merkt sich die Engi­ne, wo sie not­falls nach­schau­en muss, falls der gan­ze rest­li­che Text so erst­mal nicht in das Such­mus­ter passt. Der “gan­ze Rest” bei gro­ßen Bei­trä­gen mit meh­re­ren nicht-gierigen Quan­to­ren kann ganz schön viel sein, was die His­to­rie ger­ne anwach­sen lässt. Wir dür­fen nicht ver­ges­sen: die RegEx-Engine geht am Ende nach und nach wie­der ein Schritt zurück und prüft erneut nach, ob sie ein Ergeb­nis fin­det (Back­tracking). Um den Auf­wand zu mini­mie­ren, bie­tet es sich an, den zu über­prü­fen­den “Rest” lokal ein­zu­schrän­ken. In unse­rem Fall wird der Zei­chen­be­reich der Para­me­ter­paa­re mit dem End­zei­chen ”]” eines Tags begrenzt. Ändern wir nun den roten Abschnitt (.*?) zu ([^]]*?), tren­nen wir somit die Non-Greedy-Bereiche (Para­me­ter­paa­re und mög­li­che Zwi­schen­in­hal­te) und mini­mie­ren den Auf­wand und somit den Spei­cher­ver­brauch. Unser über­lan­ger Arti­kel müss­te wie­der erscheinen.
Mit einer klei­ne Schön­heits­ope­ra­ti­on kön­nen wir noch die (in mei­nen Augen) unnüt­ze grü­ne, nicht-einfangende Klam­me­rung um den ein­ge­fan­ge­nen optio­na­le Schräg­strich ent­fer­nen: (?:(/))? zu (/)?. Die modi­fi­zier­te Metho­de 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])?';
}

(Wor­d­Press 2.7, Funk­ti­on get_shortcode_regex(), Zei­le 165 in Datei shortcodes.php)
(Pro­blems in Wor­d­Press with long posts and plugins like Next­GEN Gallery)

7

Kommentare

  1. smilkobuta  Januar 25, 2009

    Very Thanks to you!!!
    I final­ly unders­tood the source of this pro­blem and pro­blem itself.
    I can’t read Ger­man but your com­ment image is very easy to understand.
    Dan­ke schön!!

    antworten
  2. smilkobuta  Januar 25, 2009

    I report your code to Wor­d­Press Trac
    http://trac.wordpress.org/ticket/8962

    antworten
  3. Matthias Brusdeylins  Januar 27, 2009

    Thanks for repor­ting this bug to WP Trac. I hope they under­stand, that they have to divi­de the two Non-Greedy-Char-Areas (in our examp­le on the first squa­re bra­cket ”]” — red text). So the engi­ne needs less back­trackings and in this case less memory…
    that’s all… 🙂

    antworten
  4. Matthias Brusdeylins  Juni 16, 2009

    Auch in Wor­d­Press 2.8 ist der Feh­ler wei­ter­hin vorhanden.
    Hier liegt der Speicher-Fresser eben­falls in der nicht-gierigen Klam­me­rung. Die letz­te Zei­le in der Funk­ti­on get_shortcode_regex() lau­tet hier nun korrigiert:
    return '(.?)[('.$tagregexp.')b([^]]*?)(/)?](?:(.+?)[/2])?(.?)';
    Das Ticket ist wei­ter­hin offen:
    http://core.trac.wordpress.org/ticket/8553
    WP 2.9 soll es rich­ten… dabei ist es sooo einfach…

    antworten
  5. Matthias Brusdeylins  November 21, 2009

    In Wor­d­Press 2.8.6 besteht das Pro­blem wei­ter­hin. Der Fix oben (Aus­tausch der RegEx-Zeile) geht aber auch hier und muss nach dem Update wie­der durch­ge­führt werden…

    antworten
  6. Matthias Brusdeylins  Dezember 23, 2009

    Mit Wor­d­Press 2.9 ist die­ses Pro­blem noch grö­ßer gewor­den. Der hier beschrei­be­ne Fix reicht wohl nicht mehr aus. Da unter WP 2.9 auch mein The­me nicht mehr funk­tio­niert (war­um auch immer ?!), wer­de ich mich die­sem The­ma wohl erst ab WP 3.0 anneh­men. Dann mit einem kom­plett über­ar­bei­te­ten Theme.

    antworten

Lassen Sie eine Antwort, um Matthias Brusdeylins
Klicken Sie hier, um die Antwort abzubrechen


Diese Website verwendet Akismet, um Spam zu reduzieren. Erfahre mehr darüber, wie deine Kommentardaten verarbeitet werden.

  1. smilkobuta  Januar 25, 2009