Impressum · Kontakt · Hilfe
Besucher online · Mitglieder



Portal > Foren > Ankündigungen, News und Feedback > Tutorials > [php] BBCode-Parser mit [noparse]-Tag selbst gemacht

Layoutprobleme? - Styleswitcher!

Antwort
 
Themen-Optionen
Alt 10.11.2007, 19:44 Nach oben    #1
MrNiceGuy
Lutz
 
Benutzerbild von MrNiceGuy
 
Registriert seit: 14.08.2005
Ort: Nienburg / Weser
Beiträge: 604
Standard [php] BBCode-Parser mit [noparse]-Tag selbst gemacht

[php] BBCode-Parser mit [noparse]-Tag selbst gemacht

Dieses Tutorial zeigt die Programmierung eines eigenen BBCode-Parsers. Grundkenntnisse im Umgang mit Regular Expressions (kurz: RegEx) sind sehr vorteilhaft, um die Funktionsweise nachvollziehen zu können!

Inhaltsverzeichnis

1) Einleitung und Bezug
2) Die Ausnahme "[noparse]"
3) Das Kernstück: Der Reguläre Ausdruck (RegEx)
4) Die Parameter-Zeichenkette nutzbar machen
5) Umstellen der Callback-Funktion
6) Ein paar einfache Beispiele
6.1) Beispiel: Fett, kursiv und unterstrichen
6.2) Beispiel: URLs
7) Die Verwendung optionaler Parameter
8) Nicht existierende BBCodes im Text belassen
9) BBCodes ohne Close-Tag
10) Zusammenfassung
11) Abschließende Worte



1) Einleitung und Bezug
(Zurück zum Inhaltsverzeichnis)

Als Basis für dieses Tutorial benutze ich das Beispiel von php.net:

PHP-Code:
<?php
function functionBBCode ($mixedInfo)
{
  if (
is_array ($mixedInfo) === TRUE)
  {
    
$mixedInfo '<div style="margin-left: 10px">'.$mixedInfo[1].'</div>';
  }

  return 
preg_replace_callback ('°\[indent]((?:[^[]|\[(?!/?indent])|(?R))+)\[/indent]°',
                                
'functionBBCode',
                                
$mixedInfo
                                
);
}
?>
Ich habe das Beispiel bereits ein bisschen angepasst, was die Benennung der Funktion und der Variablen belangt.



2) Die Ausnahme "[noparse]"
(Zurück zum Inhaltsverzeichnis)

Es wird eine Art "Ausnahme" im Script geben undzwar den Tag [noparse]. Dieser dient dazu, alle in ihm enthaltenen BBCode-Elemente nicht zu übersetzen! Der Vorteil ist schlicht und ergreifend der: Sollte man - aus welchen Gründen auch immer - BBCodes im Text als Text selbst anzeigen wollen, umschließt man ihn schlichtweg mit dem [noparse]-Tag:

Code:
[b]Dieser Text wird fett![/b]
würde zur folgenden Ausgabe führen:

Code:
Dieser Text wird fett!
Hingegen führt:

Code:
[noparse][b]Dieser Text wird nicht fett.[/b][/noparse]
zur gewünschten Ausgabe:

Code:
[b]Dieser Text wird nicht fett.[/b]


3) Das Kernstück: Der Reguläre Ausdruck (RegEx)
(Zurück zum Inhaltsverzeichnis)

Als nächstes werde ich den RegEx umschreiben, sodass er nicht nur den BBCode "indent" abarbeitet, sondern alle BBCodes findet, die sich in einem Text befinden:

