You are here

private function XBBCodeFilter::buildTree in Extensible BBCode 8.2

1 call to XBBCodeFilter::buildTree()
XBBCodeFilter::process in src/Plugin/Filter/XBBCodeFilter.php
Performs the filter processing.

File

src/Plugin/Filter/XBBCodeFilter.php, line 139
Contains Drupal\xbbcode\Plugin\Filter\XBBCodeFilter.

Class

XBBCodeFilter
Provides a filter that converts BBCode to HTML.

Namespace

Drupal\xbbcode\Plugin\Filter

Code

private function buildTree($text) {

  // Find all opening and closing tags in the text.
  preg_match_all(XBBCODE_RE_TAG, $text, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE);

  // Initialize the name tracker, and the list of valid tags.
  $open_by_name = [];
  $tags = [];
  foreach ($matches as $match) {
    $tag = new XBBCodeTagMatch($match);
    if (isset($this->tags[$tag->name])) {
      $tag->selfclosing = $this->tags[$tag->name]->options->selfclosing;
      $tags[] = $tag;
      $open_by_name[$tag->name] = 0;
    }
  }

  // Initialize the stack with a root element.
  $stack = [
    new XBBCodeRootElement(),
  ];
  foreach ($tags as $tag) {

    // Add text before the new tag to the parent
    end($stack)
      ->advance($text, $tag->start);

    // Case 1: The tag is opening and not self-closing.
    if (!$tag->closing && !$tag->selfclosing) {

      // Stack the open tag, and increment the tracker.
      array_push($stack, $tag);
      $open_by_name[$tag->name]++;
    }
    elseif ($tag->selfclosing) {
      end($stack)
        ->append($tag, $tag->end);
    }
    elseif ($open_by_name[$tag->name]) {
      $open_by_name[$tag->name]--;

      // Find the last matching opening tag, breaking any unclosed tag since then.
      while (end($stack)->name != $tag->name) {
        $dangling = array_pop($stack);
        end($stack)
          ->breakTag($dangling);
        $open_by_name[$dangling->name]--;
      }
      $current = array_pop($stack);
      $current
        ->advance($text, $tag->start);
      $current->source = substr($text, $current->end, $current->offset - $current->end);
      $current->closer = $tag;
      end($stack)
        ->append($current, $tag->end);
    }
  }

  // Add the remainder of the text, and then break any tags still open.
  end($stack)
    ->advance($text, strlen($text));
  while (count($stack) > 1) {
    $dangling = array_pop($stack);
    end($stack)
      ->breakTag($dangling);
  }
  return end($stack);
}