1 <?php
2 /* vim: set expandtab tabstop=4 shiftwidth=4: */
3 // +----------------------------------------------------------------------+
4 // | PHP Version 4 |
5 // +----------------------------------------------------------------------+
6 // | Copyright (c) 1997-2003 The PHP Group |
7 // +----------------------------------------------------------------------+
8 // | This source file is subject to version 2.02 of the PHP license, |
9 // | that is bundled with this package in the file LICENSE, and is |
10 // | available at through the world-wide-web at |
11 // | http://www.php.net/license/2_02.txt. |
12 // | If you did not receive a copy of the PHP license and are unable to |
13 // | obtain it through the world-wide-web, please send a note to |
14 // | license@php.net so we can mail you a copy immediately. |
15 // +----------------------------------------------------------------------+
16 // | Author: Stijn de Reede <sjr@gmx.co.uk> |
17 // +----------------------------------------------------------------------+
18 //
19 // $Id: BBCodeParser.php,v 1.17 2007/07/02 18:46:30 cweiske Exp $
20 //
21 // Modified by SilverStripe www.silverstripe.com
22
23 /**
24 * @package sapphire
25 * @subpackage misc
26 * @author Stijn de Reede <sjr@gmx.co.uk> , SilverStripe
27 *
28 *
29 * This is a parser to replace UBB style tags with their html equivalents. It
30 * does not simply do some regex calls, but is complete stack based
31 * parse engine. This ensures that all tags are properly nested, if not,
32 * extra tags are added to maintain the nesting. This parser should only produce
33 * xhtml 1.0 compliant code. All tags are validated and so are all their attributes.
34 * It should be easy to extend this parser with your own tags, see the _definedTags
35 * format description below.
36 *
37 *
38 * Usage:
39 * $parser = new SSHTMLBBCodeParser();
40 * $parser->setText('normal [b]bold[/b] and normal again');
41 * $parser->parse();
42 * echo $parser->getParsed();
43 * or:
44 * $parser = new SSHTMLBBCodeParser();
45 * echo $parser->qparse('normal [b]bold[/b] and normal again');
46 * or:
47 * echo SSHTMLBBCodeParser::staticQparse('normal [b]bold[/b] and normal again');
48 *
49 *
50 * Setting the options from the ini file:
51 * $config = parse_ini_file('BBCodeParser.ini', true);
52 * $options = &PEAR::getStaticProperty('SSHTMLBBCodeParser', '_options');
53 * $options = $config['SSHTMLBBCodeParser'];
54 * unset($options);
55 */
56 class SSHTMLBBCodeParser
57 {
58 /**
59 * An array of tags parsed by the engine, should be overwritten by filters
60 *
61 * @access private
62 * @var array
63 */
64 var $_definedTags = array();
65
66 /**
67 * A string containing the input
68 *
69 * @access private
70 * @var string
71 */
72 var $_text = '';
73
74 /**
75 * A string containing the preparsed input
76 *
77 * @access private
78 * @var string
79 */
80 var $_preparsed = '';
81
82 /**
83 * An array tags and texts build from the input text
84 *
85 * @access private
86 * @var array
87 */
88 var $_tagArray = array();
89
90 /**
91 * A string containing the parsed version of the text
92 *
93 * @access private
94 * @var string
95 */
96 var $_parsed = '';
97
98 /**
99 * An array of options, filled by an ini file or through the contructor
100 *
101 * @access private
102 * @var array
103 */
104 var $_options = array(
105 'quotestyle' => 'double',
106 'quotewhat' => 'all',
107 'open' => '[',
108 'close' => ']',
109 'xmlclose' => true,
110 'filters' => 'Basic'
111 );
112
113 /**
114 * An array of filters used for parsing
115 *
116 * @access private
117 * @var array
118 */
119 var $_filters = array();
120
121 /**
122 * Constructor, initialises the options and filters
123 *
124 * Sets the private variable _options with base options defined with
125 * &PEAR::getStaticProperty(), overwriting them with (if present)
126 * the argument to this method.
127 * Then it sets the extra options to properly escape the tag
128 * characters in preg_replace() etc. The set options are
129 * then stored back with &PEAR::getStaticProperty(), so that the filter
130 * classes can use them.
131 * All the filters in the options are initialised and their defined tags
132 * are copied into the private variable _definedTags.
133 *
134 * @param array options to use, can be left out
135 * @return none
136 * @access public
137 * @author Stijn de Reede <sjr@gmx.co.uk>
138 */
139 function SSHTMLBBCodeParser($options = array())
140 {
141 // set the already set options
142 $baseoptions = &SSHTMLBBCodeParser::getStaticProperty('SSHTMLBBCodeParser', '_options');
143 if (is_array($baseoptions)) {
144 foreach ($baseoptions as $k => $v) {
145 $this->_options[$k] = $v;
146 }
147 }
148
149 // set the options passed as an argument
150 foreach ($options as $k => $v ) {
151 $this->_options[$k] = $v;
152 }
153
154 // add escape open and close chars to the options for preg escaping
155 $preg_escape = '\^$.[]|()?*+{}';
156 if ($this->_options['open'] != '' && strpos($preg_escape, $this->_options['open'])) {
157 $this->_options['open_esc'] = "\\".$this->_options['open'];
158 } else {
159 $this->_options['open_esc'] = $this->_options['open'];
160 }
161 if ($this->_options['close'] != '' && strpos($preg_escape, $this->_options['close'])) {
162 $this->_options['close_esc'] = "\\".$this->_options['close'];
163 } else {
164 $this->_options['close_esc'] = $this->_options['close'];
165 }
166
167 // set the options back so that child classes can use them */
168 $baseoptions = $this->_options;
169 unset($baseoptions);
170
171 // return if this is a subclass
172 if (is_subclass_of($this, 'SSHTMLBBCodeParser_Filter')) {
173 return;
174 }
175
176 // extract the definedTags from subclasses */
177 $this->addFilters($this->_options['filters']);
178 }
179
180 function &getStaticProperty($class, $var)
181 {
182 static $properties;
183 if (!isset($properties[$class])) {
184 $properties[$class] = array();
185 }
186 if (!array_key_exists($var, $properties[$class])) {
187 $properties[$class][$var] = null;
188 }
189 return $properties[$class][$var];
190 }
191
192 /**
193 * Option setter
194 *
195 * @param string option name
196 * @param mixed option value
197 * @author Lorenzo Alberton <l.alberton@quipo.it>
198 */
199 function setOption($name, $value)
200 {
201 $this->_options[$name] = $value;
202 }
203
204 /**
205 * Add a new filter
206 *
207 * @param string filter
208 * @author Lorenzo Alberton <l.alberton@quipo.it>
209 */
210 function addFilter($filter)
211 {
212
213 $filter = ucfirst($filter);
214 if (!array_key_exists($filter, $this->_filters)) {
215 $class = 'SSHTMLBBCodeParser_Filter_'.$filter;
216 if (fopen('BBCodeParser/Filter/'.$filter.'.php','r',true)) {
217 include_once 'BBCodeParser/Filter/'.$filter.'.php';
218 }
219 if (!class_exists($class)) {
220
221 //PEAR::raiseError("Failed to load filter $filter", null, PEAR_ERROR_DIE);
222 }
223 else {
224 $this->_filters[$filter] = new $class;
225 $this->_definedTags = array_merge(
226 $this->_definedTags,
227 $this->_filters[$filter]->_definedTags
228 );
229 }
230 }
231
232 }
233
234 /**
235 * Remove an existing filter
236 *
237 * @param string $filter
238 * @author Lorenzo Alberton <l.alberton@quipo.it>
239 */
240 function removeFilter($filter)
241 {
242 $filter = ucfirst(trim($filter));
243 if (!empty($filter) && array_key_exists($filter, $this->_filters)) {
244 unset($this->_filters[$filter]);
245 }
246 // also remove the related $this->_definedTags for this filter,
247 // preserving the others
248 $this->_definedTags = array();
249 foreach (array_keys($this->_filters) as $filter) {
250 $this->_definedTags = array_merge(
251 $this->_definedTags,
252 $this->_filters[$filter]->_definedTags
253 );
254 }
255 }
256
257 /**
258 * Add new filters
259 *
260 * @param mixed (array or string)
261 * @return boolean true if all ok, false if not.
262 * @author Lorenzo Alberton <l.alberton@quipo.it>
263 */
264 function addFilters($filters)
265 {
266 if (is_string($filters)) {
267 //comma-separated list
268 if (strpos($filters, ',') !== false) {
269 $filters = explode(',', $filters);
270 } else {
271 $filters = array($filters);
272 }
273 }
274 if (!is_array($filters)) {
275 //invalid format
276 return false;
277 }
278 foreach ($filters as $filter) {
279 if (trim($filter)){
280 $this->addFilter($filter);
281 }
282 }
283 return true;
284 }
285
286 /**
287 * Executes statements before the actual array building starts
288 *
289 * This method should be overwritten in a filter if you want to do
290 * something before the parsing process starts. This can be useful to
291 * allow certain short alternative tags which then can be converted into
292 * proper tags with preg_replace() calls.
293 * The main class walks through all the filters and and calls this
294 * method. The filters should modify their private $_preparsed
295 * variable, with input from $_text.
296 *
297 * @return none
298 * @access private
299 * @see $_text
300 * @author Stijn de Reede <sjr@gmx.co.uk>
301 */
302 function _preparse()
303 {
304 // default: assign _text to _preparsed, to be overwritten by filters
305 $this->_preparsed = $this->_text;
306
307 // return if this is a subclass
308 if (is_subclass_of($this, 'SSHTMLBBCodeParser')) {
309 return;
310 }
311
312 // walk through the filters and execute _preparse
313 foreach ($this->_filters as $filter) {
314 $filter->setText($this->_preparsed);
315 $filter->_preparse();
316 $this->_preparsed = $filter->getPreparsed();
317 }
318 }
319
320 /**
321 * Builds the tag array from the input string $_text
322 *
323 * An array consisting of tag and text elements is contructed from the
324 * $_preparsed variable. The method uses _buildTag() to check if a tag is
325 * valid and to build the actual tag to be added to the tag array.
326 *
327 * @todo - rewrite whole method, as this one is old and probably slow
328 * - see if a recursive method would be better than an iterative one
329 *
330 * @return none
331 * @access private
332 * @see _buildTag()
333 * @see $_text
334 * @see $_tagArray
335 * @author Stijn de Reede <sjr@gmx.co.uk>
336 */
337 function _buildTagArray()
338 {
339 $this->_tagArray = array();
340 $str = $this->_preparsed;
341 $strPos = 0;
342 $strLength = strlen($str);
343
344 while (($strPos < $strLength)) {
345 $tag = array();
346 $openPos = strpos($str, $this->_options['open'], $strPos);
347 if ($openPos === false) {
348 $openPos = $strLength;
349 $nextOpenPos = $strLength;
350 }
351 if ($openPos + 1 > $strLength) {
352 $nextOpenPos = $strLength;
353 } else {
354 $nextOpenPos = strpos($str, $this->_options['open'], $openPos + 1);
355 if ($nextOpenPos === false) {
356 $nextOpenPos = $strLength;
357 }
358 }
359 $closePos = strpos($str, $this->_options['close'], $strPos);
360 if ($closePos === false) {
361 $closePos = $strLength + 1;
362 }
363
364 if ($openPos == $strPos) {
365 if (($nextOpenPos < $closePos)) {
366 // new open tag before closing tag: treat as text
367 $newPos = $nextOpenPos;
368 $tag['text'] = substr($str, $strPos, $nextOpenPos - $strPos);
369 $tag['type'] = 0;
370 } else {
371 // possible valid tag
372 $newPos = $closePos + 1;
373 $newTag = $this->_buildTag(substr($str, $strPos, $closePos - $strPos + 1));
374 if (($newTag !== false)) {
375 $tag = $newTag;
376 } else {
377 // no valid tag after all
378 $tag['text'] = substr($str, $strPos, $closePos - $strPos + 1);
379 $tag['type'] = 0;
380 }
381 }
382 } else {
383 // just text
384 $newPos = $openPos;
385 $tag['text'] = substr($str, $strPos, $openPos - $strPos);
386 $tag['type'] = 0;
387 }
388
389 // join 2 following text elements
390 if ($tag['type'] === 0 && isset($prev) && $prev['type'] === 0) {
391 $tag['text'] = $prev['text'].$tag['text'];
392 array_pop($this->_tagArray);
393 }
394
395 $this->_tagArray[] = $tag;
396 $prev = $tag;
397 $strPos = $newPos;
398 }
399 }
400
401 /**
402 * Builds a tag from the input string
403 *
404 * This method builds a tag array based on the string it got as an
405 * argument. If the tag is invalid, <false> is returned. The tag
406 * attributes are extracted from the string and stored in the tag
407 * array as an associative array.
408 *
409 * @param string string to build tag from
410 * @return array tag in array format
411 * @access private
412 * @see _buildTagArray()
413 * @author Stijn de Reede <sjr@gmx.co.uk>
414 */
415 function _buildTag($str)
416 {
417 $tag = array('text' => $str, 'attributes' => array());
418
419 if (substr($str, 1, 1) == '/') { // closing tag
420
421 $tag['tag'] = strtolower(substr($str, 2, strlen($str) - 3));
422 if (!in_array($tag['tag'], array_keys($this->_definedTags))) {
423 return false; // nope, it's not valid
424 } else {
425 $tag['type'] = 2;
426 return $tag;
427 }
428 } else { // opening tag
429
430 $tag['type'] = 1;
431 if (strpos($str, ' ') && (strpos($str, '=') === false)) {
432 return false; // nope, it's not valid
433 }
434
435 // tnx to Onno for the regex
436 // split the tag with arguments and all
437 $oe = $this->_options['open_esc'];
438 $ce = $this->_options['close_esc'];
439 $tagArray = array();
440 if (preg_match("!$oe([a-z0-9]+)[^$ce]*$ce!i", $str, $tagArray) == 0) {
441 return false;
442 }
443 $tag['tag'] = strtolower($tagArray[1]);
444 if (!in_array($tag['tag'], array_keys($this->_definedTags))) {
445 return false; // nope, it's not valid
446 }
447
448 // tnx to Onno for the regex
449 // validate the arguments
450 $attributeArray = array();
451 $regex = "=(\"[^\s$ce]+\"|[^\s$ce]";
452 if ($tag['tag'] != 'url') {
453 $regex .= "[^=]";
454 }
455 $regex .= "+)(?=[\s$ce])!i";
456 preg_match_all($regex, $str, $attributeArray, PREG_SET_ORDER);
457 foreach ($attributeArray as $attribute) {
458 $attNam = strtolower($attribute[1]);
459 if (in_array($attNam, array_keys($this->_definedTags[$tag['tag']]['attributes']))) {
460 if ($attribute[2][0] == '"' && $attribute[2][strlen($attribute[2])-1] == '"') {
461 $tag['attributes'][$attNam] = substr($attribute[2], 1, -1);
462 } else {
463 $tag['attributes'][$attNam] = $attribute[2];
464 }
465 }
466 }
467 return $tag;
468 }
469 }
470
471 /**
472 * Validates the tag array, regarding the allowed tags
473 *
474 * While looping through the tag array, two following text tags are
475 * joined, and it is checked that the tag is allowed inside the
476 * last opened tag.
477 * By remembering what tags have been opened it is checked that
478 * there is correct (xml compliant) nesting.
479 * In the end all still opened tags are closed.
480 *
481 * @return none
482 * @access private
483 * @see _isAllowed()
484 * @see $_tagArray
485 * @author Stijn de Reede <sjr@gmx.co.uk>, Seth Price <seth@pricepages.org>
486 */
487 function _validateTagArray()
488 {
489 $newTagArray = array();
490 $openTags = array();
491 foreach ($this->_tagArray as $tag) {
492 $prevTag = end($newTagArray);
493 switch ($tag['type']) {
494 case 0:
495 if (($child = $this->_childNeeded(end($openTags), 'text')) &&
496 $child !== false &&
497 /*
498 * No idea what to do in this case: A child is needed, but
499 * no valid one is returned. We'll ignore it here and live
500 * with it until someone reports a valid bug.
501 */
502 $child !== true )
503 {
504 if (trim($tag['text']) == '') {
505 //just an empty indentation or newline without value?
506 continue;
507 }
508 $newTagArray[] = $child;
509 $openTags[] = $child['tag'];
510 }
511 if ($prevTag['type'] === 0) {
512 $tag['text'] = $prevTag['text'].$tag['text'];
513 array_pop($newTagArray);
514 }
515 $newTagArray[] = $tag;
516 break;
517
518 case 1:
519 if (!$this->_isAllowed(end($openTags), $tag['tag']) ||
520 ($parent = $this->_parentNeeded(end($openTags), $tag['tag'])) === true ||
521 ($child = $this->_childNeeded(end($openTags), $tag['tag'])) === true) {
522 $tag['type'] = 0;
523 if ($prevTag['type'] === 0) {
524 $tag['text'] = $prevTag['text'].$tag['text'];
525 array_pop($newTagArray);
526 }
527 } else {
528 if ($parent) {
529 /*
530 * Avoid use of parent if we can help it. If we are
531 * trying to insert a new parent, but the current tag is
532 * the same as the previous tag, then assume that the
533 * previous tag structure is valid, and add this tag as
534 * a sibling. To add as a sibling, we need to close the
535 * current tag.
536 */
537 if ($tag['tag'] == end($openTags)){
538 $newTagArray[] = $this->_buildTag('[/'.$tag['tag'].']');
539 array_pop($openTags);
540 } else {
541 $newTagArray[] = $parent;
542 $openTags[] = $parent['tag'];
543 }
544 }
545 if ($child) {
546 $newTagArray[] = $child;
547 $openTags[] = $child['tag'];
548 }
549 $openTags[] = $tag['tag'];
550 }
551 $newTagArray[] = $tag;
552 break;
553
554 case 2:
555 if (($tag['tag'] == end($openTags) || $this->_isAllowed(end($openTags), $tag['tag']))) {
556 if (in_array($tag['tag'], $openTags)) {
557 $tmpOpenTags = array();
558 while (end($openTags) != $tag['tag']) {
559 $newTagArray[] = $this->_buildTag('[/'.end($openTags).']');
560 $tmpOpenTags[] = end($openTags);
561 array_pop($openTags);
562 }
563 $newTagArray[] = $tag;
564 array_pop($openTags);
565 /* why is this here? it just seems to break things
566 * (nested lists where closing tags need to be
567 * generated)
568 while (end($tmpOpenTags)) {
569 $tmpTag = $this->_buildTag('['.end($tmpOpenTags).']');
570 $newTagArray[] = $tmpTag;
571 $openTags[] = $tmpTag['tag'];
572 array_pop($tmpOpenTags);
573 }*/
574 }
575 } else {
576 $tag['type'] = 0;
577 if ($prevTag['type'] === 0) {
578 $tag['text'] = $prevTag['text'].$tag['text'];
579 array_pop($newTagArray);
580 }
581 $newTagArray[] = $tag;
582 }
583 break;
584 }
585 }
586 while (end($openTags)) {
587 $newTagArray[] = $this->_buildTag('[/'.end($openTags).']');
588 array_pop($openTags);
589 }
590 $this->_tagArray = $newTagArray;
591 }
592
593 /**
594 * Checks to see if a parent is needed
595 *
596 * Checks to see if the current $in tag has an appropriate parent. If it
597 * does, then it returns false. If a parent is needed, then it returns the
598 * first tag in the list to add to the stack.
599 *
600 * @param array tag that is on the outside
601 * @param array tag that is on the inside
602 * @return boolean false if not needed, tag if needed, true if out
603 * of our minds
604 * @access private
605 * @see _validateTagArray()
606 * @author Seth Price <seth@pricepages.org>
607 */
608 function _parentNeeded($out, $in)
609 {
610 if (!isset($this->_definedTags[$in]['parent']) ||
611 ($this->_definedTags[$in]['parent'] == 'all')
612 ) {
613 return false;
614 }
615
616 $ar = explode('^', $this->_definedTags[$in]['parent']);
617 $tags = explode(',', $ar[1]);
618 if ($ar[0] == 'none'){
619 if ($out && in_array($out, $tags)) {
620 return false;
621 }
622 //Create a tag from the first one on the list
623 return $this->_buildTag('['.$tags[0].']');
624 }
625 if ($ar[0] == 'all' && $out && !in_array($out, $tags)) {
626 return false;
627 }
628 // Tag is needed, we don't know which one. We could make something up,
629 // but it would be so random, I think that it would be worthless.
630 return true;
631 }
632
633 /**
634 * Checks to see if a child is needed
635 *
636 * Checks to see if the current $out tag has an appropriate child. If it
637 * does, then it returns false. If a child is needed, then it returns the
638 * first tag in the list to add to the stack.
639 *
640 * @param array tag that is on the outside
641 * @param array tag that is on the inside
642 * @return boolean false if not needed, tag if needed, true if out
643 * of our minds
644 * @access private
645 * @see _validateTagArray()
646 * @author Seth Price <seth@pricepages.org>
647 */
648 function _childNeeded($out, $in)
649 {
650 if (!isset($this->_definedTags[$out]['child']) ||
651 ($this->_definedTags[$out]['child'] == 'all')
652 ) {
653 return false;
654 }
655
656 $ar = explode('^', $this->_definedTags[$out]['child']);
657 $tags = explode(',', $ar[1]);
658 if ($ar[0] == 'none'){
659 if ($in && in_array($in, $tags)) {
660 return false;
661 }
662 //Create a tag from the first one on the list
663 return $this->_buildTag('['.$tags[0].']');
664 }
665 if ($ar[0] == 'all' && $in && !in_array($in, $tags)) {
666 return false;
667 }
668 // Tag is needed, we don't know which one. We could make something up,
669 // but it would be so random, I think that it would be worthless.
670 return true;
671 }
672
673 /**
674 * Checks to see if a tag is allowed inside another tag
675 *
676 * The allowed tags are extracted from the private _definedTags array.
677 *
678 * @param array tag that is on the outside
679 * @param array tag that is on the inside
680 * @return boolean return true if the tag is allowed, false
681 * otherwise
682 * @access private
683 * @see _validateTagArray()
684 * @author Stijn de Reede <sjr@gmx.co.uk>
685 */
686 function _isAllowed($out, $in)
687 {
688 if (!$out || ($this->_definedTags[$out]['allowed'] == 'all')) {
689 return true;
690 }
691 if ($this->_definedTags[$out]['allowed'] == 'none') {
692 return false;
693 }
694
695 $ar = explode('^', $this->_definedTags[$out]['allowed']);
696 $tags = explode(',', $ar[1]);
697 if ($ar[0] == 'none' && in_array($in, $tags)) {
698 return true;
699 }
700 if ($ar[0] == 'all' && in_array($in, $tags)) {
701 return false;
702 }
703 return false;
704 }
705
706 /**
707 * Builds a parsed string based on the tag array
708 *
709 * The correct html and attribute values are extracted from the private
710 * _definedTags array.
711 *
712 * @return none
713 * @access private
714 * @see $_tagArray
715 * @see $_parsed
716 * @author Stijn de Reede <sjr@gmx.co.uk>
717 */
718 function _buildParsedString()
719 {
720 $this->_parsed = '';
721 foreach ($this->_tagArray as $tag) {
722 switch ($tag['type']) {
723
724 // just text
725 case 0:
726 $this->_parsed .= $tag['text'];
727 break;
728
729 // opening tag
730 case 1:
731 $this->_parsed .= '<'.$this->_definedTags[$tag['tag']]['htmlopen'];
732 if ($this->_options['quotestyle'] == 'single') $q = "'";
733 if ($this->_options['quotestyle'] == 'double') $q = '"';
734 foreach ($tag['attributes'] as $a => $v) {
735 //prevent XSS attacks. IMHO this is not enough, though...
736 //@see http://pear.php.net/bugs/bug.php?id=5609
737 $v = preg_replace('#(script|about|applet|activex|chrome):#is', "\\1:", $v);
738 $v = htmlspecialchars($v);
739 $v = str_replace('&amp;', '&', $v);
740
741 if (($this->_options['quotewhat'] == 'nothing') ||
742 (($this->_options['quotewhat'] == 'strings') && is_numeric($v))
743 ) {
744 $this->_parsed .= ' '.sprintf($this->_definedTags[$tag['tag']]['attributes'][$a], $v, '');
745 } else {
746 $this->_parsed .= ' '.sprintf($this->_definedTags[$tag['tag']]['attributes'][$a], $v, $q);
747 }
748 }
749 if ($this->_definedTags[$tag['tag']]['htmlclose'] == '' && $this->_options['xmlclose']) {
750 $this->_parsed .= ' /';
751 }
752 $this->_parsed .= '>';
753 break;
754
755 // closing tag
756 case 2:
757 if ($this->_definedTags[$tag['tag']]['htmlclose'] != '') {
758 $this->_parsed .= '</'.$this->_definedTags[$tag['tag']]['htmlclose'].'>';
759 }
760 break;
761 }
762 }
763 }
764
765 /**
766 * Sets text in the object to be parsed
767 *
768 * @param string the text to set in the object
769 * @return none
770 * @access public
771 * @see getText()
772 * @see $_text
773 * @author Stijn de Reede <sjr@gmx.co.uk>
774 */
775 function setText($str)
776 {
777 $this->_text = $str;
778 }
779
780 /**
781 * Gets the unparsed text from the object
782 *
783 * @return string the text set in the object
784 * @access public
785 * @see setText()
786 * @see $_text
787 * @author Stijn de Reede <sjr@gmx.co.uk>
788 */
789 function getText()
790 {
791 return $this->_text;
792 }
793
794 /**
795 * Gets the preparsed text from the object
796 *
797 * @return string the text set in the object
798 * @access public
799 * @see _preparse()
800 * @see $_preparsed
801 * @author Stijn de Reede <sjr@gmx.co.uk>
802 */
803 function getPreparsed()
804 {
805 return $this->_preparsed;
806 }
807
808 /**
809 * Gets the parsed text from the object
810 *
811 * @return string the parsed text set in the object
812 * @access public
813 * @see parse()
814 * @see $_parsed
815 * @author Stijn de Reede <sjr@gmx.co.uk>
816 */
817 function getParsed()
818 {
819 return $this->_parsed;
820 }
821
822 /**
823 * Parses the text set in the object
824 *
825 * @return none
826 * @access public
827 * @see _preparse()
828 * @see _buildTagArray()
829 * @see _validateTagArray()
830 * @see _buildParsedString()
831 * @author Stijn de Reede <sjr@gmx.co.uk>
832 */
833 function parse()
834 {
835 $this->_preparse();
836 $this->_buildTagArray();
837 $this->_validateTagArray();
838 $this->_buildParsedString();
839 }
840
841 /**
842 * Quick method to do setText(), parse() and getParsed at once
843 *
844 * @return none
845 * @access public
846 * @see parse()
847 * @see $_text
848 * @author Stijn de Reede <sjr@gmx.co.uk>
849 */
850 function qparse($str)
851 {
852 $this->_text = $str;
853 $this->parse();
854 return $this->_parsed;
855 }
856
857 /**
858 * Quick static method to do setText(), parse() and getParsed at once
859 *
860 * @return none
861 * @access public
862 * @see parse()
863 * @see $_text
864 * @author Stijn de Reede <sjr@gmx.co.uk>
865 */
866 function staticQparse($str)
867 {
868 $p = new SSHTMLBBCodeParser();
869 $str = $p->qparse($str);
870 unset($p);
871 return $str;
872 }
873 }
874 ?>