Пример #1
0
 /**
  * Capture all attributes in given string
  *
  * @param  Tag    $tag    Target tag
  * @param  string $elName Name of the HTML element
  * @param  string $str    String containing the attribute declarations
  * @return void
  */
 protected function captureAttributes(Tag $tag, $elName, $str)
 {
     preg_match_all('/[a-z][-a-z0-9]*(?>\\s*=\\s*(?>"[^"]*"|\'[^\']*\'|[^\\s"\'=<>`]+))?/i', $str, $attrMatches);
     foreach ($attrMatches[0] as $attrMatch) {
         $pos = strpos($attrMatch, '=');
         /**
          * If there's no equal sign, it's a boolean attribute and we generate a value equal
          * to the attribute's name, lowercased
          *
          * @link http://www.w3.org/html/wg/drafts/html/master/single-page.html#boolean-attributes
          */
         if ($pos === false) {
             $pos = strlen($attrMatch);
             $attrMatch .= '=' . strtolower($attrMatch);
         }
         // Normalize the attribute name, remove the whitespace around its value to account
         // for cases like <b title = "foo"/>
         $attrName = strtolower(trim(substr($attrMatch, 0, $pos)));
         $attrValue = trim(substr($attrMatch, 1 + $pos));
         // Use the attribute's alias if applicable
         if (isset($this->config['aliases'][$elName][$attrName])) {
             $attrName = $this->config['aliases'][$elName][$attrName];
         }
         // Remove quotes around the value
         if ($attrValue[0] === '"' || $attrValue[0] === "'") {
             $attrValue = substr($attrValue, 1, -1);
         }
         $tag->setAttribute($attrName, html_entity_decode($attrValue, ENT_QUOTES, 'UTF-8'));
     }
 }
Пример #2
0
 /**
  * @testdox The [MEDIA] tag transfers its priority to the tag it creates
  */
 public function testTagPriority()
 {
     $newTag = $this->getMockBuilder('s9e\\TextFormatter\\Parser\\Tag')->disableOriginalConstructor()->setMethods(['setAttributes', 'setSortPriority'])->getMock();
     $newTag->expects($this->once())->method('setSortPriority')->with(123);
     $tagStack = $this->getMockBuilder('s9e\\TextFormatter\\Parser')->disableOriginalConstructor()->setMethods(['addSelfClosingTag'])->getMock();
     $tagStack->expects($this->once())->method('addSelfClosingTag')->will($this->returnValue($newTag));
     $tag = new Tag(Tag::START_TAG, 'MEDIA', 0, 0);
     $tag->setAttribute('media', 'foo');
     $tag->setSortPriority(123);
     Parser::filterTag($tag, $tagStack, ['foo.invalid' => 'foo']);
 }
Пример #3
0
 protected function captureAttributes(Tag $tag, $elName, $str)
 {
     \preg_match_all('/[a-z][-a-z0-9]*(?>\\s*=\\s*(?>"[^"]*"|\'[^\']*\'|[^\\s"\'=<>`]+))?/i', $str, $attrMatches);
     foreach ($attrMatches[0] as $attrMatch) {
         $pos = \strpos($attrMatch, '=');
         if ($pos === \false) {
             $pos = \strlen($attrMatch);
             $attrMatch .= '=' . \strtolower($attrMatch);
         }
         $attrName = \strtolower(\trim(\substr($attrMatch, 0, $pos)));
         $attrValue = \trim(\substr($attrMatch, 1 + $pos));
         if (isset($this->config['aliases'][$elName][$attrName])) {
             $attrName = $this->config['aliases'][$elName][$attrName];
         }
         if ($attrValue[0] === '"' || $attrValue[0] === "'") {
             $attrValue = \substr($attrValue, 1, -1);
         }
         $tag->setAttribute($attrName, \html_entity_decode($attrValue, \ENT_QUOTES, 'UTF-8'));
     }
 }
Пример #4
0
 protected function setLinkAttributes(Tag $tag, $linkInfo, $attrName)
 {
     $url = $linkInfo;
     $title = '';
     $pos = \strpos($linkInfo, ' ');
     if ($pos !== \false) {
         $url = \substr($linkInfo, 0, $pos);
         $title = \substr(\trim(\substr($linkInfo, $pos)), 1, -1);
     }
     $tag->setAttribute($attrName, $this->decode($url));
     if ($title > '') {
         $tag->setAttribute('title', $this->decode($title));
     }
 }
Пример #5
0
 /**
  * Truncate the replacement text set in a LINK_TEXT tag
  *
  * @param  \s9e\TextFormatter\Parser\Tag $tag LINK_TEXT tag
  * @return bool                               Always true to indicate that the tag is valid
  */
 public function truncate_text(\s9e\TextFormatter\Parser\Tag $tag)
 {
     $text = $tag->getAttribute('text');
     if (utf8_strlen($text) > 55) {
         $text = utf8_substr($text, 0, 39) . ' ... ' . utf8_substr($text, -10);
     }
     $tag->setAttribute('text', $text);
     return true;
 }
Пример #6
0
 protected static function tagIsMissingAnyAttribute(Tag $tag, array $attrNames)
 {
     foreach ($attrNames as $attrName) {
         if (!$tag->hasAttribute($attrName)) {
             return \true;
         }
     }
     return \false;
 }
 /**
  * @testdox filterAttributes() calls the logger's setAttribute() and unsetAttribute() methods for each attribute with a filterChain
  */
 public function testFilterAttributesCallsLoggerSetAttribute()
 {
     $logger = $this->getMock('s9e\\TextFormatter\\Parser\\Logger', ['setAttribute', 'unsetAttribute']);
     $logger->expects($this->at(0))->method('setAttribute')->with('foo');
     $logger->expects($this->at(2))->method('setAttribute')->with('bar');
     $logger->expects($this->exactly(2))->method('unsetAttribute');
     $tagConfig = new TagConfig();
     $tagConfig->attributes->add('foo')->filterChain->append(function () {
     });
     $tagConfig->attributes->add('bar')->filterChain->append(function () {
     });
     $tag = new Tag(Tag::SELF_CLOSING_TAG, 'X', 0, 0);
     $tag->setAttribute('foo', 'foo');
     $tag->setAttribute('bar', 'bar');
     Parser::filterAttributes($tag, $tagConfig->asConfig(), [], $logger);
 }
