Re: BBCodeParser massive update (/me cries)

From: Date: Tue, 11 Oct 2005 04:00:10 +0000
Subject: Re: BBCodeParser massive update (/me cries)
References: 1 2  Groups: php.pear.dev 
Request: Send a blank email to pear-dev+get-40132@lists.php.net to get a copy of this message
Hey, I have a chance to look through the Text_Wiki code now. Replying to your comments:
Actually, the whole structure was borked from the beginning, I see no point to reinvent a tree manager to parse BBCode, so it's vain to go ahead in this direction and make the code even more complicated. Text_Wiki 's treeing is implicit. I don't see how you can guarantee XHTML compliant output without some sort of stack based parser. Simply put, if "[i][b]txt[/i][/b]" results in "<i><b>txt</i></b>" (as it does now), then you can't claim that output will be XHTML. Mismatched BBCode may even qualify as a XSS-style attack because it breaks the validity of your output.
You also need some way of guaranteeing that the only tag in <ul> is <li> and other similar PEBKAC errors. The best way to do this is going to be adding a stack based parser in there somewhere. I would propose a "validate" step between the "parse" and "render" steps that can add, remove, and rearrange tokens as needed. When you put it there, you can store the results, which is nice because stack based parsing like what needs doing is rather compute intensive.
Also you don't respect coding standards and you change some default (quotes). I looked through the PEAR manual and I couldn't find anything against mixing double and single quotes (I may have missed something). I find it produces cleaner code and, last time I checked, using single quotes/concatenation is faster.
<book title="Advanced PHP Programming" author="George Schlossnagle" page="471"> So although you see a significant improvement in the performance of interpolation [in PHP 4.3], it is still faster to use concatenation to build dynamic strings.</book>
Also you don't respect coding standards and you change some default (quotes). Your XSS filtering is naturally an enhancement, but it's not complete. Text_Wiki has it from the conception. Yep, all things considered, I like Text_Wiki better, but it is missing some things...
I think Text_Wiki_BBCode would pass the tests the same. (btw it's not in your zip) The tests that I am using are in a wrapper class that would be of little use in PEAR. I'll give you my test cases if you would like them though.
A few things before I go to bed: - The parser seems to add paragraph tags to everything. Processing '"' results in '<p>&quot;</p>', but it should result in '&quot;'. If something should be inline formatted (instead of block), I'd like to keep it that way. (<p> is block formatted by default) How can I get rid of the '<p></p>'? - You are using a number of non-XHML 1.1 tags such as '<u>', '<i>', and '<b>'. Mind if I replace those with equivalents? ~Seth Testcases: class BBCode_TestCase extends PHPUnit_TestCase {
    function testGetProcessed(){
        $bbc = new BBCode('Basic,Email,Extended,Images,Links,Lists');
        $this->baseHtmlEsc($bbc, 'getProcessed');
        $this->basicBBCode($bbc, 'getProcessed');
        $this->listBBCode($bbc, 'getProcessed');
        $this->linkBBCode($bbc, 'getProcessed');
        $this->extBBCode($bbc, 'getProcessed');
        $this->imgBBCode($bbc, 'getProcessed');
        $this->emailBBCode($bbc, 'getProcessed');
    }
    function emailBBCode($bbc, $funcNam){
        $this->assertEquals('<a href="mailto:guest@anonymous.org">guest@anonymous.org</a>', $bbc->$funcNam('guest@anonymous.org'));
        $this->assertEquals('<a href="mailto:guest@anonymous.org">mail me</a>', $bbc->$funcNam('[email=guest@anonymous.org]mail me[/email]'));
        $this->assertEquals('<a href="mailto:guest@anonymous.org">guest@anonymous.org</a>', $bbc->$funcNam('[email]guest@anonymous.org[/email]'));
    }
    function imgBBCode($bbc, $funcNam){
        $this->assertEquals('<img src="img.jpg" />', $bbc->$funcNam('[img]img.jpg[/img]'));
        $this->assertEquals('<img src="http://www.server.org/image.jpg" width="100" height="200" />', $bbc->$funcNam('[img w=100 h=200]http://www.server.org/image.jpg[/img]'));
    }
    function basicBBCode($bbc, $funcNam){
        $this->assertEquals('<strong>txt</strong>', $bbc->$funcNam('[b]txt[/b]'));
        $this->assertEquals('<strong>txt</strong>', $bbc->$funcNam('[b]txt'));
        $this->assertEquals('<em>txt</em>', $bbc->$funcNam('[i]txt[/i]'));
        $this->assertEquals('<em>txt</em>', $bbc->$funcNam('[i]txt[/I]'));
        $this->assertEquals('<em>txt</em>', $bbc->$funcNam('[I]txt[/i]'));
        $this->assertEquals('<em>txt</em>', $bbc->$funcNam('[I]txt[/I]'));
        $this->assertEquals('<del>txt</del>', $bbc->$funcNam('[s]txt[/s]'));
        $this->assertEquals('<span style="text-decoration:underline;">txt</span>', $bbc->$funcNam('[u]txt[/u]'));
        $this->assertEquals('<sub>txt</sub>', $bbc->$funcNam('[sub]txt[/sub]'));
        $this->assertEquals('<sup>txt</sup>', $bbc->$funcNam('[sup]txt[/sup]'));
        $this->assertEquals('<sup><sub>txt</sub></sup>', $bbc->$funcNam('[sup][sub]txt[/sup][/sub]'));
        $this->assertEquals('<em><strong>txt</strong></em>', $bbc->$funcNam('[i][b]txt[/i][/b]'));
    }
    function listBBCode($bbc, $funcNam){
        $this->assertEquals('<ul><li>txt</li></ul>', $bbc->$funcNam('[*]txt'));
        $this->assertEquals("<ul><li>txt\n</li></ul>", $bbc->$funcNam("[ulist][*]txt\n[/ulist]"));
        $this->assertEquals('<ul><li>txt</li></ul>', $bbc->$funcNam('[ulist]txt[/ulist]'));
        $this->assertEquals('<ul><li><ul><li><ul><li>txt</li></ul></li></ul></li></ul>', $bbc->$funcNam('[ulist][ulist][ulist]txt'));
        $this->assertEquals('<ul><li>[xxx]txt[/xxx]</li></ul>', $bbc-
$funcNam('[ulist][xxx]txt[/xxx][/ulist]')); $this->assertEquals('<ul><li>txt</li></ul>', $bbc->$funcNam('[ulist][li]txt[/li][/ulist]'));
        $this->assertEquals('<ul><li>txt</li><li>txt</li></ul>', $bbc->$funcNam('[ulist][li]txt[li]txt[/ulist]'));
        $this->assertEquals('<ul><li>txt</li></ul>', $bbc->$funcNam('[ulist][*]txt[/ulist]'));
        $this->assertEquals('<ul><li><ol><li>txt</li></ol></li></ul>', $bbc->$funcNam('[ulist][*][list][*]txt[/ulist]'));
        $this->assertEquals('<ol><li>txt</li></ol>', $bbc->$funcNam('[list][li]txt[/li][/list]'));
        $this->assertEquals('<ul><li><ol><li>txt</li></ol></li></ul>', $bbc->$funcNam('[li][list][li]txt[/li][/list]'));
        $this->assertEquals('<ul><li>txt<ul><li>txt</li></ul></li></ul>', $bbc->$funcNam('[*]txt[ulist]txt[/ulist]'));
        $this->assertEquals('<ul><li><ul><li><ul><li><ul><li>txt</li></ul></li></ul></li></ul></li></ul>', $bbc->$funcNam('[li][ulist][ulist][ulist]txt'));
        $this->assertEquals(
            '<ol style="list-style-type:upper-alpha;"><li>ordered item 1, nested list:<ol style="list-style-type:upper-roman;"><li>nested item 1</li><li>nested item 2</li></ol></li><li>ordered item 2</li></ol>',
            $bbc->$funcNam('[list=A s=3][li]ordered item 1, nested list:[list=I][li]nested item 1[/li][li]nested item 2[/li][/list][/li][li]ordered item 2[/li][/list]'));
        $this->assertEquals(
            '<ol style="list-style-type:upper-alpha;"><li>ordered item 1 type A</li><li>ordered item 12 type A</li></ol>',
            $bbc->$funcNam('[list=A][li]ordered item 1 type A[/li][li=12]ordered item 12 type A[/li][/list]'));
        $this->assertEquals(
            '<ol style="list-style-type:lower-alpha;"><li>ordered item 5 type a</li><li>ordered item 6 type a</li></ol>',
            $bbc->$funcNam('[list=a s=5][li]ordered item 5 type a[/li][*]ordered item 6 type a[/list]'));
        $this->assertEquals(
            '<ol style="list-style-type:upper-roman;"><li>ordered item 1 type I</li></ol>',
            $bbc->$funcNam('[list=I][*]ordered item 1 type I[/list]'));
        $this->assertEquals(
            '<ol style="list-style-type:lower-roman;"><li>ordered item 1 type i</li><li>ordered item 4 type i</li></ol>',
            $bbc->$funcNam('[list=i][*]ordered item 1 type i[li=4]ordered item 4 type i[/li][/list]'));
        $this->assertEquals(
            '<ol style="list-style-type:decimal;"><li>ordered item 1</li><li>ordered item 2</li></ol>',
            $bbc->$funcNam('[list=1][*]ordered item 1[*]ordered item 2[/list]'));
    }
    function linkBBCode($bbc, $funcNam){
        $this->assertEquals(
            '<a href="http://www.test.com/">http://www.test.com/</a>',
            $bbc->$funcNam('http://www.test.com/'));
        $this->assertEquals(
            '<a href="http://www.test.com/">www.test.com</a>',
            $bbc->$funcNam('[url]www.test.com[/url]'));
        $this->assertEquals(
            '<a href="http://www.test.com/testurl">http://www.test.com/testurl</a>',
            $bbc->$funcNam('[url]http://www.test.com/testurl[/url]'));
        $this->assertEquals(
            '<a href="http://www.test.com/">testurl</a>',
            $bbc->$funcNam('[url=www.test.com/]testurl[/url]'));
        $this->assertEquals(
            '<a href="http://www.server.org">server</a>',
            $bbc->$funcNam('[url=http://www.server.org t=new]server[/url]'));
        $this->assertEquals(
            'txt <a href="http://www.test.com/">www.test.com</a> txt',
            $bbc->$funcNam('txt www.test.com txt'));
        $this->assertEquals(
            'txt <em><a href="http://www.test.com/">www.test.com</a></em> txt',
            $bbc->$funcNam('txt [i]www.test.com[/i] txt'));
        $this->assertEquals(
            'Click here',
            $bbc->$funcNam('[url=javascript:location.replace("bad_link");]Click here[/url]'));
        $this->assertEquals(
            '<a href="http://domain.com/index.php?i=1&amp;j=2">linked text</a>',
            $bbc->$funcNam('[url=http://domain.com/index.php?i=1&j=2]linked text[/url]'));
    }
    function extBBCode($bbc, $funcNam){
        $this->assertEquals('<h2>txt</h2>', $bbc->$funcNam('[h2]txt[/h2]'));
        $this->assertEquals('<span style="color:blue">blue text</span>', $bbc->$funcNam('[color=blue]blue text[/color]'));
        $this->assertEquals('<span style="font-size:18pt">the size of this text is 18pt</span>', $bbc->$funcNam('[size=18]the size of this text is 18pt[/size]'));
        $this->assertEquals('<span style="font-family:arial">different font type</span>', $bbc->$funcNam('[font=arial]different font type[/font]'));
        $this->assertEquals('<div style="text-align:right">yes, you\'re right, this isn\'t on the left</div>', $bbc->$funcNam('[align=right]yes, you\'re right, this isn\'t on the left[/align]'));
        $this->assertEquals('he said: <q cite="http://www.server.org/quote.html">i\'m tony montana</q>', $bbc->$funcNam('he said: [quote=http://www.server.org/quote.html]i\'m tony montana[/quote]'));
        $this->assertEquals('<code>x + y = 6;</code>', $bbc->$funcNam('[code]x + y = 6;[/code]'));
    }
    function baseHtmlEsc($bbc, $funcNam){
        $this->assertEquals('&lt;', $bbc->$funcNam('<'));
        $this->assertEquals('&gt;', $bbc->$funcNam('>'));
        $this->assertEquals('&quot;', $bbc->$funcNam('"'));
    }
}

« previous php.pear.dev (#40132) next »