<?php
/**
 *    This class handles the conversion of bb code to XHTML output<br />
 *    Major credit to mybb (http://www.mybboard.com) for the legwork on the regular expressions.
 *    
 *    <p>
 *    This class is quite simply a portable solution to coverting bb code into valid XHTML.
 *    All the major tags are currently supported, which the exception of quotes, code and lists.
 *    </p>
 *
 *    <p>
 *    This class requires an XML file to read the bb code from. While this enables you to easily add edit
 *    or remove bb codes it is imperative that you realise the potential security threat this XML file can
 *    play. If someone could write to this file they could essentially run arbitrary PHP code. On the other
 *    hand, if they can write to an XML file it's likely they can write to a php file too.
 *    </p>
 *    <p>
 *    The structure for the XML file should be as follows:<br />
 *    <code>
 *    <bbcodes>
 *    <bb>
 *        <name>b</name>
 *        <regex>#\[b\](.*?)\[/b\]#si</regex>
 *        <callback>return '&lt;strong&gt;' . $matches[1] . '&lt;/strong&gt;';</callback>
 *        <example>[b]bold[/b]</example>
 *    </bb>
 *    </bbcodes>
 *    </code>
 *    The example field is optional and is used to show the user examples of the bb code in action.<br />
 *    Note that the code uses htmlentities with as & => &amp;, otherwise the XML
 *    file is invalid.
 *    </p>
 *    
 *    Example:<br />
 *    <code>
 *    $users_post = new bb_code_parser();
 *    $users_post->setBB($_POST);
 *    // Perform validation to prevent various injections that have nothing to do with bb code here
 *    $users_post->disableBBcode('smiley',false); // disable all smileys
 *    echo $users_post->getXHTML();
 *    </code>
 *    
 *    @author Sid Karunaratne <sakaru@gmail.com>
 *    @copyright Sid Karunaratne <sakaru@gmail.com>
 *    @license  http://creativecommons.org/licenses/by/2.5/legalcode Creative Commons 2.5
*/

class bb_code_parser
{
    
private $bbcode;
    
private $bbarray;
    
private $disabled_codes;

    
/** Creates a new bbcode object to parse bbcode for you
    *
    * It's a constructor, what do you think it does??!<br />
    * You can optionally specify an XML file for the object to use as its source of bb codes
    * 
    * @author Sid Karunaratne <sakaru@gmail.com>
    * @param string $xml_filename relative filename to xml file containing bb codes
    */
    
public function __construct($xml_filename 'bbcodes.xml')
    {
        
$this->bbarray simplexml_load_file('bbcodes.xml');
        
$this->disabled_codes = array();
    }

    
/** Set the objects bbcode to a new string
    *
    * Using this function you can reuse the object eliminating the overhead of creating a new object<br />
    * Simply provide a string parameter and the class is as good as new.
    * 
    * @author Sid Karunaratne <sakaru@gmail.com>
    * @param string $input string to parse
    * @return void
    */
    
public function setBB($input)
    {
        
$this->bbcode $input;
    }
    
    
/** Parse the bbcode and get the resultant XHTML
    *
    * This is the major function in the class. turns your bbcoded input into ready to print XHTML.
    * 
    * @author Sid Karunaratne <sakaru@gmail.com>
    * @return string the outputted XHTML
    */
    
public function getXHTML()
    {
        
$text $this->bbcode;
        foreach (
$this->bbarray as $bb_code => $bb)
        {
            if (!
in_array($bb->name,$this->disabled_codes))
            {
                
$text preg_replace_callback($bb->regex,
                    
create_function('$matches',$bb->callback),
                    
$text);
            }
        }
        
$text nl2br($text);
        return 
$text;
    }
    
    
/** Get all enabled bb codes
    * 
    * Creates a 2-D array of all enabled bbcodes along with examples.
    * The array follows the structure of the XML file
    * 
    * @author Sid Karunaratne <sakaru@gmail.com>
    * @return array array of all enabled bbcodes
    */
    
public function getEnabledBBcodes()
    {
        
$enabled_codes = array();

        foreach (
$this->bbarray as $bb_code => $bb)
        {
            if (!
in_array($bb_code,$this->disabled_codes))
            {
                
array_push($enabled_codes,$bb);
            }
        }
        return 
$enabled_codes;
    }
    
    
/**
    * Disable a certain bb code or set of bb codes
    *
    * Disables the bb code specified in $code_name. To disable all bb codes of a certain class (eg all smileys)
    * set $code_name equal to the common prefix of the bb codes and set $exact
    * equal to false (eg $code_name = 'smiley' and $exact equal to false)<br />
    * Returns the number of codes disabled, so returning 0 means that nothing was disabled, if $exact
    * is set to true then the function can only ever return 0 or 1.
    *
    * @author Sid Karunaratne <sakaru@gmail.com>
    * @param string $code_name name of bbcode to disable.
    * @param boolean $exact flag determining whether to disable only a specific bbcode or all bbcodes of that type
    * @return int the number of codes disabled
    */
    
public function disableBBcode($code_name,$exact true)
    {
        
$number_disabled 0;

        foreach (
$this->bbarray as $bb_code => $bb)
        {
            if (
$exact === true  && $bb->name == $code_name)
            {
                
array_push($this->disabled_codes,$bb->name);
                return 
1;
            }
            else if (
$exact === false && strncmp($bb->name,$code_name,strlen($code_name)) === 0)
            {
                
$number_disabled++;
                
array_push($this->disabled_codes,$bb->name);
            }
        }
        return 
$number_disabled;

    }
}
?>
<form action="bbcode.php" method="post">
    Input:<br />
    <textarea name="txt" rows="8" cols="55"><?php echo @htmlentities($_POST['txt'],ENT_QUOTES); ?></textarea><br />
    <input type="submit" />
</form>
<hr />
<?php
if(isset($_POST['txt']))
{
    
$txt htmlentities($_POST['txt'],ENT_QUOTES);
    
$bb_parser = new bb_code_parser();
    
$bb_parser->setBB($txt);
    echo 
$bb_parser->getXHTML();
}
?>