Пример #8
0
 /**
  * Set a URL or IMG tag's attributes
  *
  * @param  Tag      $tag       URL or IMG tag
  * @param  array    $m         Regexp captures
  * @param  string[] $attrNames List of attribute names
  * @return void
  */
 protected function setLinkAttributes(Tag $tag, array $m, array $attrNames)
 {
     if (isset($m[3])) {
         $attrValues = $this->getInlineLinkAttributes($m);
     } else {
         $label = isset($m[2]) ? $m[2][0] : $m[1][0];
         $attrValues = $this->getReferenceLinkAttributes($label);
     }
     foreach ($attrValues as $k => $attrValue) {
         $tag->setAttribute($attrNames[$k], $attrValue);
     }
 }
Пример #9
0
 /**
  * @testdox gc() removes a paired tag's reference
  */
 public function testGc()
 {
     $startTag = new Tag(Tag::START_TAG, 'X', 0, 0);
     $endTag = new Tag(Tag::END_TAG, 'X', 1, 0);
     $startTag->pairWith($endTag);
     $this->assertSame($endTag, $startTag->getEndTag());
     $this->assertSame($startTag, $endTag->getStartTag());
     $startTag->gc();
     $this->assertNull($startTag->getEndTag());
     $endTag->gc();
     $this->assertNull($endTag->getStartTag());
 }
Пример #10
0
 protected static function compareTags(Tag $a, Tag $b)
 {
     $aPos = $a->getPos();
     $bPos = $b->getPos();
     if ($aPos !== $bPos) {
         return $bPos - $aPos;
     }
     if ($a->getSortPriority() !== $b->getSortPriority()) {
         return $b->getSortPriority() - $a->getSortPriority();
     }
     $aLen = $a->getLen();
     $bLen = $b->getLen();
     if (!$aLen || !$bLen) {
         if (!$aLen && !$bLen) {
             $order = array(Tag::END_TAG => 0, Tag::SELF_CLOSING_TAG => 1, Tag::START_TAG => 2);
             return $order[$b->getType()] - $order[$a->getType()];
         }
         return $aLen ? -1 : 1;
     }
     return $aLen - $bLen;
 }
