Probleme mit Seilers BBCode-Parser-Klasse in Verbindung mit Namespaces

Der BB-Code-Parser von Christian Seiler dürfte wahrscheinlich jedem hier ein Begriff sein. Es ist eine sehr flexible Klasse, die BBCodes (wie man sie von PHPBB kennt) ermöglicht. Auch Syntax-Fehler kann die Klasse zuverlässig beheben. Das größte Problem des BBCode-Parsers ist, dass es 1. PHP4-Code ist und dass auch die Dokumentation nicht auf Probleme eingeht, die mit der Umstellung auf PHP5 und Namespaces zu tun haben.

Nach mehreren Stunden des Rätselns stelle ich euch Lösungsmöglichkeiten vor, wie ihr die Parserklasse in Verbindung mit den PHP5-Features und Namespaces nutzen könnt. Doch zuvor möchte ich Euch das eigentliche Problem schildern.

Das Problem: Callbacks und Namespaces

Die Parserklasse bietet die Möglichkeit, eigene BBCodes zu erstellen und diese individuell parsen zu lassen. In meinem Fall waren das die Tags IMG, URL und COLOR. Dazu muss man einfach eine Callback-Funktion implementieren, die das ganze dann verarbeitet. Um die Callback-Funktion überhaupt nutzen zu können, ist folgendes nötig

$parser->addCode ('img', 'usecontent', 'do_bbcode_img', array (), 'image', array ('listitem', 'block', 'inline', 'link'), array ());

In diesem Fall ist do_bbcode_img die Callback-Funktion. Intern wird in der Parserklasse call_user_func aufgerufen, die dann do_bbcode_img aufruft. Die Funktion do_bbcode_img sieht bei mir in etwa so aus:

function do_bbcode_img ($action, $attributes, $content, $params, $node_object) {
 if ($action == 'validate') {
 if (substr ($content, 0, 5) == 'data:' || substr ($content, 0, 5) == 'file:'
 || substr ($content, 0, 11) == 'javascript:' || substr ($content, 0, 4) == 'jar:') {
 return false;
 }
 return true;
 }
 return '<img src="'.htmlspecialchars($content).'" alt="Vom User eingefügtes Bild">';
 }

Wie man sieht, ist das eine globale Funktion, die zu keiner Klasse gehört. Das Problem: Nutzt man Namespaces, gehört die Funktion zum aktuellen Namespace! Und da der BBParser nicht dazu gehört, gibt es ein Problem.

In meinem Fall existiert eine Klasse namens BBParser, die ich als ViewHelper nutzen kann.  Sie wandelt mir meine Codes um und kümmert sich zusätzlich um die Smilies. Meine Klasse nutzt intern den BBParser von Christian Seiler:


$this->parser = new \StringParser_BBCode();

Der Backslash \ vor dem Klassennamen besagt, dass die Klasse dem globalen Namensraum angehört. Das ist so, weil Christian Seiler PHP4 nutzt und es da noch keine Namespaces gab. Würde \ fehlen, dann würde PHP die Klasse im aktuellen Namespace suchen. Das wäre in meinem Fall der Namespace, der meine ViewHelpers beinhaltet. Und dann kommt es natürlich zu der Fehlermeldung

Fatal error: Class ‚GEHEIM\ViewHelper\StringParser_BBCode‘ not found in C:\xampp\htdocs\mvc\projekte\GEHEIM\classes\GEHEIM\ViewHelper\BBParser.php on line 12

Dadurch dass die Klasse von Christian Seiler keine Namespaces nutzt und somit global ist, ist auch der call_user_func-Aufruf global. Meine do_bbcode_img-Funktion gehört jedoch auch dem View-Helper-Namespace an. Ich hoffe, das Problem ist jetzt ausreichend erläutert. Jetzt kommen wir zu den verschiedenen Lösungsmöglichkeiten.

Lösung 1: Die callback-Funktionen werden ebenfalls global gemacht

Das wäre eine der einfachsten Lösungen, wenn auch die wohl hässlichste. Man müsste die Funktionen einfach in einen globalen Bereich auslagern, z.b. in die index.php, die ja meistens keinem Namespace angehört. Jetzt wäre aber der Code über mehrere Dateien verteilt, die eigentlich nichts miteinander zu tun haben sollten. Deswegen scheidet diese Lösung für mich schonmal aus.

