BBcode-System

Motivation

Das Clansphere CMS hatte ein einfaches BBcode System, das lange Zeit
erfolgreich seine Dienste geleistet hat. Doch inzwischen sind sowohl dem
Team als auch der Community Mängel aufgefallen, die es zu beheben galt:

  • Die Erweiterung des BBcode Systems war nicht ohne Weiteres möglich
    und konnte nur über einen Eingriff in den Kern des Systems erfolgen
  • Das gleichzeitige Verwenden mehrerer BBcode Erweiterungen ließ sich
    nur durch Zusammenfriemeln verschiedener Bruchteile erreichen
  • Das Aktualisieren des Kerns hatte Probleme mit den Erweiterungen zur
    Folge

Diese Probleme haben Laszlo im Namen des Clansphere
Teams dazu veranlasst, das BBcode System neu zu entwickeln.
Objektorientiert und leicht erweiterbar sollte es sein.

Das erste Konzept hat er noch alleine entworfen und sehr einfach
gehalten, doch nachdem Steffen und ich noch einige Ideen mit eingebracht
haben, haben Laszlo und ich uns dazu entschlossen, zusammen noch mal neu
anzufangen.

Insgesamt haben wir fast vier Mal komplett neu begonnen und dabei immer
wieder neue Einfälle gehabt und Erfahrungen gesammelt. Diesen
Entwicklungsprozess durch die vier verschiedenen Versionen möchten wir
euch hier vorstellen. Wen nur das fertige BBcode System interessiert,
kann gleich zu dem fertigen Quellcode
oder zu der Anwendung gehen. Alle anderen starten
bei...

Entwurf I

Laszlo schwebte vor, eine zentrale statische Klasse zum Verwalten der
BBcodes und der BBcode Patterns (ein Pattern ist ein einzelnes
Suchmuster, das ersetzt werden soll) zu erstellen. Die Patterns werden
dabei bei dieser zentralen Klasse registriert, so dass auch Patterns von
ausserhalb des Kerns eingefügt werden konnten.

Nett dabei war die Möglichkeit, verschiedene Replace-Typen angeben zu
können. Neben simplen Funktionsaufrufen standen auch ausführbarer Code
und im Besonderen auch anonyme Funktionen zur Auswahl.

Quellcode von Entwurf I

Entwurf II

Aus einer Diskussion von Steffen und mir kam
hervor, dass bei diesem Entwurf der objektorientierte Ansatz überhaupt
nicht ausgereizt wurde, da die Klasse nur als Namespace diente, statt
Objekte zu instanzieren.

Also hat Laszlo die register_pattern()-Methode in einen Konstruktor
umgebaut. Dadurch wurden auch die Array-Konstruktionen um einiges
vereinfacht. Nun gab es für jedes Suchmuster eine eigene Instanz. Die
verschiedenen Instanzen wurden in einem statischen Array gesammelt, in
dem man sie nun auch gewisser Maßen sortieren konnte, indem man beim
Instanzieren einen zusätzlichen Parameter übergab, um zu bestimmen, ob
das Suchmuster am Anfang oder Ende angewendet werden sollte.

Ein zusätzliches Feature, was durch diesen neuen Ansatz ermöglicht
wurde, war die clear()-Methode, mit der es möglich war, sämtliche
BBcode-Syntax aus einer Zeichenkette zu entfernen, um einen
unformatierten Text zu erhalten.

Quellcode von Entwurf II

Der Ansatz, für jedes Suchmuster eine Instanz zu erstellen, hat gut
funktioniert. Allerdings waren wir nicht damit zufrieden, dass die
Patterns automatisch in ein klasseninternes Array abgelegt werden, da
die Situation eintreten kann, dass man ein Suchmuster anlegen und auf
eine Zeichenkette anwenden möchte, ohne es global für den BBcode zu
registrieren. Es folgte..

Entwurf III

Also wollten wir die statischen Methoden von den Instanz-Methoden
trennen. Zuerst haben wir die vorherige BBcode-Klasse in "Pattern"
umbenannt und alle statischen Methoden und Eigenschaften entfernt. Als
nächstes brauchten wir eine neue Klasse für die statischen Methoden.

Wir hielten es für am Sinnvollsten, wenn jedes BBcode-Addon eine eigene
Klasse hat, die die statischen Methoden und Eigenschaften von einer
BBcode-Klasse erbt. So könnte man verschiedene Addons getrennt
voneinander benutzen und erweitern. Die von Clansphere mitgebrachten
Suchmuster wären damit auch in einer Klasse gruppiert. Da PHP aber erst
ab Version 5.3 statische Vererbung genügend unterstützt, mussten wir auf
die Vererbung verzichten und haben vorerst jede Addon-Klasse die
statischen Funktionen selbst (und damit mehrfach) definieren lassen.