Пример #11
0
    public function getData()
    {
        return [['foo bar', '<r><X><Y>foo</Y> <Z>bar</Z></X></r>', function ($configurator) {
            $configurator->tags->add('X');
            $configurator->tags->add('Y');
            $configurator->tags->add('Z');
        }, function ($parser) {
            $parser->addStartTag('X', 0, 0);
            $parser->addSelfClosingTag('Y', 0, 3);
            $parser->addSelfClosingTag('Z', 4, 3);
            $parser->addEndTag('X', 7, 0);
        }], ['foo bar', '<r>foo <X>bar</X></r>', function ($configurator) {
            $configurator->tags->add('X');
        }, function ($parser) {
            $parser->addStartTag('X', 4, 0);
        }], ['foo bar', '<t>foo bar</t>', null, function ($parser) {
            $parser->addStartTag('X', 4, 0);
        }], ['foo bar', '<r><X>fo<Y>o</Y></X> bar</r>', function ($configurator) {
            $configurator->tags->add('X');
            $configurator->tags->add('Y');
        }, function ($parser) {
            $parser->addStartTag('X', 0, 0);
            $parser->addEndTag('X', 3, 0);
            $parser->addStartTag('Y', 2, 0);
            $parser->addEndTag('Y', 4, 1);
        }], ['foo bar', '<r><X>fo<Y>o</Y></X><Y> b</Y>ar</r>', function ($configurator) {
            $configurator->tags->add('X');
            $configurator->tags->add('Y')->rules->autoReopen();
        }, function ($parser) {
            $parser->addStartTag('X', 0, 0);
            $parser->addEndTag('X', 3, 0);
            $parser->addStartTag('Y', 2, 0);
            $parser->addEndTag('Y', 5, 0);
        }], ['foo bar', '<r><X>fo<Y attr="foo">o</Y></X><Y attr="foo"> b</Y>ar</r>', function ($configurator) {
            $configurator->tags->add('X');
            $tag = $configurator->tags->add('Y');
            $tag->attributes->add('attr')->required = false;
            $tag->rules->autoReopen();
        }, function ($parser) {
            $parser->addStartTag('X', 0, 0);
            $parser->addEndTag('X', 3, 0);
            $parser->addStartTag('Y', 2, 0)->setAttribute('attr', 'foo');
            $parser->addEndTag('Y', 5, 0);
        }], ['x [b][i]...[/b][/i] y', '<r>x <B><s>[b]</s><I><s>[i]</s>...</I><e>[/b]</e></B><i>[/i]</i> y</r>', function ($configurator) {
            $configurator->tags->add('B');
            $configurator->tags->add('I')->rules->autoReopen();
        }, function ($parser) {
            $parser->addStartTag('B', 2, 3);
            $parser->addStartTag('I', 5, 3);
            $parser->addEndTag('B', 11, 4);
            $parser->addEndTag('I', 15, 4);
        }], ['x [b][i]...[/b]![/i] y', '<r>x <B><s>[b]</s><I><s>[i]</s>...</I><e>[/b]</e></B><I>!<e>[/i]</e></I> y</r>', function ($configurator) {
            $configurator->tags->add('B');
            $configurator->tags->add('I')->rules->autoReopen();
        }, function ($parser) {
            $parser->addStartTag('B', 2, 3);
            $parser->addStartTag('I', 5, 3);
            $parser->addEndTag('B', 11, 4);
            $parser->addEndTag('I', 16, 4);
        }], ['x [b][i][u]...[/b][/u][/i] y', '<r>x <B><s>[b]</s><I><s>[i]</s><U><s>[u]</s>...</U></I><e>[/b]</e></B><i>[/u][/i]</i> y</r>', function ($configurator) {
            $configurator->tags->add('B');
            $configurator->tags->add('I')->rules->autoReopen();
            $configurator->tags->add('U')->rules->autoReopen();
        }, function ($parser) {
            $parser->addStartTag('B', 2, 3);
            $parser->addStartTag('I', 5, 3);
            $parser->addStartTag('U', 8, 3);
            $parser->addEndTag('B', 14, 4);
            $parser->addEndTag('U', 18, 4);
            $parser->addEndTag('I', 22, 4);
        }], ['x [b][i][u]...[/b][/u][/i] y', '<r>x <B><s>[b]</s><I><s>[i]</s><U><s>[u]</s>...</U></I><e>[/b]</e></B><i>[/u][/i]</i> y</r>', function ($configurator) {
            $configurator->tags->add('B');
            $configurator->tags->add('I');
            $configurator->tags->add('U');
        }, function ($parser) {
            $parser->addStartTag('B', 2, 3);
            $parser->addStartTag('I', 5, 3);
            $parser->addStartTag('U', 8, 3);
            $parser->addEndTag('B', 14, 4);
            $parser->addEndTag('U', 18, 4);
            $parser->addEndTag('I', 22, 4);
        }], ['x [b][i][u]...[/b][/i][/u] y', '<r>x <B><s>[b]</s><I><s>[i]</s><U><s>[u]</s>...</U></I><e>[/b]</e></B><i>[/i][/u]</i> y</r>', function ($configurator) {
            $configurator->tags->add('B');
            $configurator->tags->add('I')->rules->autoReopen();
            $configurator->tags->add('U')->rules->autoReopen();
        }, function ($parser) {
            $parser->addStartTag('B', 2, 3);
            $parser->addStartTag('I', 5, 3);
            $parser->addStartTag('U', 8, 3);
            $parser->addEndTag('B', 14, 4);
            $parser->addEndTag('I', 18, 4);
            $parser->addEndTag('U', 22, 4);
        }], ['x [b][i][u]...[/b][/i][/u] y', '<r>x <B><s>[b]</s><I><s>[i]</s><U><s>[u]</s>...</U></I><e>[/b]</e></B>[/i][/u] y</r>', function ($configurator) {
            $configurator->tags->add('B');
            $configurator->tags->add('I')->rules->autoReopen();
            $configurator->tags->add('U')->rules->autoReopen();
        }, function ($parser) {
            // Set maxFixingCost to 2 so that it allows [u] and [i] to be closed, without
            // spending any efforts on reopening them
            $parser->maxFixingCost = 2;
            $parser->addStartTag('B', 2, 3);
            $parser->addStartTag('I', 5, 3);
            $parser->addStartTag('U', 8, 3);
            $parser->addEndTag('B', 14, 4);
            $parser->addEndTag('I', 18, 4);
            $parser->addEndTag('U', 22, 4);
        }], ['x [b][i][u]...[/b][/i]u[/u] y', '<r>x <B><s>[b]</s><I><s>[i]</s><U><s>[u]</s>...</U></I><e>[/b]</e></B><i>[/i]</i><U>u<e>[/u]</e></U> y</r>', function ($configurator) {
            $configurator->tags->add('B');
            $configurator->tags->add('I')->rules->autoReopen();
            $configurator->tags->add('U')->rules->autoReopen();
        }, function ($parser) {
            $parser->addStartTag('B', 2, 3);
            $parser->addStartTag('I', 5, 3);
            $parser->addStartTag('U', 8, 3);
            $parser->addEndTag('B', 14, 4);
            $parser->addEndTag('I', 18, 4);
            $parser->addEndTag('U', 23, 4);
        }], ['x [i][b][u]...[/b][/i][/u] y', '<r>x <I><s>[i]</s><B><s>[b]</s><U><s>[u]</s>...</U><e>[/b]</e></B><e>[/i]</e></I><i>[/u]</i> y</r>', function ($configurator) {
            $configurator->tags->add('B')->rules->autoReopen();
            $configurator->tags->add('I')->rules->autoReopen();
            $configurator->tags->add('U')->rules->autoReopen();
        }, function ($parser) {
            $parser->addStartTag('I', 2, 3);
            $parser->addStartTag('B', 5, 3);
            $parser->addStartTag('U', 8, 3);
            $parser->addEndTag('B', 14, 4);
            $parser->addEndTag('I', 18, 4);
            $parser->addEndTag('U', 22, 4);
        }], ['foo bar', '<r>foo <X>bar</X></r>', function ($configurator) {
            $configurator->tags->add('X');
            $configurator->tags->add('Y');
        }, function ($parser) {
            $parser->addStartTag('X', 4, 0);
            $parser->addEndTag('Y', 5, 0);
        }], ['foo bar', '<r><X>foo</X> bar</r>', function ($configurator) {
            $configurator->tags->add('X');
        }, function ($parser) {
            $parser->addSelfClosingTag('X', 0, 3);
            $parser->addSelfClosingTag('X', 0, 3);
            $parser->addSelfClosingTag('X', 1, 1);
        }], ['fooo bar', '<r><X>f<X>oo</X>o</X> bar</r>', function ($configurator) {
            $configurator->tags->add('X')->nestingLimit = 2;
        }, function ($parser) {
            $parser->addStartTag('X', 0, 0);
            $parser->addStartTag('X', 1, 0);
            $parser->addSelfClosingTag('X', 2, 1);
            $parser->addEndTag('X', 3, 0);
            $parser->addEndTag('X', 4, 0);
        }, [['err', 'Nesting limit exceeded', ['tag' => $this->runClosure(function () {
            $tag = new Tag(Tag::SELF_CLOSING_TAG, 'X', 2, 1);
            $tag->invalidate();
            return $tag;
        }), 'tagName' => 'X', 'nestingLimit' => 2]]]], ['foo bar', '<r><X>f</X><X>o</X>o bar</r>', function ($configurator) {
            $configurator->tags->add('X')->tagLimit = 2;
        }, function ($parser) {
            $parser->addSelfClosingTag('X', 0, 1);
            $parser->addSelfClosingTag('X', 1, 1);
            $parser->addSelfClosingTag('X', 2, 1);
        }, [['err', 'Tag limit exceeded', ['tag' => $this->runClosure(function () {
            $tag = new Tag(Tag::SELF_CLOSING_TAG, 'X', 2, 1);
            $tag->invalidate();
            return $tag;
        }), 'tagName' => 'X', 'tagLimit' => 2]]]], ['foo bar', '<r><X>foo</X> bar</r>', function ($configurator) {
            $configurator->tags->add('X');
            $configurator->tags->add('Y');
        }, function ($parser) {
            $parser->addSelfClosingTag('X', 0, 3);
            $parser->addSelfClosingTag('Y', 1, 1)->cascadeInvalidationTo($parser->addSelfClosingTag('Y', 5, 1));
        }], ["[pre]foo[b]x\ny[/b]bar[/pre]a\nb", "<r><PRE><s>[pre]</s>foo<B><s>[b]</s>x<br/>\ny<e>[/b]</e></B>bar<e>[/pre]</e></PRE>a<br/>\nb</r>", function ($configurator) {
            $configurator->rootRules->enableAutoLineBreaks();
            $configurator->tags->add('PRE')->rules->suspendAutoLineBreaks();
            $configurator->tags->add('B');
        }, function ($parser) {
            $parser->addStartTag('PRE', 0, 5);
            $parser->addEndTag('PRE', 21, 6);
            $parser->addStartTag('B', 8, 3);
            $parser->addEndTag('B', 14, 4);
        }], ["[pre]foo[b]x\ny[/b]bar[/pre]a\nb", "<r><PRE><s>[pre]</s>foo<B><s>[b]</s>x\ny<e>[/b]</e></B>bar<e>[/pre]</e></PRE>a<br/>\nb</r>", function ($configurator) {
            $configurator->rootRules->enableAutoLineBreaks();
            $configurator->tags->add('PRE')->rules->disableAutoLineBreaks();
            $configurator->tags->add('B');
        }, function ($parser) {
            $parser->addStartTag('PRE', 0, 5);
            $parser->addEndTag('PRE', 21, 6);
            $parser->addStartTag('B', 8, 3);
            $parser->addEndTag('B', 14, 4);
        }], ['foo bar', '<r><X>foo</X> bar</r>', function ($configurator) {
            $configurator->tags->add('X');
        }, function ($parser) {
            $parser->addSelfClosingTag('X', 0, 3);
            $parser->addIgnoreTag(2, 1);
        }], ['foo bar', '<r><X>foo</X><i> b</i>ar</r>', function ($configurator) {
            $configurator->tags->add('X');
        }, function ($parser) {
            $parser->addSelfClosingTag('X', 0, 3);
            $parser->addIgnoreTag(2, 3);
        }], ['foo bar', '<r>foo <X>bar</X></r>', function ($configurator) {
            $configurator->tags->add('X');
        }, function ($parser) {
            $parser->addSelfClosingTag('X', 4, 3);
        }], ['foo bar', '<t>foo bar</t>', function ($configurator) {
            $configurator->tags->add('X');
        }, function ($parser) {
            $parser->addSelfClosingTag('X', 4, 4);
        }], ['foo bar', '<r><X>foo bar</X></r>', function ($configurator) {
            $configurator->tags->add('X');
        }, function ($parser) {
            $parser->addSelfClosingTag('X', 0, 7);
        }], ['foo bar', '<t>foo bar</t>', function ($configurator) {
            $configurator->tags->add('X');
        }, function ($parser) {
            $parser->addSelfClosingTag('X', 0, 8);
        }], ['*foo* bar', '<r><X><s>*</s>foo<e>*</e></X> bar</r>', function ($configurator) {
            $configurator->tags->add('X');
        }, function ($parser) {
            $parser->addStartTag('X', 0, 1)->pairWith($parser->addEndTag('X', 4, 1));
        }], ['*foo* bar', '<r><X><s>*</s>foo* bar</X></r>', function ($configurator) {
            $configurator->tags->add('X');
        }, function ($parser) {
            $parser->addStartTag('X', 0, 1)->pairWith($parser->addEndTag('X', 99, 1));
        }], ['*foo* bar', '<r><X><s>*</s>foo* bar</X></r>', function ($configurator) {
            $configurator->tags->add('X');
        }, function ($parser) {
            $parser->addStartTag('X', 0, 1)->pairWith($parser->addEndTag('X', 99, 1));
            $parser->addEndTag('X', 4, 1);
        }], ['*_foo* bar_', '<r><X><s>*</s><Y><s>_</s>foo</Y><e>*</e></X><Y> bar<e>_</e></Y></r>', function ($configurator) {
            $configurator->tags->add('X');
            $configurator->tags->add('Y')->rules->autoReopen();
        }, function ($parser) {
            $parser->addStartTag('X', 0, 1)->pairWith($parser->addEndTag('X', 5, 1));
            $parser->addStartTag('Y', 1, 1)->pairWith($parser->addEndTag('Y', 10, 1));
            $parser->addEndTag('Y', 6, 1);
        }], ['**x**x***', '<r><X><s>**</s>x<Y><s>**</s>x<e>**</e></Y><e>*</e></X></r>', function ($configurator) {
            $configurator->tags->add('X');
            $configurator->tags->add('Y');
        }, function ($parser) {
            $parser->addStartTag('X', 0, 2)->pairWith($parser->addEndTag('X', 7, 2));
            $parser->addStartTag('Y', 3, 2)->pairWith($parser->addEndTag('Y', 6, 2));
        }], ['**x[**]x', '<r><X><s>**</s>x<Y>[**]</Y></X>x</r>', function ($configurator) {
            $configurator->tags->add('X');
            $configurator->tags->add('Y');
        }, function ($parser) {
            $parser->addStartTag('X', 0, 2)->pairWith($parser->addEndTag('X', 4, 2));
            $parser->addSelfClosingTag('Y', 3, 4);
        }], ['xy', '<t>x<br/>y</t>', null, function ($parser) {
            $parser->addBrTag(1);
        }], ['xy', '<t>xy</t>', function ($configurator) {
            $configurator->rootRules->preventLineBreaks();
        }, function ($parser) {
            $parser->addBrTag(1);
        }], ['xx', '<r><X>x</X><X>x</X></r>', function ($configurator) {
            $configurator->tags->add('X')->rules->closeParent('X');
        }, function ($parser) {
            $parser->addStartTag('X', 0, 0);
            $parser->addStartTag('X', 1, 0);
        }], ['xx [hr] yy', '<r>xx <HR>[hr]</HR> yy</r>', function ($configurator) {
            $configurator->tags->add('HR')->rules->autoClose();
        }, function ($parser) {
            $parser->addStartTag('HR', 3, 4);
        }], ['xx [hr][/hr] yy', '<r>xx <HR><s>[hr]</s><e>[/hr]</e></HR> yy</r>', function ($configurator) {
            $configurator->tags->add('HR')->rules->autoClose();
        }, function ($parser) {
            $parser->addStartTag('HR', 3, 4);
            $parser->addEndTag('HR', 7, 5);
        }], ['xx [img=foo.png] yy', '<r>xx <IMG src="foo.png">[img=foo.png]</IMG> yy</r>', function ($configurator) {
            $tag = $configurator->tags->add('IMG');
            $tag->attributes->add('src');
            $tag->rules->autoClose();
        }, function ($parser) {
            $parser->addStartTag('IMG', 3, 13)->setAttribute('src', 'foo.png');
        }], ['xx [img]foo.png[/img] yy', '<r>xx <IMG src="foo.png"><s>[img]</s>foo.png<e>[/img]</e></IMG> yy</r>', function ($configurator) {
            $tag = $configurator->tags->add('IMG');
            $tag->attributes->add('src');
            $tag->rules->autoClose();
        }, function ($parser) {
            $tag = $parser->addStartTag('IMG', 3, 5);
            $tag->setAttribute('src', 'foo.png');
            $tag->pairWith($parser->addEndTag('IMG', 15, 6));
        }], ['XYX', '<r><X><s>X</s><Y>Y</Y><e>X</e></X></r>', function ($configurator) {
            $configurator->tags->add('X');
            $configurator->tags->add('Y');
        }, function ($parser) {
            $parser->addStartTag('X', 0, 1);
            $parser->addSelfClosingTag('Y', 1, 1);
            $parser->addEndTag('X', 2, 1);
        }], ['XYX', '<r><X><s>X</s>Y<e>X</e></X></r>', function ($configurator) {
            $configurator->tags->add('X')->rules->denyChild('Y');
            $configurator->tags->add('Y');
        }, function ($parser) {
            $parser->addStartTag('X', 0, 1);
            $parser->addSelfClosingTag('Y', 1, 1);
            $parser->addEndTag('X', 2, 1);
        }], ['XYZYX', '<r><X><s>X</s><Y><s>Y</s><Z>Z</Z><e>Y</e></Y><e>X</e></X></r>', function ($configurator) {
            $configurator->tags->add('X')->rules->denyChild('Z');
            $configurator->tags->add('Y');
            $configurator->tags->add('Z');
        }, function ($parser) {
            $parser->addStartTag('X', 0, 1);
            $parser->addStartTag('Y', 1, 1);
            $parser->addSelfClosingTag('Z', 2, 1);
            $parser->addEndTag('Y', 3, 1);
            $parser->addEndTag('X', 4, 1);
        }], ['XYZYX', '<r><X><s>X</s><Y><s>Y</s>Z<e>Y</e></Y><e>X</e></X></r>', function ($configurator) {
            $configurator->tags->add('X')->rules->denyChild('Z');
            $configurator->tags->add('Y')->rules->isTransparent();
            $configurator->tags->add('Z');
        }, function ($parser) {
            $parser->addStartTag('X', 0, 1);
            $parser->addStartTag('Y', 1, 1);
            $parser->addSelfClosingTag('Z', 2, 1);
            $parser->addEndTag('Y', 3, 1);
            $parser->addEndTag('X', 4, 1);
        }], ['XYX', '<r><X><s>X</s>Y<e>X</e></X></r>', function ($configurator) {
            $configurator->tags->add('X')->rules->denyDescendant('Y');
            $configurator->tags->add('Y');
        }, function ($parser) {
            $parser->addStartTag('X', 0, 1);
            $parser->addSelfClosingTag('Y', 1, 1);
            $parser->addEndTag('X', 2, 1);
        }], ['XYZYX', '<r><X><s>X</s><Y><s>Y</s>Z<e>Y</e></Y><e>X</e></X></r>', function ($configurator) {
            $configurator->tags->add('X')->rules->denyDescendant('Z');
            $configurator->tags->add('Y');
            $configurator->tags->add('Z');
        }, function ($parser) {
            $parser->addStartTag('X', 0, 1);
            $parser->addStartTag('Y', 1, 1);
            $parser->addSelfClosingTag('Z', 2, 1);
            $parser->addEndTag('Y', 3, 1);
            $parser->addEndTag('X', 4, 1);
        }], ['XYX', '<r><X><s>X</s>Y<e>X</e></X></r>', function ($configurator) {
            $rules = $configurator->tags->add('X')->rules;
            $rules->isTransparent();
            $rules->denyChild('Y');
            $configurator->tags->add('Y');
        }, function ($parser) {
            $parser->addStartTag('X', 0, 1);
            $parser->addSelfClosingTag('Y', 1, 1);
            $parser->addEndTag('X', 2, 1);
        }], ['XYYYX', '<r><X><s>X</s><Y><s>Y</s><Y><s>Y</s><Y><s>Y</s></Y></Y></Y><e>X</e></X></r>', function ($configurator) {
            $configurator->tags->add('X');
            $configurator->tags->add('Y');
        }, function ($parser) {
            $parser->maxFixingCost = 0;
            $parser->addStartTag('X', 0, 1);
            $parser->addStartTag('Y', 1, 1);
            $parser->addStartTag('Y', 2, 1);
            $parser->addStartTag('Y', 3, 1);
            $parser->addEndTag('X', 4, 1);
        }, [['warn', 'Fixing cost limit exceeded']]], ['XYYYYX', '<r><X><s>X</s><Y><s>Y</s><Y>Y</Y><Y>Y</Y><e>Y</e></Y><e>X</e></X></r>', function ($configurator) {
            $configurator->tags->add('X');
            $configurator->tags->add('Y');
        }, function ($parser) {
            // Ensuring that we can still parse well-formed text if we disallow any fixing
            $parser->maxFixingCost = 0;
            $parser->addStartTag('X', 0, 1);
            $parser->addStartTag('Y', 1, 1);
            $parser->addSelfClosingTag('Y', 2, 1);
            $parser->addSelfClosingTag('Y', 3, 1);
            $parser->addEndTag('Y', 4, 1);
            $parser->addEndTag('X', 5, 1);
        }], ['..', '<r><X>.</X><X>.</X></r>', function ($configurator) {
            $configurator->tags->add('X');
        }, function ($parser) {
            // NOTE: the tags are added in order of position, but the stack still needs to
            //       be sorted following the right tiebreakers. This is what we're testing
            $parser->addEndTag('X', 2, 0);
            $parser->addEndTag('X', 1, 0);
            $parser->addStartTag('X', 1, 0);
            $parser->addStartTag('X', 0, 0);
        }], ['...', '<r><X>.</X><X>.</X><X>.</X></r>', function ($configurator) {
            $configurator->tags->add('X')->filterChain->append(function ($tag, $parser) {
                if ($tag->getPos() === 0) {
                    $parser->addSelfClosingTag('X', 2, 1);
                }
                return true;
            })->addParameterByName('parser');
        }, function ($parser) {
            $parser->addSelfClosingTag('X', 0, 1);
            $parser->addSelfClosingTag('X', 1, 1);
        }], ['...', '<r><X>.</X><X>.</X><X>.</X></r>', function ($configurator) {
            $configurator->tags->add('X')->filterChain->append(function ($tag, $parser) {
                if ($tag->getPos() === 0) {
                    $parser->addSelfClosingTag('X', 1, 1);
                }
                return true;
            })->addParameterByName('parser');
        }, function ($parser) {
            $parser->addSelfClosingTag('X', 0, 1);
            $parser->addSelfClosingTag('X', 2, 1);
        }], ['[UL]
					[*] foo
					[*] bar
				[/UL]', '<r><UL><s>[UL]</s>
					<LI><s>[*]</s> foo</LI>
					<LI><s>[*]</s> bar</LI>
				<e>[/UL]</e></UL></r>', function ($configurator) {
            $configurator->tags->add('UL');
            $rules = $configurator->tags->add('LI')->rules;
            $rules->closeAncestor('LI');
            $rules->ignoreSurroundingWhitespace();
        }, function ($parser) {
            $parser->addStartTag('UL', 0, 4);
            $parser->addStartTag('LI', 10, 3);
            $parser->addStartTag('LI', 23, 3);
            $parser->addEndTag('UL', 35, 5);
        }], ['[UL]
					[*] foo
					[*] bar
				[/UL]', '<r><UL><s>[UL]</s>
					<LI><s>[*]</s> foo</LI>
					<LI><s>[*]</s> bar</LI>
				<e>[/UL]</e></UL></r>', function ($configurator) {
            $configurator->tags->add('UL');
            $rules = $configurator->tags->add('LI')->rules;
            $rules->closeParent('LI');
            $rules->ignoreSurroundingWhitespace();
        }, function ($parser) {
            $parser->addStartTag('UL', 0, 4);
            $parser->addStartTag('LI', 10, 3);
            $parser->addStartTag('LI', 23, 3);
            $parser->addEndTag('UL', 35, 5);
        }], ['XX', '<r>X<X>X</X></r>', function ($configurator) {
            $configurator->tags->add('X')->filterChain->append(function ($tag) {
                return (bool) $tag->getPos();
            });
        }, function ($parser) {
            $parser->addSelfClosingTag('X', 0, 1);
            $parser->addSelfClosingTag('X', 1, 1);
        }], ['XYX', '<r><X><s>X</s>Y<e>X</e></X></r>', function ($configurator) {
            $configurator->tags->add('X')->rules->denyChild('Y');
            $configurator->tags->add('Y');
        }, function ($parser) {
            $parser->addStartTag('X', 0, 1);
            $parser->addSelfClosingTag('Y', 1, 1);
            $parser->addEndTag('X', 2, 1);
        }, [['warn', 'Tag is not allowed in this context', ['tagName' => 'Y', 'tag' => $this->runClosure(function () {
            $tag = new Tag(Tag::SELF_CLOSING_TAG, 'Y', 1, 1);
            $tag->invalidate();
            return $tag;
        })]]]], ['XX', '<r><X><s>X</s><e>X</e></X></r>', function ($configurator) {
            $configurator->tags->add('X')->rules->denyChild('Y');
            $configurator->tags->add('Y');
        }, function ($parser) {
            $parser->addStartTag('X', 0, 1);
            $parser->addSelfClosingTag('Y', 1, 0);
            $parser->addEndTag('X', 1, 1);
        }, [['debug', 'Tag is not allowed in this context', ['tagName' => 'Y', 'tag' => $this->runClosure(function () {
            $tag = new Tag(Tag::SELF_CLOSING_TAG, 'Y', 1, 0);
            $tag->invalidate();
            return $tag;
        })]]]], ['X', '<t>X</t>', function ($configurator) {
            $configurator->tags->add('X')->rules->requireAncestor('Y');
            $configurator->tags->add('Y');
        }, function ($parser) {
            $parser->addSelfClosingTag('X', 0, 1);
        }], ['.X.', '<r><NOPARSE><s>.</s>X<e>.</e></NOPARSE></r>', function ($configurator) {
            $configurator->tags->add('NOPARSE')->rules->ignoreTags();
            $configurator->tags->add('X');
        }, function ($parser) {
            $parser->addTagPair('NOPARSE', 0, 1, 2, 1);
            $parser->addSelfClosingTag('X', 1, 1);
        }], ['.X.', '<r><NOPARSE><s>.</s>X<e>.</e></NOPARSE></r>', function ($configurator) {
            $configurator->tags->add('NOPARSE')->rules->ignoreTags();
            $configurator->tags->add('X')->rules->closeParent('NOPARSE');
        }, function ($parser) {
            $parser->addTagPair('NOPARSE', 0, 1, 2, 1);
            $parser->addSelfClosingTag('X', 1, 1);
        }], ['X.X.X', '<r><X><s>X</s><NOPARSE><s>.</s>X<e>.</e></NOPARSE><e>X</e></X></r>', function ($configurator) {
            $configurator->tags->add('NOPARSE')->rules->ignoreTags();
            $configurator->tags->add('X');
        }, function ($parser) {
            $parser->addTagPair('NOPARSE', 1, 1, 3, 1);
            $parser->addStartTag('X', 0, 1);
            $parser->addEndTag('X', 2, 1);
            $parser->addEndTag('X', 4, 1);
        }], ['.X.', '<r><NOPARSE>.<i>X</i>.</NOPARSE></r>', function ($configurator) {
            $configurator->tags->add('NOPARSE')->rules->ignoreTags();
        }, function ($parser) {
            $parser->addTagPair('NOPARSE', 0, 0, 3, 0);
            $parser->addIgnoreTag(1, 1);
        }], ['.XX.', '<r><NOPARSE><p>.X</p><p>X.</p></NOPARSE></r>', function ($configurator) {
            $tag = $configurator->tags->add('NOPARSE');
            $tag->rules->createParagraphs();
            $tag->rules->ignoreTags();
        }, function ($parser) {
            $parser->addTagPair('NOPARSE', 0, 0, 4, 0);
            $parser->addParagraphBreak(2);
        }], ['.X.', '<r><NOPARSE>.<br/>X.</NOPARSE></r>', function ($configurator) {
            $configurator->tags->add('NOPARSE')->rules->ignoreTags();
        }, function ($parser) {
            $parser->addTagPair('NOPARSE', 0, 0, 3, 0);
            $parser->addBrTag(1);
        }], ['foobar', '<t><p>foo</p><p>bar</p></t>', function ($configurator) {
            $configurator->rootRules->createParagraphs();
        }, function ($parser) {
            $parser->addParagraphBreak(3);
        }], ['foo|bar', '<r><p>foo</p><i>|</i><p>bar</p></r>', function ($configurator) {
            $configurator->rootRules->createParagraphs();
        }, function ($parser) {
            $parser->addParagraphBreak(3);
            $parser->addIgnoreTag(3, 1);
        }], ["\n\n", "<r><X><br/>\n</X><X>\n</X></r>", function ($configurator) {
            $configurator->rootRules->enableAutoLineBreaks();
            $configurator->tags->add('X');
        }, function ($parser) {
            $parser->addTagPair('X', 0, 0, 1, 0);
            $parser->addTagPair('X', 1, 0, 2, 0)->setFlags(Parser::RULE_SUSPEND_AUTO_BR);
        }], ['xxx', "<r><T8>x<T8>x</T8>x</T8></r>", function ($configurator) {
            // Create 9 tags with different rules so that they don't occupy the same bit in
            // the allowed tags bitfields
            for ($i = 0; $i <= 8; ++$i) {
                $j = ($i + 1) % 9;
                $configurator->tags->add('T' . $i)->rules->denyChild('T' . $j);
            }
        }, function ($parser) {
            $parser->addTagPair('T8', 0, 0, 3, 0);
            $parser->addSelfClosingTag('T8', 1, 1);
        }], ['[x][y]..[/y][/x]', '<r><X><s>[x]</s></X><Y><s>[y]</s><X>..</X><e>[/y]</e></Y><i>[/x]</i></r>', function ($configurator) {
            $configurator->tags->add('X')->rules->autoReopen();
            $configurator->tags->add('Y')->rules->closeParent('X')->fosterParent('X');
        }, function ($parser) {
            $parser->addStartTag('X', 0, 3);
            $parser->addStartTag('Y', 3, 3);
            $parser->addEndTag('Y', 8, 4);
            $parser->addEndTag('X', 12, 4);
        }], ["foo\nbar", "<t>foo\nbar</t>"], ["foo\nbar", "<t>foo<br/>\nbar</t>", function ($configurator) {
            $configurator->rootRules->enableAutoLineBreaks();
        }], ["foo\nbar", "<t>foo\nbar</t>", function ($configurator) {
            $configurator->rootRules->disableAutoLineBreaks();
            $configurator->rootRules->enableAutoLineBreaks();
        }], ["[Y]\n[N]\n[Y]\n[N]\n[/N]\n[/Y]\n[/N]\n[/Y]", "<r><Y><s>[Y]</s><br/>\n<N><s>[N]</s>\n<Y><s>[Y]</s><br/>\n<N><s>[N]</s>\n<e>[/N]</e></N><br/>\n<e>[/Y]</e></Y>\n<e>[/N]</e></N><br/>\n<e>[/Y]</e></Y></r>", function ($configurator) {
            $configurator->tags->add('Y')->rules->enableAutoLineBreaks();
            $configurator->tags->add('N')->rules->disableAutoLineBreaks();
        }, function ($parser) {
            $parser->addStartTag('Y', 0, 3);
            $parser->addStartTag('N', 4, 3);
            $parser->addStartTag('Y', 8, 3);
            $parser->addStartTag('N', 12, 3);
            $parser->addEndTag('N', 16, 4);
            $parser->addEndTag('Y', 21, 4);
            $parser->addEndTag('N', 26, 4);
            $parser->addEndTag('Y', 31, 4);
        }], ["[Y]\n[S]\n[X]\n[/X]\n[/S]\n[/Y]", "<r><Y><s>[Y]</s><br/>\n<S><s>[S]</s>\n<X><s>[X]</s><br/>\n<e>[/X]</e></X>\n<e>[/S]</e></S><br/>\n<e>[/Y]</e></Y></r>", function ($configurator) {
            $configurator->tags->add('Y')->rules->enableAutoLineBreaks();
            $configurator->tags->add('S')->rules->suspendAutoLineBreaks();
            $configurator->tags->add('X');
        }, function ($parser) {
            $parser->addStartTag('Y', 0, 3);
            $parser->addStartTag('S', 4, 3);
            $parser->addStartTag('X', 8, 3);
            $parser->addEndTag('X', 12, 4);
            $parser->addEndTag('S', 17, 4);
            $parser->addEndTag('Y', 22, 4);
        }], ['...', '<t>...</t>', function ($configurator) {
            $configurator->tags->add('X');
        }, function ($parser) {
            $parser->addSelfClosingTag('X', 1, 1);
            $parser->addVerbatim(0, 3);
        }], [".\n.\n", "<t>.\n.<br/>\n</t>", function ($configurator) {
            $configurator->rootRules->enableAutoLineBreaks();
        }, function ($parser) {
            $parser->addVerbatim(1, 1)->setFlags(0);
        }], [".\n.\n", "<t>.<br/>\n.\n</t>", function ($configurator) {
            $configurator->rootRules->disableAutoLineBreaks();
        }, function ($parser) {
            $parser->addVerbatim(1, 1)->setFlags(Parser::RULE_ENABLE_AUTO_BR);
        }], ['xxx', '<r><i>xxx</i></r>', null, function ($parser) {
            $parser->addIgnoreTag(0, 99);
        }]];
    }
