コード例 #1
0
ファイル: Parser.php プロジェクト: skywalker512/FlarumChina
 protected function processEndTag(Tag $tag)
 {
     $tagName = $tag->getName();
     if (empty($this->cntOpen[$tagName])) {
         return;
     }
     $closeTags = array();
     $i = \count($this->openTags);
     while (--$i >= 0) {
         $openTag = $this->openTags[$i];
         if ($tag->canClose($openTag)) {
             break;
         }
         $closeTags[] = $openTag;
         ++$this->currentFixingCost;
     }
     if ($i < 0) {
         $this->logger->debug('Skipping end tag with no start tag', array('tag' => $tag));
         return;
     }
     $keepReopening = (bool) ($this->currentFixingCost < $this->maxFixingCost);
     $reopenTags = array();
     foreach ($closeTags as $openTag) {
         $openTagName = $openTag->getName();
         if ($keepReopening) {
             if ($openTag->getFlags() & self::RULE_AUTO_REOPEN) {
                 $reopenTags[] = $openTag;
             } else {
                 $keepReopening = \false;
             }
         }
         $tagPos = $tag->getPos();
         if ($openTag->getFlags() & self::RULE_IGNORE_WHITESPACE) {
             $tagPos = $this->getMagicPos($tagPos);
         }
         $endTag = new Tag(Tag::END_TAG, $openTagName, $tagPos, 0);
         $endTag->setFlags($openTag->getFlags());
         $this->outputTag($endTag);
         $this->popContext();
     }
     $this->outputTag($tag);
     $this->popContext();
     if (!empty($closeTags) && $this->currentFixingCost < $this->maxFixingCost) {
         $ignorePos = $this->pos;
         $i = \count($this->tagStack);
         while (--$i >= 0 && ++$this->currentFixingCost < $this->maxFixingCost) {
             $upcomingTag = $this->tagStack[$i];
             if ($upcomingTag->getPos() > $ignorePos || $upcomingTag->isStartTag()) {
                 break;
             }
             $j = \count($closeTags);
             while (--$j >= 0 && ++$this->currentFixingCost < $this->maxFixingCost) {
                 if ($upcomingTag->canClose($closeTags[$j])) {
                     \array_splice($closeTags, $j, 1);
                     if (isset($reopenTags[$j])) {
                         \array_splice($reopenTags, $j, 1);
                     }
                     $ignorePos = \max($ignorePos, $upcomingTag->getPos() + $upcomingTag->getLen());
                     break;
                 }
             }
         }
         if ($ignorePos > $this->pos) {
             $this->outputIgnoreTag(new Tag(Tag::SELF_CLOSING_TAG, 'i', $this->pos, $ignorePos - $this->pos));
         }
     }
     foreach ($reopenTags as $startTag) {
         $newTag = $this->addCopyTag($startTag, $this->pos, 0);
         $endTag = $startTag->getEndTag();
         if ($endTag) {
             $newTag->pairWith($endTag);
         }
     }
 }
コード例 #2
0
ファイル: TagTest.php プロジェクト: CryptArc/TextFormatter
 /**
  * @testdox canClose() returns TRUE if the tags are paired together
  */
 public function testCanClosePaired()
 {
     $startTag = new Tag(Tag::START_TAG, 'X', 0, 0);
     $endTag = new Tag(Tag::END_TAG, 'X', 1, 0);
     $endTag->pairWith($startTag);
     $this->assertTrue($endTag->canClose($startTag));
 }