Als nächstes haben wir in diesem Entwurf der Pattern-Klasse zwei neue
Eingeschaften geschenkt: Regex und Level. Mit dem Setzen der
Regex-Eigenschaft konnte man nun festlegen, ob das Suchmuster als
regulärer Ausdruck oder als einfacher String behandelt werden sollte.
Die Level-Eigenschaft war nötig um steuern zu können, wann ein
Suchmuster angewendet werden soll. Beim Anwenden des BBcodes auf eine
Zeichenkette wurden nur die Suchmuster benutzt, die höchstens das Level
hatten, das beim Aufruf der convert-Methode festgelegt wurde.

Die dritte Neuerung bei diesem Entwurf war die Abtrennung des
Secure-Systems, welches HTML-Syntax maskiert, vom Rest des BBcodes.

Quellcode von Entwurf III

Somit haben wir es geschafft, das BBcode System leicht erweiterbar zu
machen und haben den OOP-Ansatz bei den Patterns gut umgesetzt. Der Kern
ist sauber von den eigentlichen Funktionen getrennt und BBcode Addons
lassen sich auch parallel zu den Clansphere BBcodes verwenden, so dass
Addons auch Patterns hinzufügen können, ohne dass diese gleich überall
eingesetzt werden und dennoch über das Addon ansteuerbar sind. Zudem
sind die Funktionen gut in den jeweiligen Klassen gekapselt.

Allerdings ist das System noch nicht sehr flexibel. Unterschiedliche
BBcodes können nur durch Aufrufen verschiedener statischer Klassen
erreicht werden. Einzelne Muster lassen sich noch nicht ansteuern. Diese
überlegungen führten uns zur...

Lösung

Wir fanden es viel logischer, einzelne BBcode-Systeme als Objekte zu
instanzieren, als statische Klassen zu verwenden. Dadurch kann der
jeweilige BBcode überall genau spezifiziert werden.

Deshalb haben wir beschlossen, dass sowohl Clansphere als auch die
Addons ihre Patterns nur dem BBcode-System zur Verfügung stellen, ohne
deren Nutzung regulieren zu können. An den Stellen, an denen der BBcode
verwendet wird, kann man somit die einzelnen Patterns auswählen, die an
genau dieser Stelle verwendet werden. Dadurch hat man die maximale
Flexibilität erreicht.

Allerdings ist auch klar, dass es so jedes Mal ein Akt wäre, das
BBcode-System zu verwenden, weil erst alle Patterns aufgelistet werden
müssten, die unterstützt werden sollen.

Ein weiteres Problem ist die Dynamik. Zwar können so die Patterns
überall manuell ausgewählt werden, allerdings möchte man das oft gar
nicht so genau, da sonst nachträglich hinzugefügte Patterns (zum
Beispiel durch Addons) nirgends verwendet werden würden, wenn man das
Pattern nicht manuell an allen Stellen hinzufügt.

Aus diesen Gründen haben wir uns dazu entschlossen, Pattern-Gruppen
zu erstellen.

Die Patterns werden bei der Anmeldung nun also verschiedenen (auch
mehreren) Gruppen zugeordnet. Beim Instanzieren eines BBcodes gibt man
die Gruppe an, dessen Patterns verwendet werden sollen. Dabei gibt man
aber keineswegs die Flexibilität auf. Man entscheidet sich nicht für
eine fixe Gruppe, sondern die Gruppe bestimmt nur, welche Patterns
anfänglich verwendet werden. Der BBcode kann genauso wie ohne Gruppen
die Patterns einzeln hinzufügen und entfernen.

Der fertige Quellcode

Quellcode der finalen Version

Quellcode des ClansphereBbcode Addons

Ergebnis

Wir haben es geschafft, ein BBcode System zu entwickeln, das

  • durch den zentralen Zugriffspunkt Bbcode::register von überall aus
    leicht erweiterbar ist
  • bei jeder Verwendung bis ins Detail konfigurierbar ist
  • es ermöglicht, jedes Pattern einzeln und komfortabel (mit Namen)
    anzusprechen
  • trotz der Konfigurierbarkeit auch nachträgliches Erweitern gut
    unterstützt
  • sich trotz der Komplexität auch mal schnell und unkompliziert
    verwenden lässt, weil es auf die Gruppen zugreift

Anwendung

Ein paar Beispiele zur Anwendung der
BBcode-Klasse

Download der BBcode-Klasse mit<br />
Beispielen