Hallo!
Dieses Tutorial soll nicht als Ersatz für das bestehende BB-Code-Tutorial dienen, sondern lediglich eine stetig auftauchende Problematik im Bereich der BB-Codes behandelt und einen Lösungsansatz bietet. Deswegen werde ich hier auch nicht weiter auf alle möglichen BB-Codes eingehen, die man nutzen könnte, sonder - damit es schön einfach ist - lediglich ein Beispiel anhand des 'b'-BB-Code-Tags ansprechen. Der Quelltext kann logischerweise an andere BB-Codes angepasst werden bzw. auch verallgemeinert werden, sollte dies nötig sein. Die Problematik, die ich meine: Verschachtelte BB-Codes.
Es ist leider so, dass mit dem normalen Befehl:
PHP-Code:
<?php
/* ... */
$text = preg_replace ('/\[b\](.*)\[\/b\]/Us', '[b]\\1[/b]', $text);
/* ... */
?>
bei folgendem Inhalt von $text:
Code:
Dies ist ein Test mit verschachtelten BB-Codes.
das Ergebnis so aussieht:
Code:
Dies ist ein Test mit verschachtelten BB-Codes.
Ansich ist das vielleicht nicht immer ein Problem, aber man kann diese Problematik der fehlerhaften BB-Codes leicht umgehen. Mich persönlich störte diese Problematik insbesondere bei fehlerhaft verschachtelten '[quote]'-Tags, da hier dann der ohnehin schon fehlerhaft zitierte Text noch mehr "verstümmelt" wird. Dabei kann es dann schnell mal vorkommen, dass bei unachtsamen Usern das Zitat falsch gedeutet wird.
Wie umgeht man dieses Problem also nun? Durch das 'U' (Dieses hier: '.../Us') ist gewährleistet, dass dieser Regex Ungreedy arbeitet, also das nächste vorkommende '' nutzen will und nicht, wie es standard wäre, das letzte, das er findet. Aber: Er soll auch das zugehörige 'b'-BB-Code-Tag verwenden und nicht das erste, was in dem genannten Beispiel zur entsprechend fehlerhaften ausgabe führte. Wenn wir also folgendes Ergebnis haben wollen:
Code:
Dies ist ein Test mit verschachtelten BB-Codes.
PHP-Code:
<?php
/* ... */
$text = preg_replace ('/\[(b)(\:[\d]+)\](.*)\[\/\1\2\]/Us', '[b]\\3<b>
Müssen wir den Text vorher parsen und hinterher den BB-Code erst ersetzen. Es ist mit Regex möglich Backreferences zu setzen, die es uns erlauben, die zugehörigen BB-Codes zu ermitteln:
</b>', $text);
/* ... */
?>
In diesem Beispiel habe ich unter anderem auch das zweite 'b' durch Backreferences ersetzt. Das '\1' setzt in das '[/]' also das 'b' ein -> '' und durch das '\2' bekommen wir in den Tag noch die zugehörige Nummer '[/b:123]'.
Haben wir nun also vorliegenden Code:
Code:
[b:1]Dies ist ein Test [b:2]mit verschachtelten BB-Codes[/b:2].
wird durch unseren Regex das gewünschte Ergebnis erziehen:
Code:
Dies ist ein Test mit verschachtelten BB-Codes.
Aber: Wie bekommen wir jetzt möglichst unproblematisch die Zahlen eingefügt? Eigentlich ganz einfach. Wir lesen erstmal alles an BB-Codes aus, was vorhanden ist:
PHP-Code:
<?php
/* ... */
$bbcode = array ();
if (preg_match_all ('/(?:\[(\/)?(b)\])/s', $text, $vars) > 0)
{
/* Folgt gleich weiter unten im Tutorial (FOR-Schleife) */
}
/* ... */
?>
Das Array $bbcode benötigen wir gleich für die Abarbeiung von BB-Codes.
Mit unserem Beispiel erhalten wir nun 3 Ergebnisse des Regex': 'b'-BB-Code-Tag, 'b'-BB-Code-Tag und '/b'-BB-Code-Tag. Nun muss nurnoch das entsprechende Ergebnis ausgewertet werden:
PHP-Code:
<?php
/* ... */
for ($i = 0;
$i < count ($vars[0]);
$i++)
{
if (empty ($vars[1][$i]) === TRUE)
{
if (isset ($bbcode[$vars[2][$i]]) === FALSE)
{
$bbcode[$vars[2][$i]] = 0;
}
$bbcode[$vars[2][$i]]++;
}
$text = preg_replace ('/'.preg_quote ($vars[0][$i], '/').'/s', '['.$vars[1][$i].$vars[2][$i].':'.$bbcode[$vars[2][$i]].$vars[3][$i].']', $text, 1);
if ($vars[1][$i] == '/'
&& $bbcode[$vars[2][$i]] > 0)
{
$bbcode[$vars[2][$i]]--;
}
}
$text = preg_replace ('/(?:\[(\/)?(b)\:0\])/s', '[\\1\\2]', $text);
/* ... */
?>
Wie man hier unschwer erkennen kann, wird in dem Array der entsprechende BB-Code hoch bzw. runtergezählt, jenachdem, ob ein BB-Code-Tag geöffnet (erhöht) oder geschlossen (verringert) wird. Wenn bei 0 angekommen wird, wird nicht weiter runter gezählt. Alle BB-Code-Tags, die ein ':0' besitzen sind somit automatisch überflüssig und werden durch das letzte 'preg_replace' wieder in ihre Ausgangsposition versetzt (ohne ':0'). Es ist jedoch notwendig, vorher das ':0' einzufügen, da die erste 'preg_replace'-Funktion das erste Vorkommen des Suchstrings ersetzt und es sonst zu fehlern führt, wenn wir z.B. folgenden Text haben:
Code:
[b:1]Dies ist ein Test [/b:1]mit fehlerhaften BB-Codes[/b:1] [b:1]die zur Falschausgabe führen
Sowas wollen wir natürlich auch vermeiden, deswegen sieht nach der FOR-Schleife unser Text auch so aus:
Code:
[b:1]Dies ist ein Test [/b:1]mit fehlerhaften BB-Codes[/b:0] [b:1]die zur Falschausgabe führen[/b:1].
und nach der Anwendung des letzten 'preg_replace' so:
Code:
[b:1]Dies ist ein Test [/b:1]mit fehlerhaften BB-Codes
[/b]
Code:
[b:1]die zur Falschausgabe führen[/b:1].
Wir haben also unser Ziel erreicht und mittels:
PHP-Code:
<?php
/* ... */
unset ($temp);
while ($temp != $text)
{
$temp = $text;
$text = preg_replace ('/\[(b)(\:[\d]+)\](.*?)\[\/\1\2\]/s', '[b]\\3[/b]', $text);
}
/* ... */
?>
erhalten wir das gewünschte Ergebnis.
Ich hoffe dass mancher dieses Tutorial als sinnvoll erachtet, der sich an der selben Problematik gestört fühlt, wie ich
MfG
MNG