Code:
°\[(\w+)]((?:[^[]|\[(?!/?\1])|(?R))+)\[/\1
Somit findet der RegEx alle in eckigen Klammern eingeschlossene BBCodes, ganz egal, ob der BBCode überhaupt besteht oder nicht. Sollte z.B. ein [gibbetnich][/gibbetnich]-Paar dabei sein, dann wird es schlichtweg nicht durch etwas anderes ersetzt, obwohl er es als RegEx findet.

Die Suchergebnisse für den RegEx sind nun also der BBCode selbst an der Position 1 (z.B. "b") und der Inhalt des BBCodes an der Position 2 (z.B. "Dies ist einer fetter Text").

Soweit, so gut, wir wollen aber noch eventuell Parameter mit übergeben, was z.B. bei dem URL-Tag praktisch ist (z.B. "Name des Links"). Entsprechend muss der RegEx erweitert werden:

Code:
°\[(\w+)((?:\s|=)[^]]*)?]((?:[^[]|\[(?!/?\1])|(?R))+)\[/\1]°
nun befindet sich an Position 1 immernoch der BBCode selber, an Position 2 jedoch die eventuellen Parameter als zusammenhängende Zeichenkette (z.B. "=http://www.domain.tld") und der Inhalt des BBCodes an der Position 3.



4) Die Parameter-Zeichenkette nutzbar machen
(Zurück zum Inhaltsverzeichnis)

Um die Parameter auch vernünftig benutzen zu können, füge ich folgende Funktion hinzu, die die Parameter-Zeichenkette analysiert und ein Array daraus generiert:

PHP-Code:
<?php
function functionParseParameter ($stringParameter)
{
  
$arrayParameter = array ();

  if (
is_string ($stringParameter) === TRUE
  
AND empty ($stringParameter) === FALSE
      
)
  {
    if (
preg_match_all ('°(^|\w+)\=(\"?)([^\"]*?)\2(?: |$)°',
                        
$stringParameter,
                        
$arrayMatches,
                        
PREG_SET_ORDER
                        
) > 0
        
)
    {
      foreach (
$arrayMatches AS $integerMatchCount => $arrayMatch)
      {
        if (empty (
$arrayMatch[1]) === TRUE)
        {
          
$stringKey '__INIT__';
        }
        else
        {
          
$stringKey strtolower ($arrayMatch[1]);
        }

        
$arrayParameter[$stringKey] = $arrayMatch[3];

        
$arrayParameter[$integerMatchCount] = &$arrayParameter[$stringKey];
      }

      unset (
$arrayMatch);
    }
  }

  return 
$arrayParameter;
}
?>
Sollte ein BBCode z.B. folgendermaßen aussehen: "[url=http://www.domain.tld Title="Link-Titel"]Link[/url]", dann würde die URL im Key "__INIT__" landen und "Link-Titel" im Key "title":

Code:
Array
(
    [__INIT__] => http://www.domain.tld
    [0] => http://www.domain.tld
    [title] => Link-Titel
    [1] => Link-Titel
)


5) Umstellen der Callback-Funktion
(Zurück zum Inhaltsverzeichnis)

Als Nächstes stelle ich die Struktur der Funktion etwas um:

PHP-Code:
<?php
function functionBBCode ($mixedInfo)
{
  
$stringCode '';

  
$arrayParameter = array ();

  
$booleanMixedInfoIsArray FALSE;

  if (
is_array ($mixedInfo) === TRUE
  
AND count ($mixedInfo) == 4
      
)
  {
    
$stringText $mixedInfo[3];

    
$stringCode strtolower ($mixedInfo[1]);

    
$arrayParameter functionParseParameter ($mixedInfo[2]);

    
$booleanMixedInfoIsArray TRUE;
  }
  elseif (
is_string ($mixedInfo) === TRUE)
  {
    
$stringText $mixedInfo;
  }
  else
  {
    return 
'';
  }

  if (
$stringCode != 'noparse')
  {
    
$stringText preg_replace_callback ('°\[(\w+)((?:\s|=)[^]]*)?]((?:[^[]|\[(?!/?\1])|(?R))*)\[/\1]°',
                                         
'functionBBCode',
                                         
$stringText
                                         
);
  }

  if (
$booleanMixedInfoIsArray === TRUE)
  {
    
// Hier kommt die Verarbeitung der BBCodes rein
  
}

  return 
$stringText;
}
?>
Es wird am Anfang nun geprüft, ob es sich bei dem übergebenen Parameter $mixedInfo um ein Array mit 4 Elementen handelt (Im Falle, dass der RegEx erfolgreich angewendet werden konnte), oder ob es sich bei dem Inhalt der Variablen um einen übergebenen String handelt. Der vom BBCode umhüllte Text wird in $stringText, das generierte Array mit den Parametern in $arrayParameter und der BBCode selbst in $stringCode gespeichert. Das Ergebnis wird in $booleanMixedInfoIsArray abgelegt. Ist es nichts von Beidem, wird ein leerer String zurückgegeben.
Sollte der BBCode "noparse" sein, so wird der Text innerhalb dieser Klammer nicht weiter abgearbeitet.
Dort, wo der Kommentar zu sehen ist, werde ich später die Verarbeitung der BBCodes vornehmen.



6) Ein paar einfache Beispiele
(Zurück zum Inhaltsverzeichnis)

Nun werde ich ein paar einfache Beispiele anbringen, wie man verschiedene BBCodes umsetzen kann. Eingesetzt werden muss folgender Code an der Kommentierten Stelle im Script oben.



6.1) Beispiel: Fett, kursiv und unterstrichen
(Zurück zum Inhaltsverzeichnis)

PHP-Code:
<?php
/* [...] Folgender Code steht in der IF-Anweisung beim kommentar im Script oben [...] */
    
