Jump to content

Help needed with stackbased UBB parser


Fluffy Convict

Recommended Posts

Currently, Drupal is lacking proper UBB support, so I'd thought it would be a good idea to write a stack-based UBB parser. For me, this is a first, but also a challenge. I am stuck now, and really could use some help. Regarding the code below, I have two questions:

 

[*]the code gets stuck in a never-ending loop when the first tag in $text is a closing tag (e.g. [/b ]). Why, and what's your suggested solution?

[*]I want the parser to support "double tags" (tags that require an opening and closing, like [b ]) and "single tags" (like [ hr] and perhaps ones you define yourself). I'm not sure on how to implement the handling of single tags here - any suggestions?

 



$text = 'The [b foo="fVal"]quick [i bar="bVal"]brown[/i] [u]fox[/b] jumped over the [b]lazy[/b] dog. [img src="http://www.google.com/intl/en_ALL/images/logo.gif"]';
       
echo ubbParse($text);

function ubbParse($text) {
  $validDoubleTags = array('b', 'i', 'foo', 'span', 'table', 'tr', 'td', 'cite');
  $validSingleTags = array('img', 'hr');
  $lastNode = array();
  $stack    = array();
  $size    = 0;

  while (false !== ($node = ubbSearchTag($text))) {
    // First node, special conditions
    if (empty($lastNode)) {
      // Text before node on stack
      array_push($stack, substr($text, 0, $node['OPEN']));
      $size = array_push($stack, $node); // First node on stack
    }
    // All but the first node
    else {
      // Opening tag
      if ('/' != substr($node['TAG'], 0, 1)) {
        // Text before node on stack
        array_push($stack, substr($text, $lastNode['CLOSE'] + 1, $node['OPEN'] - 1 - $lastNode['CLOSE']));
        // Add node to the stack
        $size = array_push($stack, $node);
      }
      // Closing tag
      else {
        $prevText = '';
        // Grab all text on the end of the array
        $prevText = _popText($stack);
        // Text from lastNode to current node (fox, lazy)
        $prevText.= substr($text, $lastNode['CLOSE'] + 1, $node['OPEN'] - 1 - $lastNode['CLOSE']);
        $match = array();
       
        do {
          $match = array_pop($stack);
          if (in_array($match['TAG'], $validDoubleTags)) {
            $output = "<{$match['TAG']}";
            if (isset($match['ATTR'])) {
              foreach ($match['ATTR'] as $id => $attribute) {
                $output.= " $id=\"$attribute\"";
              }
            }
            $output.= ">{$prevText}</{$match['TAG']}>";
            $prevText = _popText($stack) . $output;
          }
          else {
            // Do not automagically close the tag
            $output = "[{$match['TAG']}]{$prevText}";
            $prevText = _popText($stack) . $output;
          }
        }
        while ($match['TAG'] != substr($node['TAG'], 1));
       
        array_push($stack, $prevText);
      }
    }
    $lastNode = $node;
  }
  $stack[] = substr($text, $lastNode['CLOSE'] + 1, strlen($text) - 1 - $lastNode['CLOSE']);
 
  return implode('', $stack);
}

/**
* Every time it's called, it returns the next tag in $text
*
* @param string $text
* @return array|false UBB Node or false when no more nodes
*/
function ubbSearchTag($text) {
  static $curr = 0;

  $length = strlen($text);
  $open  = strpos($text, '[', $curr);
  $close  = strpos($text, ']', $curr);

  if (0 == $close) {
    return false;
  }

  $slice = substr($text, $open, $close - $open);
  $tag = array();
  preg_match('/\[([^ \[\]]*) ?/i', $slice, $tag);

  $attributes = array();
  preg_match_all('/ ([^="\[\]]*)="([^="]*)"/i', $slice, $attributes);

  $node = array(
    'TAG'  => strtolower($tag[1]),
    'OPEN'  => $open,
    'CLOSE' => $close,
  );
 
  foreach ($attributes[0] as $key => $attribute) {
    $node['ATTR'][$attributes[1][$key]] = $attributes[2][$key];
  }

  unset($tag, $attributes);
  $curr = $close + 1;

  return $node;
}

/**
* Pops off all array elements that only contain text and return them all as one
* string
*/
function _popText(&$stack) {
  $text = '';
  while (!is_array($stack[count($stack) - 1])) {
    $text = array_pop($stack) . $text;
    if (0 == count($stack)) {
      break;
    }
  }
  return $text;
}

Any help would be greatly appreciated (and credited for, of course :))!

Link to comment
https://forums.phpfreaks.com/topic/136146-help-needed-with-stackbased-ubb-parser/
Share on other sites

Archived

This topic is now archived and is closed to further replies.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.