コード例 #3
0
ファイル: Parser.php プロジェクト: CryptArc/TextFormatter
 /**
  * Process given end tag at current position
  *
  * @param  Tag  $tag end tag
  * @return void
  */
 protected function processEndTag(Tag $tag)
 {
     $tagName = $tag->getName();
     if (empty($this->cntOpen[$tagName])) {
         // This is an end tag with no start tag
         return;
     }
     /**
      * @var array List of tags need to be closed before given tag
      */
     $closeTags = [];
     // Iterate through all open tags from last to first to find a match for our tag
     $i = count($this->openTags);
     while (--$i >= 0) {
         $openTag = $this->openTags[$i];
         if ($tag->canClose($openTag)) {
             break;
         }
         $closeTags[] = $openTag;
         ++$this->currentFixingCost;
     }
     if ($i < 0) {
         // Did not find a matching tag
         $this->logger->debug('Skipping end tag with no start tag', ['tag' => $tag]);
         return;
     }
     // Only reopen tags if we haven't exceeded our "fixing" budget
     $keepReopening = (bool) ($this->currentFixingCost < $this->maxFixingCost);
     // Iterate over tags that are being closed, output their end tag and collect tags to be
     // reopened
     $reopenTags = [];
     foreach ($closeTags as $openTag) {
         $openTagName = $openTag->getName();
         // Test whether this tag should be reopened automatically
         if ($keepReopening) {
             if ($openTag->getFlags() & self::RULE_AUTO_REOPEN) {
                 $reopenTags[] = $openTag;
             } else {
                 $keepReopening = false;
             }
         }
         // Find the earliest position we can close this open tag
         $tagPos = $tag->getPos();
         if ($openTag->getFlags() & self::RULE_IGNORE_WHITESPACE) {
             $tagPos = $this->getMagicPos($tagPos);
         }
         // Output an end tag to close this start tag, then update the context
         $endTag = new Tag(Tag::END_TAG, $openTagName, $tagPos, 0);
         $endTag->setFlags($openTag->getFlags());
         $this->outputTag($endTag);
         $this->popContext();
     }
     // Output our tag, moving the cursor past it, then update the context
     $this->outputTag($tag);
     $this->popContext();
     // If our fixing budget allows it, peek at upcoming tags and remove end tags that would
     // close tags that are already being closed now. Also, filter our list of tags being
     // reopened by removing those that would immediately be closed
     if (!empty($closeTags) && $this->currentFixingCost < $this->maxFixingCost) {
         /**
          * @var integer Rightmost position of the portion of text to ignore
          */
         $ignorePos = $this->pos;
         $i = count($this->tagStack);
         while (--$i >= 0 && ++$this->currentFixingCost < $this->maxFixingCost) {
             $upcomingTag = $this->tagStack[$i];
             // Test whether the upcoming tag is positioned at current "ignore" position and it's
             // strictly an end tag (not a start tag or a self-closing tag)
             if ($upcomingTag->getPos() > $ignorePos || $upcomingTag->isStartTag()) {
                 break;
             }
             // Test whether this tag would close any of the tags we're about to reopen
             $j = count($closeTags);
             while (--$j >= 0 && ++$this->currentFixingCost < $this->maxFixingCost) {
                 if ($upcomingTag->canClose($closeTags[$j])) {
                     // Remove the tag from the lists and reset the keys
                     array_splice($closeTags, $j, 1);
                     if (isset($reopenTags[$j])) {
                         array_splice($reopenTags, $j, 1);
                     }
                     // Extend the ignored text to cover this tag
                     $ignorePos = max($ignorePos, $upcomingTag->getPos() + $upcomingTag->getLen());
                     break;
                 }
             }
         }
         if ($ignorePos > $this->pos) {
             /**
              * @todo have a method that takes (pos,len) rather than a Tag
              */
             $this->outputIgnoreTag(new Tag(Tag::SELF_CLOSING_TAG, 'i', $this->pos, $ignorePos - $this->pos));
         }
     }
     // Re-add tags that need to be reopened, at current cursor position
     foreach ($reopenTags as $startTag) {
         $newTag = $this->addCopyTag($startTag, $this->pos, 0);
         // Re-pair the new tag
         $endTag = $startTag->getEndTag();
         if ($endTag) {
             $newTag->pairWith($endTag);
         }
     }
 }