switch ($mixedInfo[1])
    {
      case 
'b':
      case 
'i':
      case 
'u':
      {
        
$stringText '<'.$mixedInfo[1].'>'.$stringText.'</'.$mixedInfo[1].'>';

        break;
      }

      
// Hier kommen später weitere CASEs hinein
    
}
/* [...] */
?>
Als Erstes ein Beispiel der bekanntesten und einfachsten BBCodes für die Textformatierung: [b], [i] und [u]. Wie unschwer zu erkennen ist, befindet sich in $stringText der Inhalt der BBCodes (der Text, der zwischen dem Open- und dem Close-Tag steht). Ich habe diese drei Elemente in einer Anweisung verarbeitet, da sie vom Aufbau her gleich sind und es daher unsinnig wäre, 3 gleichklingende CASEs davon zu machen. Ich nutze also das Suchergebnis 1 (den BBCode selbst: "[b]" => "b", etc.), um den HTML-Code zu generieren, der um den Text gelegt werden soll. Schon können einfache BBCodes für Fett, Kursiv und Unterstrichen genutzt werden.



6.2) Beispiel: URLs
(Zurück zum Inhaltsverzeichnis)

Leider sind nicht alle BBCodes so einfach und bedürfen daher etwas mehr Code, um angemessen zu funktionieren. Deshalb hier ein Beispiel für den URL-BBCode (ACHTUNG: Die URL selber wird in diesem Beispiel nicht auf korrekte Schreibweise geprüft, um das Beispiel nicht komplizierter zu machen, als es zwingend nötig ist!) :

PHP-Code:
<?php
/* [...] */
      
case 'url':
      {
        if (
count ($arrayParameter) == 0)
        {
          
$stringText '<a href="'.$stringText.'">'.$stringText.'</a>';
        }
        else
        {
          
$stringText '<a href="'.$arrayParameter['__INIT__'].'">'.$stringText.'</a>';
        }

        break;
      }
/* [...] */
?>
Hier wird nun geprüft, ob im Parameter-Array Elemente vorhanden sind oder nicht. Ist dies nicht der Fall, wird der Text zwischen den Tags als URL angenommen. Sollte jedoch der INIT-Parameter vorhanden sein, wird dieser als URL verwendet und der Text zwischen den Tags als Text für den Link.



7) Die Verwendung optionaler Parameter
(Zurück zum Inhaltsverzeichnis)

Optional könnte man zum Beispiel auch noch eine Möglichkeit einbauen, den TITLE-Parameter übersetzen zu lassen (z.B. "[url=http://www.domain.tld title="Dies ist der Titel des Links"]Link-Text[/url]") :

PHP-Code:
<?php
/* [...] */
      
case 'url':
      {
        if (
count ($arrayParameter) == 0
        
OR  array_key_exists ('__INIT__',
                              
$arrayParameter
                              
) === FALSE
            
)
        {
          
$stringText '<a href="'.$stringText.'">'.$stringText.'</a>';
        }
        else
        {
          
$stringTitle '';

          if (
array_key_exists ('title',
                                
$arrayParameter
                                
) === TRUE
              
)
          {
            
$stringTitle ' title="'.$arrayParameter['title'].'"';
          }

          
$stringText '<a href="'.$arrayParameter['__INIT__'].'"'.$stringTitle.'>'.$stringText.'</a>';

          unset (
$stringTitle);
        }

        break;
      }
/* [...] */
?>
Jeden weiteren BBCode kann man auf diese Weise einfügen und die Daten speziell verarbeiten lassen, wie es der jeweilige BBCode benötigt.



8) Nicht existierende BBCodes im Text belassen
(Zurück zum Inhaltsverzeichnis)

Bislang ist es so, dass alle BBCodes, die nicht verarbeitet werden, schlichtweg gelöscht werden. Sollte man dieses nicht wollen, reicht folgender default-Case:

PHP-Code:
<?php
/* [...] */
      
default:
      {
        
$stringText $mixedInfo[0];

        break;
      }
/* [...] */
?>


9) BBCodes ohne Close-Tag
(Zurück zum Inhaltsverzeichnis)

Ein kleines Problem gibt es aber noch: Es kann BBCodes geben, die keinen Close-Tag haben. Sollte man jemals solche BBCodes haben wollen (z.B. "[br]", "", etc.), muss man vor dem Ersetzen diese Codes noch um einen Close-Tag erweitern. Das geht zum Beispiel mit einem einfachen preg_replace ():

PHP-Code:
<?php
$stringText 
preg_replace ('°\[(br|hr)]°is',
                            
'[\1][/\1]',
                            
$stringText
                            
);
?>
Dies würde alle BR- und HR-Tags mit einem Close-Tag versehen, damit sie vom RegEx gefunden werden können.



10) Zusammenfassung
(Zurück zum Inhaltsverzeichnis)

