BBCode Parser
<?php
/**
* Squiloople Framework
*
* LICENSE: Feel free to use and redistribute this code.
*
* @author Michael Rushton <michael@squiloople.com>
* @link http://squiloople.com/
* @version 1.0
* @category Squiloople
* @package Models
* @subpackage Parsers
* @copyright © 2010 Michael Rushton
*/
// Define the namespace
namespace Models\Parsers;
/**
* BBCode Parser
*
* Parses BBCode in a string
*/
final class BBCodeParser
{
/**
* Array to contain regular expressions of BB tags
*
* @access private
* @var array $_bbTags
*/
private $_bbTags = array(
'/\[b\]([\x20-\x7E]+?)\[\/b\]/i',
'/\[i\]([\x20-\x7E]+?)\[\/i\]/i',
'/\[u\]([\x20-\x7E]+?)\[\/u\]/i',
'/\[s\]([\x20-\x7E]+?)\[\/s\]/i',
'/\[sub\]([\x20-\x7E]+?)\[\/sub\]/i',
'/\[sup\]([\x20-\x7E]+?)\[\/sup\]/i',
'/\[img\]([\x20-\x7E]+?)\[\/img\]/i',
'/\[url\]([\x20-\x7E]+?)\[\/url\]/i',
'/\[email\]([\x20-\x7E]+?)\[\/email\]/i',
'/\[quote\]([\x20-\x7E]+?)\[\/quote\]/i',
'/\[color=([0-9a-f]{6})\]([\x20-\x7E]+?)\[\/color\]/i',
'/\[size=([1-9]?[0-9])\]([\x20-\x7E]+?)\[\/size\]/i',
'/\[font=([a-z\x20]+)\]([\x20-\x7E]+?)\[\/font\]/i',
'/\[img=([\x20-\x7E]+?)\]([\x20-\x7E]+?)\[\/img\]/i',
'/\[url=([\x20-\x5A\x5C\x5E-\x7E]+)\]([\x20-\x7E]+?)\[\/url\]/i',
'/\[email=([\x20-\x5A\x5C\x5E-\x7E]+)\]([\x20-\x7E]+?)\[\/email\]/i',
'/\[quote=([\x20-\x5A\x5C\x5E-\x7E]+)\]([\x20-\x7E]+?)\[\/quote\]/i',
);
/**
* Array to contain HTML tag replacements
*
* @access private
* @var array $_htmlTags
*/
private $_htmlTags = array(
'<strong>$1</strong>',
'<em>$1</em>',
'<span style="text-decoration:underline">$1</span>',
'<del>$1</del>',
'<sub>$1</sub>',
'<sup>$1</sup>',
'<img src="$1" alt="" />',
'<a href="$1">$1</a>',
'<a href="mailto:$1">$1</a>',
'<fieldset>$1</fieldset>',
'<span style="color:#$1;background-color:transparent">$2</span>',
'<span style="font-size:$1px">$2</span>',
'<span style="font-family:\'$1\', sans-serif">$2</span>',
'<img src="$2" alt="$1" />',
'<a href="$1">$2</a>',
'<a href="mailto:$1">$2</a>',
'<fieldset><legend>$1</legend>$2</fieldset>',
);
/**
* Create "[tag]Text[/tag] => <tag>Text</tag>" style BB tags
*
* @access public
* @param string $bbTag
* @param string|bool $htmlTag
* @return array
*/
public function createTag($bbTag, $htmlTag = false)
{
// If an HTML tag is not specified, emulate the BB tag
$htmlTag = $htmlTag ?: $bbTag;
// Create a new BB tag regular expression of the form: [tag]Text[/tag]
$this->_bbTags[] = '/\[' . $bbTag. '\]([\x20-\x7E]+?)\[\/' . $bbTag . '\]/i';
// Create a new HTML tag replacement of the form Text
$this->_htmlTags[] = '<' . $htmlTag . '>$1</' . strtok($htmlTag, ' ') . '>';
// Return an array with the BBCode regular expression and the HTML replacement
return array(end($this->_bbTags), end($this->_htmlTags));
}
/**
* Create "[tag=option]Text[/tag] => <tag option="option">Text</tag>" style BB tags
*
* @access public
* @param string $bbTag
* @param string $htmlTag
* @return array
*/
public function createParameterTag($bbTag, $htmlTag)
{
// Create a new BB tag regular expression of the form [tag=option][/tag]
$this->_bbTags[] = '/\[' . $bbTag . '=([\x20-\x5A\x5C\x5E-\x7E]+)\]([\x20-\x7E]+?)\[\/' . $bbTag . '\]/i';
// Create a new HTML tag replacement of the form Text
$this->_htmlTags[] = '<' . $htmlTag . '>$2</' . strtok($htmlTag, ' ') . '>';
// Return an array with the BBCode regular expression and the HTML replacement
return array(end($this->_bbTags), end($this->_htmlTags));
}
/**
* Create "[smile] => :)" style BB tags
*
* @access public
* @param string $bbTag
* @param string $htmlTag
* @return array
*/
public function createSpecialTag($bbTag, $htmlTag)
{
// Create a new BB tag regular expression of the form [tag] (does not require usual brackets)
$this->_bbTags[] = '/' . preg_quote($bbTag) . '/i';
// Create a new replacement (does not need to be an HTML tag)
$this->_htmlTags[] = $htmlTag;
// Return an array with the BBCode regular expression and the HTML replacement
return array(end($this->_bbTags), end($this->_htmlTags));
}
/**
* Return the parsed string
*
* @access public
* @param string $string
* @return string
*/
public function parseString($string)
{
// Only replace tags if the string has not been fully parsed
for ($count = 1; $count != 0;)
{
$string = preg_replace($this->_bbTags, $this->_htmlTags, $string, -1, $count);
}
// Return the parsed string
return $string;
}
}
To create an instance of the class, simply declare it as normal:
$stringParser = new \Models\Parsers\BBCodeParser;
To parse a string containing BBCode, call the parseString() method passing as the only parameter the string to be parsed:
echo $stringParser->parseString('[bbcode]BBCode[/bbcode]');
To create a new tag, call the createTag() method passing as the first parameter the BB tag to be used (without the square brackets), and as the optional second parameter the HTML tag to replace it (without the angled brackets) if it is not the same as the BBCode tag:
$stringParser->createTag('p');
$stringParser->createTag('d', 'div');
echo $stringParser->parseString('[d][p]Text[/p][/d]');
// <div><p>Text</p></div>
To create a new tag that uses a parameter ([tag=parameter][/tag]), call the createParameterTag() method passing as the first parameter the BB tag to be used (without the square brackets) and as the second parameter the HTML tag to replace it (without the angled brackets) using $1 as a placeholder for the parameter:
$stringParser->createTag('p');
$stringParser->createParameterTag('d', 'div id="$1"');
echo $stringParser->parseString('[d=parameter][p]Text[/p][/d]');
// <div id="parameter"><p>Text</p></div>
To create a new tag that uses a non-containing tag ([smile]), call the createSpecialTag() method passing as the first parameter the BB tag to be used (with the square brackets, if desired) and as the second parameter its replacement (with the angled brackets if desired):
$stringParser->createTag('d', 'div');
$stringParser->createSpecialTag('[smile]', '<img src="smile.png" alt="smile" />');
echo $stringParser->parseString('Look at my smile: [smile]');
// Look at my smile: <img src="smile.png" alt="smile" />
All three of these methods will return an array where the first element is the BBCode regular expression and the second the HTML replacement:
print_r($stringParser->createTag('d', 'div'));
/*
* Array (
* [0] => /\[d\]([\x20-\x7E]+?)\[\/d\]/i
* [1] => <div>$1</div>
* )
*/
The default tags are:
[b]Bold[/b] [i]Italics[/i] [u]Underline[/i] [s]Strikethrough[/s] [sub]Subscript[/sub] [sup]Superscript[/sup] [img]image.png[/img] [url]http://example.com[/url] [email]michael@example.com[/email] [quote]Quote[/quote] [color=red]Red[/color] [size=15]15px[/size] [font=Arial]Arial[/font] [img=alt]smile.png[/img] [url=http://example.com]Example[/url] [email=michael@example.com]Michael[/email] [quote=Michael]Quote[/quote]