Lösung 2: Die BBCode-Klasse wird in den richtigen Namespace verlagert

Hierzu schreibt man in die beiden Dateien des Parsers (stringparser.class.php und stringparser_bbcode.class.php) in die erste Zeile folgendes:

 namespace MeinNamespace;

MeinNamespace muss natürlich mit dem entsprechenden Namespace ersetzt werden. Bei mir wäre das ViewHelper. Diese Lösung funktioniert, ist aber genauso hässlich wie die erste. Man muss Fremdcode anpassen. Was passiert, wenn ein Update der Klasse kommt? Dann muss wieder alles angepasst werden. Außerdem könnte man die Klasse dann immer nur aus genau einem Namespace heraus benutzen. Das widerspricht schon einem grundlegenden Prinzip der OOP: Wiederverwendbarkeit.

Für mich kommt auch diese Lösung nicht in Frage. Zum Glück waren das aber noch nicht alle Möglichkeiten!

Lösung 3: Anpassen des Callback-Funktionsnamens

Oben habe ich geschrieben, wie man eigene Codes hinzufügt. Über die Zeile

$parser->addCode ('img', 'usecontent', 'do_bbcode_img', array (), 'image', array ('listitem', 'block', 'inline', 'link'), array ());

werden IMG-Tags ermöglicht. Hier würde do_bbcode_img im globalen Namespace aufgerufen werden. Passen wir die Zeile aber folgendermaßen an, haben wir eine schöne Lösung:

$parser->addCode ('url', 'usecontent?',  __NAMESPACE__.'\\do_bbcode_img', array ('usecontent_param' => 'default'), 'link', array ('listitem', 'block', 'inline'), array ('link'));

Es wird nun do_bbcode_img im aktuellen Namespace ausgeführt. Das ist genau das, was wir wollen! Dennoch geht es noch ein bisschen eleganter. Kommen wir zu Lösung 4!

Lösung 4: Wie 3, nur besser

Wenn wir schon objektorientiert programmieren, können wir auch statische Methoden in den Klassen benutzen. Meine BBParser-ViewHelper-Klasse hat folgendes spendiert bekommen:

public static function do_bbcode_img ($action, $attributes, $content, $params, $node_object) {
 if ($action == 'validate') {
 if (substr ($content, 0, 5) == 'data:' || substr ($content, 0, 5) == 'file:'
 || substr ($content, 0, 11) == 'javascript:' || substr ($content, 0, 4) == 'jar:') {
 return false;
 }
 return true;
 }
 return '<img src="'.htmlspecialchars($content).'" alt="Vom User eingefügtes Bild">';
 }

Die Funktion gehört nun zu der Klasse und kann dennoch von call_user_func aufgerufen werden. Jetzt muss nur noch der Aufruf der addCode-Methode angepasst werden:

$this->parser->addCode ('img', 'usecontent', __NAMESPACE__.'\\BBParser::do_bbcode_img', array (), 'image', array ('listitem', 'block', 'inline', 'link'), array ());

Diese Lösung gefällt mir persönlich am besten und wird jetzt auch so bei mir verwendet.

Ich hoffe, ich konnte euch weiterhelfen!

1 Star2 Stars3 Stars4 Stars5 Stars (Wurde noch nicht bewertet)
Loading...


Ein Kommentar zu “Probleme mit Seilers BBCode-Parser-Klasse in Verbindung mit Namespaces”

  1. Hallo! Vielen Dank für den Artikel. Habe selbst gerne die Methode 3 gewählt. Methode 4 ist aber doch einleuchtender 😉

Hinterlasse einen Kommentar!

Schreibe einen Kommentar

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

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

»Informationen zum Artikel

Autor: Simon
Datum: 05.12.2010
Zeit: 14:53 Uhr
Kategorien: Fehlermeldungen, OOP & Design Patterns, Wissenswertes
Gelesen: 9547x heute: 4x

Kommentare: RSS 2.0.
Diesen Artikel kommentieren oder einen Trackback senden.

»Meta