bb code library - create your own bbcode system
Posted: Thu Oct 28, 2010 8:25 pm
Update: This project has been redone and renamed and now resides in the CryoBB thread
Preface
This was, and is, a particularly fun little project of mine. I've always found string manipulation to be fun and there's nothing better in string manipulation than bb code systems (or maybe template engines). So, I am developing this library to allow developers to write a bb code system where they define their tags and define those tags' replacements - all without even creating a complex parser!
Features
- Create your own BB code style tags and define the HTML replacements for them.
- Allows tags with parameters.. eg [ quote="name" ] text [ /quote ]
- Allows text between tags to be used as a parameter.. eg [ url ] http://www.example.com [ /url ]
- Allows for text between tags to not be parsed for bb code style tags, useful for tags such as [ code ] text [ /code ]
- Allows functions to be called on the text between tags
- Correctly parses nested bb code style tags of the same kind (assuming input string is correctly nested, see Todo)
- Correctly parses nested bb code style tags of any kind (assuming input string is correctly nested, see Todo)
Todo
- Create a single regex to match all BB code style tags rather than recursively calling preg_match_all()
- Iterate through the string to be parsed and check for (and fix) incorrect nesting prior to parsing
- Cleanse parameters for URLs, or allow a custom cleansing function to be called on any parameter.
- Add more error checking and verbosity
- Feature enhancement: allow dev to specify placeholder macro
- Feature: new lines to paragraphs/br
Code
bb.class.php
bb_tag.class.php
Usage
To demonstrate the usage of this we're going to start with a sample string of bb code that we will create tags for to match and parse all of the bb code tags it contains. [left]this is left[/left] 1[sub]2[/sub]3[sup]4[/sup]5 [center]this is center[/center] [right]this is right[/right] [silly]this is silly[/silly][/code]
The above string contains the bb code tags b, u, i, img, quote, url, code, php, left, sub, sup, center, right, and silly.
We will use the above library to create our own bb code parser and definition system.
First, the classes are needed
Second, the bb code tags need to be defined.
The following code will create and define the b, u, i, and img tags. This is done by creating a new object of BB_Tag and defining it's replacements for the opening and closing tag.
Tags with parameters such as quote and img are done as follows:
$tag->hasParameter() is called and then has two definitions in setReplacements(). the first array will be used if the optional parameter isn't used (eg [ quote ]text[ /quote ]). The second array will be used if the parameter is present, and the {placeholder} text will be replaced with the parameter (eg [ quote="name" ]text[ /quote ]).
If the parameter can be found inside the tags such as with URL, the useTextAsParameter() method is called.
Next up, the code tag is unique because we don't want to parse anything inside of it, including bb code tags, so we use the parseContents() method with a parameter of false
Next up, we will apply a function to the php tag so that it highlights the code inside. Applying a function is as easy as supplying the function name to apply and the parameters to call it with.
You can call built in functions, user created functions, or pass an instance of an object and a method name.
e.g. $tag->applyFunction('functionName');
$tag->applyFunction('functionName', array('param1', 'param2'));
$tag->applyFunction(array($object, 'method'), array('param1');
Next up is the sub, sup, center, left, right, and silly tags.. just to show how easy it is to create and define and parse any tag.
That's it. All of the tags are created and defined. Now all that's left is to create our BB object and attach the tags to it.
And finally, parse!
That turns the sample string above into the following HTML:
Or here is a screenshot of it so you can see it in action 
http://i53.tinypic.com/1y0shg.jpg
So, in short, this will allow devs to develop their own bb code system just by creating and defining the tags they want to use.. be it the normal bb code tags or any cool creative ones they come up with.
Critique
1) What are your thoughts on this project?
2) Critique the code itself and how the BB and BB_Tag objects interact
3) The {placeholder} macro for tags with a parameter really bothers me, but I guess a macro of some sort is needed?
3) Suggest features or improvements not listed in the todo section
4) Other thoughts?
Preface
This was, and is, a particularly fun little project of mine. I've always found string manipulation to be fun and there's nothing better in string manipulation than bb code systems (or maybe template engines). So, I am developing this library to allow developers to write a bb code system where they define their tags and define those tags' replacements - all without even creating a complex parser!
Features
- Create your own BB code style tags and define the HTML replacements for them.
- Allows tags with parameters.. eg [ quote="name" ] text [ /quote ]
- Allows text between tags to be used as a parameter.. eg [ url ] http://www.example.com [ /url ]
- Allows for text between tags to not be parsed for bb code style tags, useful for tags such as [ code ] text [ /code ]
- Allows functions to be called on the text between tags
- Correctly parses nested bb code style tags of the same kind (assuming input string is correctly nested, see Todo)
- Correctly parses nested bb code style tags of any kind (assuming input string is correctly nested, see Todo)
Todo
- Create a single regex to match all BB code style tags rather than recursively calling preg_match_all()
- Iterate through the string to be parsed and check for (and fix) incorrect nesting prior to parsing
- Cleanse parameters for URLs, or allow a custom cleansing function to be called on any parameter.
- Add more error checking and verbosity
- Feature enhancement: allow dev to specify placeholder macro
- Feature: new lines to paragraphs/br
Code
bb.class.php
Code: Select all
<?php
/**
* This package allows one to create their own bb code tags and define the
* replacements for them.
*
* Package: BB
* File: bb.class.php
* Version: 1.0.0
* Author: Scott Martin <sjm.dev1[at]gmail[dot]com>
* Date: October 28, 2010
* License: GNU GPL 3.0
*
* Features:
* - Create your own BB code style tags and define the HTML replacements for
* them.
* - Allows tags with parameters.. eg [ quote="name" ] text [ /quote ]
* - Allows text between tags to be used as a parameter..
* eg [ url ] http://www.example.com [ /url ]
* - Allows for text between tags to not be parsed for bb code style tags,
* useful for tags such as [ code ] text [ /code ]
* - Allows functions to be called on the text between tags
* - Correctly parses nested bb code style tags of the same kind (assuming
* input string is correctly nested, see Todo)
* - Correctly parses nested bb code style tags of any kind (assuming input
* string is correctly nested, see Todo)
*
* Todo:
* - Create a single regex to match all BB code style tags rather than
* recursively calling preg_match_all()
* - Iterate through the string to be parsed and check for (and fix)
* incorrect nesting prior to parsing
* - Cleanse parameters for URLs, or allow a custom cleansing function to be
* called on any parameter.
* - Add more error checking and verbosity
*
<BB creation and definition system>
Copyright (C) <2010> <Scott Martin>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
class BB
{
//string holder
private $str = '';
//bbtags holder
private $tags = array();
/**
* Converts allowed BB code tags to HTML.
*
* @access public
* @param string $str
* @return string
*/
public function make($str)
{
$this->str = $str;
if ($matches = $this->pregMatchRecursive($str))
{
foreach ($matches AS $match)
{
$this->makeTag($match);
}
}
return $this->str;
}
/**
* Recursively parses $str for bb code tags. This function would be entirely
* unnecessary if I could write a good enough regex.
*
* @access private
* @param string $str
* @return mixed - array of matches or boolean false when no matches are found
*/
private function pregMatchRecursive($str)
{
$ret = array();
if ((strpos($str, '[') !== false) && (strpos($str, ']') !== false))
{
if (preg_match_all('#\[(\w+)(=.+?)?\]((?:[^[]|\[(?!/?\\1(=.+?)?\])|(?R))+)\[/\\1\]#s', $str, $matches, PREG_SET_ORDER))
{
$ret = array_merge($ret, $matches);
foreach ($matches AS $match)
{
$match[1] = strtolower($match[1]);
if (array_key_exists($match[1], $this->tags))
{
if ($this->tags[$match[1]]->parseContents)
{
if ($newMatches = $this->pregMatchRecursive($match[3]))
{
$ret = array_merge($ret, $newMatches);
}
}
}
}
return $ret;
}
}
return false;
}
/**
* Adds a BB code tag to the list of allowed tags.
*
* @access public
* @param object $tag - object of type BB_Tag
* @return void
*/
public function addTag($tag)
{
$this->tags[$tag->tag] = $tag;
}
/**
* Does the replacing of BB code tags to their HTML counterparts.. or
* manipulation of content between BB code tags, depending on the tags'
* parameters.
*
* @access private
* @param array $match
* @return void
*/
private function makeTag($match)
{
//apply function to matched content
if (count($this->tags[$match[1]]->applyFunction))
{
$params = $match[3];
if (!empty($this->tags[$match[1]]->applyFunction[1]))
{
$params = $this->tags[$match[1]]->applyFunction[1];
array_unshift($params, $match[3]);
}
$match[3] = call_user_func_array($this->tags[$match[1]]->applyFunction[0], $params);
}
//simple tag (e.g. [b]text[/b])
if (!$this->tags[$match[1]]->parameter)
{
$this->str = str_replace(
$match[0],
$this->tags[$match[1]]->replacements[0] . $match[3] . $this->tags[$match[1]]->replacements[1],
$this->str
);
return;
}
//tags with parameter (e.g. [quote]text[/quote] or [quote="name"]text[/quote])
if ($this->tags[$match[1]]->parameter == 1)
{
if (empty($match[2]) === true)
{
if (empty($this->tags[$match[1]]->replacements[0]) !== true)
{
if ($this->tags[$match[1]]->useTextAsParameter)
{
$this->str = str_replace(
$match[0],
str_replace(
'{placeholder}',
$match[3],
$this->tags[$match[1]]->replacements[0][0]
) . $match[3] . $this->tags[$match[1]]->replacements[0][1],
$this->str
);
} else
{
$this->str = str_replace(
$match[0],
$this->tags[$match[1]]->replacements[0][0] . $match[3] . $this->tags[$match[1]]->replacements[0][1],
$this->str
);
}
}
} else
{
//remove = and "" or '' in parameter supplied
$match[2] = substr($match[2], 1);
if ((substr($match[2], 0, 1) == '"' && substr($match[2], -1) == '"')|| (substr($match[2], 0, 1) == '\'' && substr($match[2], -1) == '\''))
{
$match[2] = substr(substr($match[2], 0, strlen($match[2]) - 1), 1);
}
$this->str = str_replace(
$match[0],
str_replace(
'{placeholder}',
$match[2],
$this->tags[$match[1]]->replacements[1][0]
) . $match[3] . $this->tags[$match[1]]->replacements[1][1],
$this->str
);
}
return;
}
}
}Code: Select all
<?php
/**
* This package allows one to create their own bb code tags and define the
* replacements for them.
*
* Package: BB
* File: bb_tag.class.php
* Version: 1.0.0
* Author: Scott Martin <sjm.dev1[at]gmail[dot]com>
* Date: October 28, 2010
*
* Features:
* - Create your own BB code style tags and define the HTML replacements for
* them.
* - Allows tags with parameters.. eg [ quote="name" ] text [ /quote ]
* - Allows text between tags to be used as a parameter..
* eg [ url ] http://www.example.com [ /url ]
* - Allows for text between tags to not be parsed for bb code style tags,
* useful for tags such as [ code ] text [ /code ]
* - Allows functions to be called on the text between tags
* - Correctly parses nested bb code style tags of the same kind (assuming
* input string is correctly nested, see Todo)
* - Correctly parses nested bb code style tags of any kind (assuming input
* string is correctly nested, see Todo)
*
* Todo:
* - Create a single regex to match all BB code style tags rather than
* recursively calling preg_match_all()
* - Iterate through the string to be parsed and check for (and fix)
* incorrect nesting prior to parsing
* - Cleanse parameters for URLs, or allow a custom cleansing function to be
* called on any parameter.
* - Add more error checking and verbosity
*
<BB creation and definition system>
Copyright (C) <2010> <Scott Martin>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
class BB_Tag
{
//tag name
private $tag;
//tag has parameter?
private $parameter = false;
//use text between tags as parameter?
private $useTextAsParameter = false;
//html replacements for bb code tags
private $replacements;
//parse the contents between bb code tags for additional bbcode tags?
private $parseContents = true;
//apply a function to contents between bb code tags?
private $applyFunction = array();
/**
* Sets the tag name
*
* @access public
* @param string $tag - the name of the BB code tag
* @return void
*/
public function __construct($tag)
{
$this->tag = $tag;
}
/**
* Sets the HTML replacement tags for the opening and closing BB code tags
*
* @access public
* @param array $replacement
* @return void
*/
public function setReplacements($replacement)
{
$this->replacements = $replacement;
}
/**
* Notifies this object that this bb code tag has a parameter
*
* @access public
* @param void
* @access void
*/
public function hasParameter()
{
$this->parameter = true;
}
/**
* Notifies this object to use the text between the opening and closing BB
* code tags as a parameter.
*
* @access public
* @param void
* @return void
*/
public function useTextAsParameter()
{
$this->useTextAsParameter = true;
}
/**
* Tells this object whether or not to parse the contents between this tags'
* open and close for more BB code tags.
*
* @access public
* @param boolean $bool
* @return void
*/
public function parseContents($bool)
{
$this->parseContents = $bool;
}
/**
* Tells the object to apply a function to the contents between this tags'
* open and close.
*
* @access public
* @param mixed $function - string function name or array($objectInstance, 'methodName')
* @param array $params - parameters to pass to the function
* @return void
*/
public function applyFunction($function, $params=array())
{
$this->applyFunction = array($function, $params);
}
//accessor for object properties
public function __get($var)
{
return $this->$var;
}
}To demonstrate the usage of this we're going to start with a sample string of bb code that we will create tags for to match and parse all of the bb code tags it contains.
Code: Select all
[b]bold[/b] [u]underline[/u] [i]italic[/i] [img]http://www.google.com/images/nav_logo16.png[/img] [b][u][i]all three[/i][/u][/b] [quote="test"]test quote[/quote] [quote]another test quote[/quote] [url]http://www.google.com[/url] [url=http://www.google.com]this is google[/url] [code]y = mx + b;Code: Select all
<?php function test_function($param){ echo "My parameter is: " . $param; } ?>The above string contains the bb code tags b, u, i, img, quote, url, code, php, left, sub, sup, center, right, and silly.
We will use the above library to create our own bb code parser and definition system.
First, the classes are needed
Code: Select all
<?php
require_once 'bb.class.php';
require_once 'bb_tag.class.php';The following code will create and define the b, u, i, and img tags. This is done by creating a new object of BB_Tag and defining it's replacements for the opening and closing tag.
Code: Select all
#create and define simple BB Code tags
$bold = new BB_Tag('b');
$bold->setReplacements(array('<strong>', '</strong>'));
$italic = new BB_Tag('i');
$italic->setReplacements(array('<span style="font-style: italic;">', '</span>'));
$underline = new BB_Tag('u');
$underline->setReplacements(array('<span style="text-decoration: underline;">', '</span>'));
$img = new BB_Tag('img');
$img->setReplacements(array('<img src="', '" alt="Image">'));$tag->hasParameter() is called and then has two definitions in setReplacements(). the first array will be used if the optional parameter isn't used (eg [ quote ]text[ /quote ]). The second array will be used if the parameter is present, and the {placeholder} text will be replaced with the parameter (eg [ quote="name" ]text[ /quote ]).
If the parameter can be found inside the tags such as with URL, the useTextAsParameter() method is called.
Code: Select all
#create and define tags with an optional parameter
$quote = new BB_Tag('quote');
$quote->hasParameter();
$quote->setReplacements(array(
array('<div><strong>Quote:</strong><blockquote>', '</blockquote>'),
array('<div><strong>{placeholder} wrote:</strong><blockquote>', '</blockquote>')
));
$url = new BB_Tag('url');
$url->hasParameter();
$url->useTextAsParameter();
$url->setReplacements(array(
array('<a href="{placeholder}" target="_blank">', '</a>'),
array('<a href="{placeholder}" target="_blank">', '</a>')
));Code: Select all
#create and define a tag that doesn't parse bbcode inside
$code = new BB_Tag('code');
$code->parseContents(false);
$code->setReplacements(array('<pre>', '</pre>'));You can call built in functions, user created functions, or pass an instance of an object and a method name.
e.g. $tag->applyFunction('functionName');
$tag->applyFunction('functionName', array('param1', 'param2'));
$tag->applyFunction(array($object, 'method'), array('param1');
Code: Select all
#create and define a tag that applies a function to the contents
$php = new BB_Tag('php');
$php->parseContents(false);
$php->applyFunction('highlight_string', array(1));
$php->setReplacements(array('<div>', '</div>'));Code: Select all
#create custom tags that nobody else uses.. ok maybe other people do use them ;)
$sub = new BB_Tag('sub');
$sub->setReplacements(array('<sub>', '</sub>'));
$sup = new BB_Tag('sup');
$sup->setReplacements(array('<sup>', '</sup>'));
$center = new BB_Tag('center');
$center->setReplacements(array('<div style="text-align: center;">', '</div>'));
$left = new BB_Tag('left');
$left->setReplacements(array('<div style="text-align: left;">', '</div>'));
$right = new BB_Tag('right');
$right->setReplacements(array('<div style="text-align: right;">', '</div>'));
#create a super silly custom tag
$silly = new BB_Tag('silly');
$silly->setReplacements(array('<h1><blink><span style="color: #f00;">', '</span></blink></h1>'));Code: Select all
#instantiate BB Object
$bb = new BB();
#attach tags
$bb->addTag($bold);
$bb->addTag($italic);
$bb->addTag($underline);
$bb->addTag($img);
$bb->addTag($quote);
$bb->addTag($url);
$bb->addTag($code);
$bb->addTag($php);
$bb->addTag($sub);
$bb->addTag($sup);
$bb->addTag($center);
$bb->addTag($left);
$bb->addTag($right);
$bb->addTag($silly);Code: Select all
#make our string!
echo $bb->make($testString);That turns the sample string above into the following HTML:
Code: Select all
<strong>bold</strong> <span style="text-decoration: underline;">underline</span> <span style="font-style: italic;">italic</span> <img src="http://www.google.com/images/nav_logo16.png" alt="Image"> <strong><span style="text-decoration: underline;"><span style="font-style: italic;">all three</span></span></strong><div><strong>test wrote:</strong><blockquote>test quote</blockquote> <div><strong>Quote:</strong><blockquote>another test quote</blockquote> <a href="http://www.google.com" target="_blank">http://www.google.com</a> <a href="http://www.google.com" target="_blank">this is google</a><pre>y = mx + b;</pre> <div><code><span style="color: #000000">
<span style="color: #0000BB"><?php </span><span style="color: #007700">function </span><span style="color: #0000BB">test_function</span><span style="color: #007700">(</span><span style="color: #0000BB">$param</span><span style="color: #007700">){ echo </span><span style="color: #DD0000">"My parameter is: " </span><span style="color: #007700">. </span><span style="color: #0000BB">$param</span><span style="color: #007700">; } </span><span style="color: #0000BB">?></span>
</span>
</code></div> <div style="text-align: left;">this is left</div>1<sub>2</sub>3<sup>4</sup>5<div style="text-align: center;">this is center</div> <div style="text-align: right;">this is right</div> <h1><blink><span style="color: #f00;">this is silly</span></blink></h1>http://i53.tinypic.com/1y0shg.jpg
So, in short, this will allow devs to develop their own bb code system just by creating and defining the tags they want to use.. be it the normal bb code tags or any cool creative ones they come up with.
Critique
1) What are your thoughts on this project?
2) Critique the code itself and how the BB and BB_Tag objects interact
3) The {placeholder} macro for tags with a parameter really bothers me, but I guess a macro of some sort is needed?
3) Suggest features or improvements not listed in the todo section
4) Other thoughts?