Custom BB Codes

Discussion in 'General Webmaster Support' started by monoVertex, May 8, 2007.

  1. enouwee

    enouwee Non ex transverso sed deorsum

    Ratings:
    +235 / 0 / -0
    Here's the basic matching code, so you can see, what it does:

    PHP:
    <?php

    $string 
    '[a url="test"]this[B]a test[/a]';

    if (
    preg_match(',\[(a|img)\s+url="([^"]*)"\](.*?)\[/\1\],i'$string$matches))
    {
            
    print_r($matches);
    }

    ?>
    There are tons of ways to proceed from here. I'd add PREG_OFFSET_CAPTURE to the flags passed to preg_match() and replace the BBCode, including URL and body after a thorough validation using substr_replace().
     
  2. monoVertex

    monoVertex I'm back!

    Ratings:
    +459 / 0 / -0
    You know, you got me even deeper in the fog xD.
     
  3. enouwee

    enouwee Non ex transverso sed deorsum

    Ratings:
    +235 / 0 / -0
    No problem, I bet everything will be much clearer, once you see this. It's very easy, didn't even take 30 minutes to write: :D

    PHP:
    <?php

    $string 
    'XX[a url="test"]this[B]a test[/a]YY';
    $string .= 'ZZ[img url="my_test"]this an image test[/img]TT';

    if (
    preg_match_all(',\[([a-z]+)\s+url="([^"]*)"\](.*?)\[/\1\],i'$string$matchesPREG_OFFSET_CAPTURE))
    {
            for (
    $i count($matches[0]) - 1$i >= 0$i--)
            {
                    
    $valid true;

                    
    $len strlen($matches[0][$i][0]);
                    
    $pos $matches[0][$i][1];

                    
    $tag strtolower($matches[1][$i][0]);
                    switch (
    $tag)
                    {
                            case 
    'a':
                            case 
    'img':
                                    
    // filter your other valid tags here
                                    
    break;
                            default:
                                    
    $valid false;
                    }

                    if (
    $valid == true)
                    {
                            
    // process URL part
                            
    $url $matches[2][$i][0];

                            if (empty(
    $url))
                            {
                                    
    $valid false;
                            }

                            
    // do other validation here
                    
    }

                    if (
    $valid == true)
                    {
                            
    // process body part
                            
    $body $matches[3][$i][0];

                            if (empty(
    $body))
                            {
                                    
    $valid false;
                            }

                            
    // do other validation here
                    
    }

                    if (
    $valid)
                    {
                            switch (
    $tag)
                            {
                                    case 
    'img':
                                            
    $output '<img src="' $url '" alt="' $body '" />';
                                            break;
                                    case 
    'a':
                                            
    $output '<a href="' $url '">' $body '</a>';
                                            break;
                            }
                    }
                    else
                    {
                            
    $output '';
                    }

                    
    $string substr_replace($string$output$pos$len);

                    print 
    "ITERATION USING MATCH $i:\n";
                    print 
    "$string\n\n";
            }

            print 
    "FINAL RESULT:\n$string\n";
    }


    ?>
     
  4. monoVertex

    monoVertex I'm back!

    Ratings:
    +459 / 0 / -0
    Meh, I started to understand these functions better. I made myself a piece of code which is supposed to replace the url tags.

    PHP:
    $text preg_replace('/(\[url=) (.*?) (\]) (.*?) (\[\/url\])/i','<a href="$2">$4</a>',$text);
    I see no syntax problem, and it's supposed to work... However, the url tags are still displayed as literal on the page...
     
  5. enouwee

    enouwee Non ex transverso sed deorsum

    Ratings:
    +235 / 0 / -0
    What are you trying to do? I see a lot of useless spaces in that expression.

    If you want to replace something like this:
    [url.=THIS_IS_MY_URL]THIS IS MY TEXT[/url.] (ignore . due to parsing problem)

    better use an expression like:
    PHP:
    $text preg_replace(',\[url=\s*(.*?)\s*\]\s*(.*?)\s*\[/url\],i','<a href="$1">$2</a>',$text);
    Note that some captures are useless, as you don't use them and \s* matches any number of whitespaces, including 0.
     
  6. monoVertex

    monoVertex I'm back!

    Ratings:
    +459 / 0 / -0
    Thank you! One more question :D. If there are line breaks inside the tags, they are not parsed. How cam I make the function to ignore line breaks?
     
  7. enouwee

    enouwee Non ex transverso sed deorsum

    Ratings:
    +235 / 0 / -0
    If you want the "." to match newlines, you have to add a "s" modifier, like:
    Code:
    /line1.*line2/s
    Alternatively, the "m" modifier changes the behaviour of "^" to match every "begin of line" and not only the "begin of the string".

    The full list of supported modifiers is:
    http://www.php.net/manual/en/reference.pcre.pattern.modifiers.php
     
  8. monoVertex

    monoVertex I'm back!

    Ratings:
    +459 / 0 / -0
    Hmmm... It's not supposed to match new lines, I can have something like this:

    Code:
     
    [font.=verdana]test
    
    
    test
    test
    test
    
    test
    [/verdana]
    This is not parsed and the first "test" is not on a new line.

    EDIT: Nvm, studied that list and understood, it's all working now, thanks a lot!
     
  9. enouwee

    enouwee Non ex transverso sed deorsum

    Ratings:
    +235 / 0 / -0
    This works like a charm:

    PHP:
    <?php

    $string 
    ='[font=verdana]test


    test
    test
    test

    test
    [/font]'
    ;

    print_r(preg_replace('!\[font=(.*?)\](.*?)\[/font\]!is''<font face="$1">$2</font>'$string));

    ?>
     
  10. monoVertex

    monoVertex I'm back!

    Ratings:
    +459 / 0 / -0
    Yeah, I saw that :D. Gotta spread first :(. You are credited on the page, anyway :D.
     
  11. enouwee

    enouwee Non ex transverso sed deorsum

    Ratings:
    +235 / 0 / -0
    If you want to implement your BBCode like that, please don't put my name next to it. phyrex1an already said it the previous posts, I'm going to repeat it:
    Don't blindly use that preg_replace() thing without any additional validation steps, as your code will be vulnerable to XSS (cross-site scripting) attacks.

    You have to filter both inputs, either by limiting the choices or filtering out malicious content. Rather than saying "(.*?)", you'd use "(arial|times|courier)" to set the font family from a given subset. preg_replace_callback() allows you to do post-processing on the matches: a callback function generates the replacement string.

    I put a much larger piece of code in #23, which takes the whole input apart and later replaces it (without preg_replace_callback(), but essentially, both do the same). You can transform and validate the fields as you like before they're inserted back into the text.
     

Share This Page