Im Grunde bin ich kein Freund davon, fertige Scripte zum Download anzubieten, da ich selber die Gefahr sehe, dass dann das Tutorial nicht (oder zumindest nicht richtig) gelesen wird, das Script stumpf kopiert wird und bei Fehlern oder Problemen unnötige Fragen gestellt werden, die sich durch das Lesen des Tutorials erübrigen würden. Dennoch habe ich mich dazu entschlossen den Code zu diesem Script als Datei mit anzubieten. Ich habe allerdings eine Bitte: Bei Problemen und Fehlern bitte erst das Tutorial lesen. Sollte das Problem oder der Fehler nicht beschrieben oder nicht verständlich genug sein, sind Fragen natürlich erwünscht!
Grundsätzlich ist das Script natürlich nicht vollständig, sondern beinhaltet lediglich die in diesem Tutorial angesprochenen Funktionen bzw. BBCodes. Einzig der CASE für [br]- und [hr]-Tags ist hinzugekommen (Diese wurden als Beispiele für BBCodes ohne Close-Tag angesprochen und sind nur aus diesem Grund eingefügt worden).
Zusätzlich hänge ich noch eine Datei an, die ein Beispielscript enthält, mit dem man die Funktionsweise des Scriptes überprüfen kann.

Die Dateien enthalten keine Kommentare, da sich die Funktionsweise aus dem Tutorial selbst ergibt.



11) Abschließende Worte
(Zurück zum Inhaltsverzeichnis)

An dieser Stelle endet dieses kleine Tutorial. Ich hoffe, dass ich - auch wenn ein bisschen Grundwissen in RegEx vorausgesetzt wird - einigermaßen anschaulich erklären konnte, wie man mit Hilfe von RegEx' einen vernünftigen BBCode-Parser bauen kann. Die Art und Weise, wie die Codes ersetzt werden bleibt natürlich jedem selbst überlassen, aufgrund der Fülle möglicher Template-Engines habe ich jedoch darauf verzichtet eine derartige Schnittstelle zu verwenden. Jeder, der gerne seine TE dazu benutzen möchte, die Inhalte in formatiertes HTML zu pressen, möge sich mit der TE seiner Wahl näher auseinander setzen und die Schnittstelle dazu entsprechend selber programmieren.
Ich weise an dieser Stelle noch ein Mal darauf hin, dass ich in meinen Beispielen die Inhalte nicht geprüft habe (das gilt vor Allem für die Parameter!) und dass entsprechende Sicherheitsvorkehrungen von Jedem selbst zu implementieren sind!

Sollten noch Fragen auftauchen, so bin ich gerne bereit meine Hilfe anzubieten, ebenso bin ich für Verbesserungsvorschläge und / oder Kritik (aber bitte nur konstruktive!) offen und dankbar!
Angehängte Dateien
Dateityp: zip bbcode.zip (1,8 KB, 0x aufgerufen)

Geändert von MrNiceGuy (27.04.2008 um 16:44 Uhr).
MrNiceGuy ist offline  
Add Post to del.icio.usBookmark Post in TechnoratiDiesen Beitrag zu Mister Wong hinzufügen!
Mit Zitat antworten
Alt 27.04.2008, 08:26 Nach oben    #2
MrNiceGuy
Lutz
 
Benutzerbild von MrNiceGuy
 
Registriert seit: 14.08.2005
Ort: Nienburg / Weser
Beiträge: 604
Standard

Aufgrund dieser Frage wurde ein Fehler im RegEx behoben.

Geändert von Jann Hendrik (27.04.2008 um 12:14 Uhr).
MrNiceGuy ist offline  
Add Post to del.icio.usBookmark Post in TechnoratiDiesen Beitrag zu Mister Wong hinzufügen!
Mit Zitat antworten
Antwort

« [PHP] Ein eigenes Templatesystem schreiben | - »

Aktive Benutzer in diesem Thema: 1 (Registrierte Benutzer: 0, Gäste: 1)
 
Themen-Optionen

Forumregeln
Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge anzufügen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

vB Code ist An.
Smileys sind An.
[IMG] Code ist An.
HTML-Code ist Aus.
Trackbacks are An
Pingbacks are An
Refbacks are Aus

Ähnliche Themen
Thema Autor Forum Antworten Letzter Beitrag
[PHP] vCard PHP Parser Jann Hendrik Tutorials 0 22.08.2007 10:24
[Java] Parser Generierung mit JavaCC - Eine Einführung pago Tutorials 0 14.04.2006 11:54


Alle Zeitangaben in WEZ +2. Es ist jetzt 21:31 Uhr.

Nach oben
Wir nutzen das Zend Framework, vBulletin (vBulletin v3.6.7, Copyright ©2000-2008, Jelsoft Enterprises Ltd.
SEO by vBSEO 3.0.0) und vBSEO.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44