HTML_BBCodeParser2
[ class tree: HTML_BBCodeParser2 ] [ index: HTML_BBCodeParser2 ] [ all elements ]

Source for file BBCodeParser2.php

Documentation is available at BBCodeParser2.php

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

Documentation generated on Sat, 03 Nov 2012 14:30:03 +0000 by phpDocumentor 1.4.3. PEAR Logo Copyright © PHP Group 2004.