|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333 |
- <?php
-
- namespace App\Vendor\BBCode;
-
- use App\Vendor\BBCode\Tags\BoldTag;
- use App\Vendor\BBCode\Tags\NoParseTag;
- use App\Vendor\BBCode\Tokenizer\Token;
- use App\Vendor\BBCode\Tokenizer\Tokenizer;
- use Closure;
-
- /*
- * BBCode to HTML converter
- *
- * Inspired by Kai Mallea (kmallea@gmail.com)
- *
- * Licensed under the MIT license:
- * http://www.opensource.org/licenses/mit-license.php
- */
- class Bbcode {
-
- protected $tagTypes = [];
-
- /**
- * The text with BBCodes
- *
- * @var string|null
- */
- protected $text = null;
-
- /**
- * BBCode constructor.
- *
- * @param string|null $text The text - might include BBCode tags
- */
- public function __construct($text = null)
- {
- $this->setText($text);
- }
-
- /**
- * Set the raw text - might include BBCode tags
- *
- * @param string $text The text
- * @retun void
- */
- public function setText($text)
- {
- $this->text = $text;
- }
-
- /**
- * Renders only the text without any BBCode tags.
- *
- * @param string $text Optional: Render the passed BBCode string instead of the internally stored one
- * @return string
- */
- public function renderPlain($text = null)
- {
- if ($this->text !== null and $text === null) {
- $text = $this->text;
- }
-
- return preg_replace("/\[(.*?)\]/is", '', $text);
- }
-
- /**
- * Renders only the text without any BBCode tags.
- * Alias for renderRaw().
- *
- * @deprecated Deprecated since 1.1.0
- *
- * @param string $text Optional: Render the passed BBCode string instead of the internally stored one
- * @return string
- */
- public function renderRaw($text = null)
- {
- return $this->renderPlain($text);
- }
-
- /**
- * Renders BBCode to HTML
- *
- * @param string $text Optional: Render the passed BBCode string instead of the internally stored one
- * @param bool $escape Escape HTML entities? (Only "<" and ">"!)
- * @param bool $keepLines Keep line breaks by replacing them with <br>?
- * @return string
- */
- public function render($text = null, $escape = true, $keepLines = true)
- {
- if ($this->text !== null and $text === null) {
- $text = $this->text;
- }
-
- $tokenizer = new Tokenizer($this->tagTypes);
- $tokens = $tokenizer->tokenize($text, $escape, $keepLines);
-
- $html = '';
- $level = 0;
- foreach ($tokens as $index => $token) {
- switch ($token->getType()) {
- case Token::TYPE_LINEBREAK:
- $html .= '<br/>';
- break;
- case Token::TYPE_PLAIN_TEXT:
- $html .= $token->getValue();
- break;
- case Token::TYPE_TAG_OPENING:
- case Token::TYPE_TAG_CLOSING:
- $tagOpening = $token->getType() === Token::TYPE_TAG_OPENING;
- $tagName = $token->getValue();
-
- // If the tag is not known, just do not render it. We do not want to throw any exceptions.
- if (isset($this->tagTypes[$tagName])) {
- $tagType = $this->tagTypes[$tagName];
-
- /** @var AbstractTagType $tag */
- $tag = new $tagType;
- $tag->render($html, $tagOpening);
- }
-
-
- if ($tagOpening) {
- $level++;
- } else {
- $level--;
- }
- }
- }
-
- return $html;
-
-
- $html = '';
- $len = mb_strlen($text);
- $inTag = false; // True if current position is inside a tag
- $inName = false; // True if current pos is inside a tag name
- $inStr = false; // True if current pos is inside a string
- /** @var Tag|null $tag */
- $tag = null;
- $openTags = array();
-
- /*
- * Loop over each character of the text
- */
- for ($i = 0; $i < $len; $i++) {
- $char = mb_substr($text, $i, 1);
-
- if ($keepLines) {
- if ($char == "\n") {
- $html .= '<br/>';
- }
- if ($char == "\r") {
- continue;
- }
- }
-
- if (! $escape or ($char != '<' and $char != '>')) {
- /*
- * $inTag == true means the current position is inside a tag definition
- * (= inside the brackets of a tag)
- */
- if ($inTag) {
- if ($char == '"') {
- if ($inStr) {
- $inStr = false;
- } else {
- if ($inName) {
- $tag->valid = false;
- } else {
- $inStr = true;
- }
- }
- } else {
- /*
- * This closes a tag
- */
- if ($char == ']' and ! $inStr) {
- $inTag = false;
- $inName = false;
-
- if ($tag->valid) {
- $code = null;
-
- if ($tag->opening) {
- $code = $this->generateTag($tag, $html, null, $openTags);
- } else {
- $openingTag = $this->popTag($openTags, $tag);
- if ($openingTag) {
- $code = $this->generateTag($tag, $html, $openingTag, $openTags);
- }
- }
-
- if ($code !== null and $tag->opening) {
- $openTags[$tag->name][] = $tag;
- }
-
- $html .= $code;
- }
- continue;
- }
-
- if ($inName and ! $inStr) {
- /*
- * This makes the current tag a closing tag
- */
- if ($char == '/') {
- if ($tag->name) {
- $tag->valid = false;
- } else {
- $tag->opening = false;
- }
- } else {
- /*
- * This means a property starts
- */
- if ($char == '=') {
- if ($tag->name) {
- $inName = false;
- } else {
- $tag->valid = false;
- }
- } else {
- $tag->name .= mb_strtolower($char);
- }
- }
- } else { // If we are not inside the name we are inside a property
- $tag->property .= $char;
- }
- }
- } else {
- /*
- * This opens a tag
- */
- if ($char == '[') {
- $inTag = true;
- $inName = true;
- $tag = new Tag();
- $tag->position = mb_strlen($html);
- } else {
- $html .= $char;
- }
- }
- } else {
- $html .= htmlspecialchars($char);
- }
- }
-
- /*
- * Check for tags that are not closed and close them.
- */
- foreach ($openTags as $name => $openTagsByType) {
- $closingTag = new Tag($name, false);
-
- foreach ($openTagsByType as $openTag) {
- $html .= $this->generateTag($closingTag, $html, $openTag);
- }
- }
-
- return $html;
- }
-
- /**
- * Generates and returns the HTML code of the current tag
- *
- * @param Tag $tag The current tag
- * @param string $html The current HTML code passed by reference - might be altered!
- * @param Tag|null $openingTag The opening tag that is linked to the tag (or null)
- * @param Tag[] $openTags Array with tags that are opned but not closed
- * @return string
- */
- protected function generateTag(Tag $tag, &$html, Tag $openingTag = null, array $openTags = [])
- {
- $code = null;
-
- if (in_array($tag->name, $this->ignoredTags)) {
- return $code;
- }
-
- // Custom tags:
- foreach ($this->customTagClosures as $name => $closure) {
- if ($tag->name === $name) {
- exit($tag->name);
- $code .= $closure($tag, $html, $openingTag);
- }
- }
-
- return $code;
- }
-
- /**
- * Magic method __toString()
- *
- * @return string
- */
- public function __toString()
- {
- return $this->render();
- }
-
- /**
- * Returns the last tag of a given type and removes it from the array.
- *
- * @param Tag[] $tags Array of tags
- * @param Tag $tag Return the last tag of the type of this tag
- * @return Tag|null
- */
- protected function popTag(array &$tags, $tag)
- {
- if (! isset($tags[$tag->name])) {
- return null;
- }
-
- $size = sizeof($tags[$tag->name]);
-
- if ($size === 0) {
- return null;
- } else {
- return array_pop($tags[$tag->name]);
- }
- }
-
- /**
- * Returns true if $haystack ends with $needle
- *
- * @param string $haystack
- * @param string $needle
- * @return bool
- */
- protected function endsWith($haystack, $needle)
- {
- return ($needle === '' or mb_substr($haystack, -mb_strlen($needle)) === $needle);
- }
- }
|