View Source

<ac:macro ac:name="unmigrated-inline-wiki-markup"><ac:plain-text-body><![CDATA[{zone-template-instance:ZFDEV:Zend Proposal Zone Template}

{zone-data:component-name}
Zend_Filter_Bbcode
{zone-data}

{zone-data:proposer-list}
[Pieter Kokx|mailto:pieter@kokx.com]
{zone-data}

{zone-data:revision}
1.1 - 1 August 2006: Updated from community comments.
1.2 - 27 December 2007: Started to update the proposal
1.3 - 29 December 2007: Updated the class skeleton.
{zone-data}

{zone-data:overview}
Zend_Filter_Bbcode is a stack-based bbcode parser which will always output valid XHTML. This component will also implement context-awareness for each tag.
{zone-data}

{zone-data:references}
* [BBCode Wikipedia|http://en.wikipedia.org/wiki/BBCode]
{zone-data}

{zone-data:requirements}
* This component *will* provide extensibility to create your own BBcode tags.
* This component *will* provide XHTML valid output.
* This component *will* provide context-awareness for BBcode tags.
{zone-data}

{zone-data:dependencies}
* Zend_Filter_Exception
* Zend_Filter_Interface
* Zend_Filter
* Zend_Loader
* Zend_Uri
* Zend_View_Helper_*
{zone-data}

{zone-data:operation}
Many programmers are parsing BBcode with regular expressions. Well, regular expressions isn't the worst solution for this problem, but it is almost an impossible task to create XHTML valid output for user input like '\[b\]\[u\]some sample test\[/b\]\[/u\]'. A well written stack-based BBcode parser (like this proposal) does produce XHTML valid output in that case.

With regular expressions, extensibility is also a huge problem. When you are looking back to regular expressions you've written a month ago, mostly you will keep staring at it for half an hour and still not understand it. With Zend_Filter_Bbcode, you just have to extend Zend_Filter_Bbcode and add a function (see UC 3). You can even use PHP code for the tag, so it isn't a problem to add line numbers or highlighting to a tag like \[code\].

Context-awareness is also a main concern. HTML doesn't allow text inside a <ol> or <ul> tag, unless it is inside a <li> tag. When you are using regular expressions or just simple replaces for a BBcode parser, this is an impossible task. But Zend_Filter_Bbcode checks the nesting of a tag, and also if it is allowed in the current position. If it isn't allowed, the tag will not be parsed. The same is with text in the wrong position, if it isn't allowed there, it will not be added to the return string.
{zone-data}

{zone-data:milestones}
* Milestone 1: \[DONE\] Initial class design
* Milestone 2: \[DONE\] Create prototype
* Milestone 3: Finish proposal and submit for community review
* Milestone 3: Create code-covering unit tests
{zone-data}

{zone-data:class-list}
* Zend_Filter_Bbcode
* Zend_View_Helper_Bbcode
{zone-data}

{zone-data:use-cases}

{composition-setup}

{deck:id=use-cases1}

{card:label=UC 1 : simple usage}
{code:type=php}
<?php
$bbcode = new Zend_Filter_Bbcode();

$code = '[b][i]some sample text[/i][/b]';

// this will output: <strong><em>some sample text</em></strong>
echo $bbcode->filter($code);
{code}
{card}

{card:label=UC 2 : XHTML valid output}
{code:type=php}
<?php
$bbcode = new Zend_Filter_Bbcode();

$code = '[b][i]some sample text[/b][/i] some sample text';

// this will output XHTML valid text: <strong><em>some sample text</em></strong> some sample text
echo $bbcode->filter($code);
{code}
{card}

{card:label=UC 3 : Own Bbcode tags}
{code:type=php}
class My_Bbcode extends Zend_Filter_Bbcode
{

protected $_contextMap = array(
'b' => array('u', 'i', 's', 'foo'),
'u' => array('b', 'i', 's', 'foo'),
'i' => array('b', 'u', 's', 'foo'),
's' => array('b', 'i', 'u', 'foo'),
'foo' => null
);

function _bbCodeFoo($info)
{
/*
the $info array will contain this information:
$info['name'] will contain the tag's name
$info['func'] will contain the tag's function name
$info['attrs'] will contain the tag's attributes
*/

return 'Bar: ' . $this->_parse('[/foo]');
}
}

$bbcode = new My_Bbcode();

$code = '[b][foo]some sample text[/foo][/b]';

// this will output: <strong>Bar: some sample text</strong>
echo $bbcode->filter($code);
{code}
{card}

{card:label=UC 4 : context-awareness}
{code:type=php}
<?php
$bbcode = new Zend_Filter_Bbcode();

$code = '[b][code]some sample text[/code][/b]';

// this will output: <strong>[code]some sample text[/code]</strong>
echo $bbcode->filter($code);
{code}
{card}

{card:label=UC 5 : Usage with Zend_View}
{code:type=php}
<?php
echo $this->bbcode('[b][i]some sample text[/b][/i]');
{code}
{card}

{card:label=UC 6 : Using other filters}
{code:type=php}
<?php
$bbcode = new Zend_Filter_Bbcode();

$filter = new Zend_Filter_PregReplace('/some text/', 'some replacement');

$bbcode->addFilter($filter);

$code = '[b]some text[/b]';

// will output <strong>some replacement</strong>
echo $bbcode->filter($code);
{code}
{card}

{deck}

{zone-data}

{zone-data:skeletons}

{composition-setup}

{deck:id=skeletons}

{card:label=Zend_Filter_Bbcode}
{code}
<?php
class Zend_Filter_Bbcode implements Zend_Filter_Interface
{

protected $_contextMap = array();
protected $_functionPrefix = '_bbCode';
protected $_currentTag;
protected $_pointer = 0;
protected $_stack = array();
protected $_tagStart = '[';
protected $_tagEnd = ']';
protected $_searchedStoppers = array();
protected $_filter;


// functions to configure Zend_Filter_Bbcode
public function __construct(array $config = array()) {}
public function addContextMap(array $tags) {}
public function clearContextMap() {}
public function defineStandardContextMap() {}
public function setTagBrackets($start = '[', $end = ']') {}
public function setFunctionPrefix($functionPrefix = '_bbCode') {}
public function setFilter(Zend_Filter $filter) {}
public function getFilter() {}
public function addFilter(Zend_Filter_Interface $filter) {}

// functions used for parsing and rendering BBcode
public function filter($value) {}
protected function _tokenize($code) {}
protected function _getTagInfo($token) {}
protected function _prepareStoppers($stoppers = array()) {}
protected function _parse($stoppers = array(), $showText = true) {}
protected function _nextToken() {}

// standard BBcode functions
protected function _bbCodeB(array $info) {}
protected function _bbCodeI(array $info) {}
protected function _bbCodeU(array $info) {}
protected function _bbCodeS(array $info) {}
protected function _bbCodeCode(array $info) {}
protected function _bbCodeUrl(array $info) {}
protected function _bbCodeImg(array $info) {}
protected function _bbCodeList(array $info) {}
protected function _bbCodeLi(array $info) {}
}
{code}
{card}

{card:label=Zend_View_Helper_Bbcode}
{code:type=php}
class Zend_View_Helper_Bbcode
{

public function bbcode($input = null, $class = 'Zend_Filter_Bbcode', array $config = array()) {}
}
{code}
{card}

{deck}
{zone-data}

{zone-template-instance}]]></ac:plain-text-body></ac:macro>