Пример #12
0
 /**
  * sortTags() callback
  *
  * Tags are stored as a stack, in LIFO order. We sort tags by position _descending_ so that they
  * are processed in the order they appear in the text.
  *
  * @param  Tag     $a First tag to compare
  * @param  Tag     $b Second tag to compare
  * @return integer
  */
 protected static function compareTags(Tag $a, Tag $b)
 {
     $aPos = $a->getPos();
     $bPos = $b->getPos();
     // First we order by pos descending
     if ($aPos !== $bPos) {
         return $bPos - $aPos;
     }
     // If the tags start at the same position, we'll use their sortPriority if applicable. Tags
     // with a lower value get sorted last, which means they'll be processed first. IOW, -10 is
     // processed before 10
     if ($a->getSortPriority() !== $b->getSortPriority()) {
         return $b->getSortPriority() - $a->getSortPriority();
     }
     // If the tags start at the same position and have the same priority, we'll sort them
     // according to their length, with special considerations for  zero-width tags
     $aLen = $a->getLen();
     $bLen = $b->getLen();
     if (!$aLen || !$bLen) {
         // Zero-width end tags are ordered after zero-width start tags so that a pair that ends
         // with a zero-width tag has the opportunity to be closed before another pair starts
         // with a zero-width tag. For example, the pairs that would enclose each of the letters
         // in the string "XY". Self-closing tags are ordered between end tags and start tags in
         // an attempt to keep them out of tag pairs
         if (!$aLen && !$bLen) {
             $order = [Tag::END_TAG => 0, Tag::SELF_CLOSING_TAG => 1, Tag::START_TAG => 2];
             return $order[$b->getType()] - $order[$a->getType()];
         }
         // Here, we know that only one of $a or $b is a zero-width tags. Zero-width tags are
         // ordered after wider tags so that they have a chance to be processed before the next
         // character is consumed, which would force them to be skipped
         return $aLen ? -1 : 1;
     }
     // Here we know that both tags start at the same position and have a length greater than 0.
     // We sort tags by length ascending, so that the longest matches are processed first. If
     // their length is identical, the order is undefined as PHP's sort isn't stable
     return $aLen - $bLen;
 }
Пример #13
0
 /**
  * @testdox addTagPair() returns the newly-created start tag
  */
 public function testAddTagPairReturn()
 {
     $dummyStack = new DummyStack();
     $startTag = new Tag(Tag::START_TAG, 'FOO', 1, 2);
     $endTag = new Tag(Tag::END_TAG, 'FOO', 3, 4);
     $startTag->pairWith($endTag);
     $this->assertEquals($startTag, $dummyStack->addTagPair('FOO', 1, 2, 3, 4));
 }