I'm also adding in emoticon support.
Dang, I'm embarassed to have the original version up there now
I've done so many improvements already but probably won't have the next version done for another week or so.
Moderator: General Moderators
I suppose you could do it like this:josh wrote:This syntax also irks me:
(for example what if I wanted both src="" and alt="" to be sourced from the BB codes)Code: Select all
$img = new BB_Tag('img'); $img->setReplacements(array('<img src="', '" alt="Image">'));
Code: Select all
$img = new BB_Tag('img');
$img->hasParameter();
$img->setReplacements(array(
array('<img src="', '" alt="Image">'),
array('<img alt="{placeholder}" src="', '">')
);If you were not, you'd be doing it wrong (not improving as time moves forward)s.dot wrote:Dang, I'm embarassed to have the original version up there now
How about:s.dot wrote: I suppose you could do it like this:Code: Select all
$img = new BB_Tag('img'); $img->hasParameter(); $img->setReplacements(array( array('<img src="', '" alt="Image">'), array('<img alt="{placeholder}" src="', '">') );
Code: Select all
$img = new BB_Tag('img');
$img->hasParameter();
$img->setReplacements(array(
array('<img src="{img_src}" alt="{alt_txt}">'),
);Code: Select all
$img->setParameters(array('url', 'alt_txt'));
// sprintf like character
$img->setReplacement('<img src="%" alt="%">');
// php template like tags using above names
$img->setReplacement('<img src="{url}" alt="{alt_txt}">') 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 = '<p>' . str_replace("\n\n", "</p>\n\n<p>", $str) . '</p>';
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);
}
$inside = array('', '');
$parse = array('', '');
if (isset($this->tags[$match[1]]) && $this->tags[$match[1]]->inside == false)
{
$inside = array("</p>\n\n", '<p>');
if ($this->tags[$match[1]]->parseContents == true)
{
$parse = array('<p>', "</p>\n\n");
}
}
//simple tag (e.g. <span style="font-weight: bold">text</span>)
if (!$this->tags[$match[1]]->parameter)
{
$this->str = str_replace(
$match[0],
$inside[0] . $this->tags[$match[1]]->replacements[0] . $parse[0] . $match[3] . $parse[1] . $this->tags[$match[1]]->replacements[1] . $inside[1],
$this->str
);
return;
}
//tags with parameter (e.g. <blockquote class="uncited"><div>text</div></blockquote> or <blockquote><div><cite>name wrote:</cite>text</div></blockquote>)
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],
$inside[0] . str_replace(
'{placeholder}',
$match[3],
$this->tags[$match[1]]->replacements[0][0]
) . $parse[0] . $match[3] . $parse[1] . $this->tags[$match[1]]->replacements[0][1] . $inside[1],
$this->str
);
} else
{
$this->str = str_replace(
$match[0],
$inside[0] . $this->tags[$match[1]]->replacements[0][0] . $parse[0] . $match[3] . $parse[1] . $this->tags[$match[1]]->replacements[0][1] . $inside[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],
$inside[0] . str_replace(
'{placeholder}',
$match[2],
$this->tags[$match[1]]->replacements[1][0]
) . $parse[0] . $match[3] . $parse[1] . $this->tags[$match[1]]->replacements[1][1] . $inside[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;
//inside of <p> tag?
private $inside = true;
//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;
}
/**
* Sets whether or not the tag can be inside of a <p> tag
*
* @author Jonah Dahlquint <jonahbron.d[at]gmail[dot]com>
* @access public
* @param boolean $inside
* @return void
*/
public function setInside($inside)
{
$this->inside = (bool) $inside;
}
/**
* 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;
}
}Code: Select all
$bold = new BB_Tag('b');
$bold->setReplacements(array('<strong>', '</strong>'));
$bold->setInside(true);
$div = new BB_Tag('div');
$div->setReplacements(array('<div style="border:solid 1px #000000;">', '</div>'));
$div->setInside(false);Code: Select all
$div = new BB_Tag('div');
$div->setReplacements(array('<div>', '</div>'));
$div->setInside(false);
$bb = new BB();
$bb->addTag($div);
echo $bb->make("[div] hello world [/div]");
/* outputs:
<p></p><div> hello world</div><p></p>
*/Yes, it gets very complicated. There are two routes: the easy route, or the thorough route. I usually lean toward the thorough route, but that's not always a good thing.s.dot wrote:The paragraph thing is very interesting.
Not only because of the challenge of block/inline elements, but because of the need to set an additional parameter AND the margins it creates at the top of say, a TD or DIV.
I may leave the default set to use nl2br() but make an additional parameter for those that wish to use paragraph formatting. It will be challenging. I may define all block level elements and check the replacement for existence of a block level element. It will be interesting.
Another thing: Checking input for correct nesting and closing tags.
Is this really needed? The regexp checks for full matches (start tag, text, end tag) and only replaces if the whole match is found. I realize it may be beneficial to potentially create valid html output, but that doesn't stop someone from putting in [ b ][ quote ]text[ /quote ][ /b ] which would break the validity anyways.
Code: Select all
private function makeTags($matches)
{
foreach ($matches AS $match)
{
#each $match consists of the following
#$match[0] = full match [tagname]contents[/tagname]
#$match[1] = tag name
#$match[2] = optional parameter (e.g. ="name")
#$match[3] = the contents between the opening and closing bb code tag
//applying a function to the tag contents
if (count($this->tags[$match[1]]->applyFunction))
{
$match[3] = $this->applyFunction($match);
}
//tag with no parameter replacement (e.g. [b]text[/b])
if (!$this->tags[$match[1]]->parameter)
{
$this->str = $this->makeSimpleTag($match);
continue;
}
//tags with parameter (e.g. [quote]text[/quote] or [quote="name"]text[/quote])
if ($this->tags[$match[1]]->parameter == 1)
{
$this->str = $this->makeParameterTag($match);
}
}
}
private function makeSimpleTag($match)
{
return str_replace(
$match[0],
$this->tags[$match[1]]->replacements[0] . $match[3] . $this->tags[$match[1]]->replacements[1],
$this->str
);
}The new regexp will only make matches for defined tags, if there are any. eg "/(b|u|i|etc)..../i", so any match that makes it to the makeTags() function will be present in the tag listJonah Bron wrote:Major improvement.
Um, it looks like it doesn't check if that tag exists in $this->tags, but I could be wrong. Seems like it might throw an "undefined index" error?
Jonah Bron wrote: Seems like it might throw an "undefined index" error?
Gotta love having tests. If you didn't have tests, you'd be writing 10x as much code out of paranoias.dot wrote:any match that makes it to the makeTags() function will be present in the tag list