From 0446bc4f41f9fb79f28d8704c8e7f0e19c1b1fb9 Mon Sep 17 00:00:00 2001 From: flashwave Date: Sat, 6 Aug 2016 19:31:32 +0200 Subject: [PATCH] improved the bbcode framework --- app/BBCode/Parser.php | 93 ++++++++++++++ app/BBCode/TagBase.php | 37 ++++++ app/BBCode/Tags/Align.php | 37 ++++++ app/BBCode/Tags/Bold.php | 29 +++++ app/BBCode/Tags/Box.php | 38 ++++++ app/BBCode/Tags/Code.php | 36 ++++++ app/BBCode/Tags/Colour.php | 29 +++++ app/BBCode/Tags/Header.php | 29 +++++ app/BBCode/Tags/Image.php | 29 +++++ app/BBCode/Tags/Italics.php | 29 +++++ app/BBCode/Tags/Link.php | 29 +++++ app/BBCode/Tags/ListTag.php | 39 ++++++ app/BBCode/Tags/NamedLink.php | 29 +++++ app/BBCode/Tags/NamedQuote.php | 29 +++++ app/BBCode/Tags/Newline.php | 29 +++++ app/BBCode/Tags/Quote.php | 29 +++++ app/BBCode/Tags/Size.php | 70 +++++++++++ app/BBCode/Tags/Spoiler.php | 29 +++++ app/BBCode/Tags/Strike.php | 29 +++++ app/BBCode/Tags/Underline.php | 29 +++++ app/BBCode/Tags/User.php | 38 ++++++ app/BBCode/Tags/YouTube.php | 30 +++++ app/BBcode.php | 175 --------------------------- app/Comment.php | 2 +- app/Controllers/HelperController.php | 4 +- app/Forum/Post.php | 2 +- app/User.php | 4 +- 27 files changed, 801 insertions(+), 181 deletions(-) create mode 100644 app/BBCode/Parser.php create mode 100644 app/BBCode/TagBase.php create mode 100644 app/BBCode/Tags/Align.php create mode 100644 app/BBCode/Tags/Bold.php create mode 100644 app/BBCode/Tags/Box.php create mode 100644 app/BBCode/Tags/Code.php create mode 100644 app/BBCode/Tags/Colour.php create mode 100644 app/BBCode/Tags/Header.php create mode 100644 app/BBCode/Tags/Image.php create mode 100644 app/BBCode/Tags/Italics.php create mode 100644 app/BBCode/Tags/Link.php create mode 100644 app/BBCode/Tags/ListTag.php create mode 100644 app/BBCode/Tags/NamedLink.php create mode 100644 app/BBCode/Tags/NamedQuote.php create mode 100644 app/BBCode/Tags/Newline.php create mode 100644 app/BBCode/Tags/Quote.php create mode 100644 app/BBCode/Tags/Size.php create mode 100644 app/BBCode/Tags/Spoiler.php create mode 100644 app/BBCode/Tags/Strike.php create mode 100644 app/BBCode/Tags/Underline.php create mode 100644 app/BBCode/Tags/User.php create mode 100644 app/BBCode/Tags/YouTube.php delete mode 100644 app/BBcode.php diff --git a/app/BBCode/Parser.php b/app/BBCode/Parser.php new file mode 100644 index 0000000..1dc7f00 --- /dev/null +++ b/app/BBCode/Parser.php @@ -0,0 +1,93 @@ + + */ +class Parser +{ + /** + * Holds the bbcode parsers. + * @var array + */ + public static $parsers = [ + // Basic markup + Tags\Bold::class, + Tags\Italics::class, + Tags\Underline::class, + Tags\Strike::class, + Tags\Header::class, + Tags\Image::class, + Tags\Spoiler::class, + + // More advanced + Tags\Colour::class, + Tags\Align::class, + Tags\Size::class, + Tags\YouTube::class, + + // Links + Tags\NamedLink::class, + Tags\Link::class, + + // Quotes + Tags\NamedQuote::class, + Tags\Quote::class, + + // Advanced parsing + Tags\Box::class, + Tags\Code::class, + Tags\ListTag::class, + Tags\User::class, + + // Newline must always be last + Tags\Newline::class, + ]; + + /** + * Parse the emoticons. + * @param string $text + * @return string + */ + public static function parseEmoticons($text) + { + // Get emoticons from the database + $emotes = DB::table('emoticons') + ->get(); + + // Parse all emoticons + foreach ($emotes as $emote) { + $image = "{$emote->emote_string}"; + $icon = preg_quote($emote->emote_string, '#'); + $text = preg_replace("#{$icon}#", $image, $text); + } + + // Return the parsed text + return $text; + } + + /** + * Convert the parsed text to HTML. + * @param string $text + * @return string + */ + public static function toHTML($text) + { + $text = self::parseEmoticons($text); + + foreach (self::$parsers as $parser) { + $text = call_user_func([$parser, 'parse'], $text); + } + + return $text; + } +} diff --git a/app/BBCode/TagBase.php b/app/BBCode/TagBase.php new file mode 100644 index 0000000..a9a6a6f --- /dev/null +++ b/app/BBCode/TagBase.php @@ -0,0 +1,37 @@ + + */ +class TagBase +{ + /** + * The pattern to match. + * @var string + */ + public static $pattern = ""; + + /** + * The string to replace it with. + * @var string + */ + public static $replace = ""; + + /** + * Parses the bbcode. + * @param string $text + * @return string + */ + public static function parse($text) + { + return preg_replace(static::$pattern, static::$replace, $text); + } +} diff --git a/app/BBCode/Tags/Align.php b/app/BBCode/Tags/Align.php new file mode 100644 index 0000000..34f8bbb --- /dev/null +++ b/app/BBCode/Tags/Align.php @@ -0,0 +1,37 @@ + + */ +class Align extends TagBase +{ + /** + * Parses the bbcode. + * @param string $text + * @return string + */ + public static function parse($text) + { + return preg_replace_callback( + '/\[align\=(left|center|centre|right)\](.*?)\[\/align\]/s', + function ($matches) { + if ($matches[1] === 'centre') { + $matches[1] = 'center'; + } + + return "
{$matches[2]}
"; + }, + $text + ); + } +} diff --git a/app/BBCode/Tags/Bold.php b/app/BBCode/Tags/Bold.php new file mode 100644 index 0000000..d6b2400 --- /dev/null +++ b/app/BBCode/Tags/Bold.php @@ -0,0 +1,29 @@ + + */ +class Bold extends TagBase +{ + /** + * The pattern to match. + * @var string + */ + public static $pattern = "/\[b\](.*?)\[\/b\]/s"; + + /** + * The string to replace it with. + * @var string + */ + public static $replace = "$1"; +} diff --git a/app/BBCode/Tags/Box.php b/app/BBCode/Tags/Box.php new file mode 100644 index 0000000..6d62993 --- /dev/null +++ b/app/BBCode/Tags/Box.php @@ -0,0 +1,38 @@ + + */ +class Box extends TagBase +{ + /** + * Parses the bbcode. + * @param string $text + * @return string + */ + public static function parse($text) + { + return preg_replace_callback( + '/\[box(?:\=(.*?))?\](.*?)\[\/box\]/s', + function ($matches) { + $title = strlen($matches[1]) ? $matches[1] : 'Click to open'; + + return "
" + . "
{$title}
" + . "" + . "
"; + }, + $text + ); + } +} diff --git a/app/BBCode/Tags/Code.php b/app/BBCode/Tags/Code.php new file mode 100644 index 0000000..2201742 --- /dev/null +++ b/app/BBCode/Tags/Code.php @@ -0,0 +1,36 @@ + + */ +class Code extends TagBase +{ + /** + * Parses the bbcode. + * @param string $text + * @return string + */ + public static function parse($text) + { + return preg_replace_callback( + '/\[code(?:\=([a-z]+))?\](.*?)\[\/code\]/s', + function ($matches) { + $class = strlen($matches[1]) ? " class='lang-{$matches[1]}'" : ''; + // htmlencode bbcode characters here as well + + return "
{$matches[2]}
"; + }, + $text + ); + } +} diff --git a/app/BBCode/Tags/Colour.php b/app/BBCode/Tags/Colour.php new file mode 100644 index 0000000..8ed8457 --- /dev/null +++ b/app/BBCode/Tags/Colour.php @@ -0,0 +1,29 @@ + + */ +class Colour extends TagBase +{ + /** + * The pattern to match. + * @var string + */ + public static $pattern = "/\[colou?r\=(#[A-f0-9]{6}|#[A-f0-9]{3})\](.*?)\[\/colou?r\]/s"; + + /** + * The string to replace it with. + * @var string + */ + public static $replace = "$2"; +} diff --git a/app/BBCode/Tags/Header.php b/app/BBCode/Tags/Header.php new file mode 100644 index 0000000..fac0b9b --- /dev/null +++ b/app/BBCode/Tags/Header.php @@ -0,0 +1,29 @@ + + */ +class Header extends TagBase +{ + /** + * The pattern to match. + * @var string + */ + public static $pattern = "/\[header\](.*?)\[\/header\]/s"; + + /** + * The string to replace it with. + * @var string + */ + public static $replace = "

$1

"; +} diff --git a/app/BBCode/Tags/Image.php b/app/BBCode/Tags/Image.php new file mode 100644 index 0000000..2547385 --- /dev/null +++ b/app/BBCode/Tags/Image.php @@ -0,0 +1,29 @@ + + */ +class Image extends TagBase +{ + /** + * The pattern to match. + * @var string + */ + public static $pattern = "/\[img\](.*?)\[\/img\]/s"; + + /** + * The string to replace it with. + * @var string + */ + public static $replace = "$1"; +} diff --git a/app/BBCode/Tags/Italics.php b/app/BBCode/Tags/Italics.php new file mode 100644 index 0000000..cd489fb --- /dev/null +++ b/app/BBCode/Tags/Italics.php @@ -0,0 +1,29 @@ + + */ +class Italics extends TagBase +{ + /** + * The pattern to match. + * @var string + */ + public static $pattern = "/\[i\](.*?)\[\/i\]/s"; + + /** + * The string to replace it with. + * @var string + */ + public static $replace = "$1"; +} diff --git a/app/BBCode/Tags/Link.php b/app/BBCode/Tags/Link.php new file mode 100644 index 0000000..e9099fb --- /dev/null +++ b/app/BBCode/Tags/Link.php @@ -0,0 +1,29 @@ + + */ +class Link extends TagBase +{ + /** + * The pattern to match. + * @var string + */ + public static $pattern = "/\[url\](.*?)\[\/url\]/s"; + + /** + * The string to replace it with. + * @var string + */ + public static $replace = "$1"; +} diff --git a/app/BBCode/Tags/ListTag.php b/app/BBCode/Tags/ListTag.php new file mode 100644 index 0000000..0838d5f --- /dev/null +++ b/app/BBCode/Tags/ListTag.php @@ -0,0 +1,39 @@ + + */ +class ListTag extends TagBase +{ + /** + * Parses the bbcode. + * @param string $text + * @return string + */ + public static function parse($text) + { + return preg_replace_callback( + '/\[list(?:\=(1|A|a|I|i))?\](.*?)\[\/list\]/s', + function ($matches) { + $content = preg_replace('/\[\*\](.*)/', '
  • $1
  • ', $matches[2]); + + if ($matches[1] !== '') { + return "
      {$content}
    "; + } + + return ""; + }, + $text + ); + } +} diff --git a/app/BBCode/Tags/NamedLink.php b/app/BBCode/Tags/NamedLink.php new file mode 100644 index 0000000..4947a8d --- /dev/null +++ b/app/BBCode/Tags/NamedLink.php @@ -0,0 +1,29 @@ + + */ +class NamedLink extends TagBase +{ + /** + * The pattern to match. + * @var string + */ + public static $pattern = "/\[url\=(.*?)\](.*?)\[\/url\]/s"; + + /** + * The string to replace it with. + * @var string + */ + public static $replace = "$2"; +} diff --git a/app/BBCode/Tags/NamedQuote.php b/app/BBCode/Tags/NamedQuote.php new file mode 100644 index 0000000..7082818 --- /dev/null +++ b/app/BBCode/Tags/NamedQuote.php @@ -0,0 +1,29 @@ + + */ +class NamedQuote extends TagBase +{ + /** + * The pattern to match. + * @var string + */ + public static $pattern = "/\[quote\=(.*?)\](.*)\[\/quote\]/s"; + + /** + * The string to replace it with. + * @var string + */ + public static $replace = "
    $1$2
    "; +} diff --git a/app/BBCode/Tags/Newline.php b/app/BBCode/Tags/Newline.php new file mode 100644 index 0000000..3cb00ab --- /dev/null +++ b/app/BBCode/Tags/Newline.php @@ -0,0 +1,29 @@ + + */ +class Newline extends TagBase +{ + /** + * The pattern to match. + * @var string + */ + public static $pattern = "/\r\n|\r|\n/"; + + /** + * The string to replace it with. + * @var string + */ + public static $replace = "
    "; +} diff --git a/app/BBCode/Tags/Quote.php b/app/BBCode/Tags/Quote.php new file mode 100644 index 0000000..14afd00 --- /dev/null +++ b/app/BBCode/Tags/Quote.php @@ -0,0 +1,29 @@ + + */ +class Quote extends TagBase +{ + /** + * The pattern to match. + * @var string + */ + public static $pattern = "/\[quote\](.*?)\[\/quote\]/s"; + + /** + * The string to replace it with. + * @var string + */ + public static $replace = "
    $1
    "; +} diff --git a/app/BBCode/Tags/Size.php b/app/BBCode/Tags/Size.php new file mode 100644 index 0000000..4c574b9 --- /dev/null +++ b/app/BBCode/Tags/Size.php @@ -0,0 +1,70 @@ + + */ +class Size extends TagBase +{ + /** + * The maximum size a user can specify. + * @var int + */ + private static $maxSize = 200; + + /** + * The minimum size a user can specify. + * @var int + */ + private static $minSize = 1; + + /** + * Text aliases for the various sizes. + * @var array + */ + private static $aliases = [ + 'tiny' => 50, + 'small' => 85, + 'normal' => 100, + 'large' => 150, + 'huge' => 200, + ]; + + /** + * Parses the bbcode. + * @param string $text + * @return string + */ + public static function parse($text) + { + return preg_replace_callback( + '/\[size\=([a-z0-9]+)\](.*?)\[\/size\]/s', + function ($matches) { + if (is_numeric($matches[1])) { + $size = intval($matches[1]); + + if ($size < self::$minSize || $size > self::$maxSize) { + return $matches[0]; + } + } elseif (in_array($matches[1], self::$aliases)) { + $size = self::$aliases[$matches[1]]; + } else { + return $matches[0]; + } + + // we'll just use per cent for now, don't let this make it to production though + return "
    {$matches[2]}
    "; + }, + $text + ); + } +} diff --git a/app/BBCode/Tags/Spoiler.php b/app/BBCode/Tags/Spoiler.php new file mode 100644 index 0000000..dc50b96 --- /dev/null +++ b/app/BBCode/Tags/Spoiler.php @@ -0,0 +1,29 @@ + + */ +class Spoiler extends TagBase +{ + /** + * The pattern to match. + * @var string + */ + public static $pattern = "/\[spoiler\](.*?)\[\/spoiler\]/s"; + + /** + * The string to replace it with. + * @var string + */ + public static $replace = "$1"; +} diff --git a/app/BBCode/Tags/Strike.php b/app/BBCode/Tags/Strike.php new file mode 100644 index 0000000..d4b8371 --- /dev/null +++ b/app/BBCode/Tags/Strike.php @@ -0,0 +1,29 @@ + + */ +class Strike extends TagBase +{ + /** + * The pattern to match. + * @var string + */ + public static $pattern = "/\[s\](.*?)\[\/s\]/s"; + + /** + * The string to replace it with. + * @var string + */ + public static $replace = "$1"; +} diff --git a/app/BBCode/Tags/Underline.php b/app/BBCode/Tags/Underline.php new file mode 100644 index 0000000..f45d7d7 --- /dev/null +++ b/app/BBCode/Tags/Underline.php @@ -0,0 +1,29 @@ + + */ +class Underline extends TagBase +{ + /** + * The pattern to match. + * @var string + */ + public static $pattern = "/\[u\](.*?)\[\/u\]/s"; + + /** + * The string to replace it with. + * @var string + */ + public static $replace = "$1"; +} diff --git a/app/BBCode/Tags/User.php b/app/BBCode/Tags/User.php new file mode 100644 index 0000000..4306931 --- /dev/null +++ b/app/BBCode/Tags/User.php @@ -0,0 +1,38 @@ + + */ +class User extends TagBase +{ + /** + * Parses the bbcode. + * @param string $text + * @return string + */ + public static function parse($text) + { + return preg_replace_callback( + '/\[user\]([0-9]+)\[\/user\]/s', + function ($matches) { + $user = UserObject::construct($matches[1]); + $route = route('user.profile', $user->id); + + return "{$user->username}"; + }, + $text + ); + } +} diff --git a/app/BBCode/Tags/YouTube.php b/app/BBCode/Tags/YouTube.php new file mode 100644 index 0000000..5fbf0e1 --- /dev/null +++ b/app/BBCode/Tags/YouTube.php @@ -0,0 +1,30 @@ + + */ +class YouTube extends TagBase +{ + /** + * The pattern to match. + * @var string + */ + public static $pattern = "/\[youtube\]([A-Za-z0-9\-\_]+)\[\/youtube\]/s"; + + /** + * The string to replace it with. + * @var string + */ + public static $replace = ""; +} diff --git a/app/BBcode.php b/app/BBcode.php deleted file mode 100644 index 86365dd..0000000 --- a/app/BBcode.php +++ /dev/null @@ -1,175 +0,0 @@ - - */ -class BBcode -{ - /** - * Holds the bbcode parsers. - * @var array - */ - public static $parsers = [ - 'bold' => [ - 'pattern' => '/\[b\](.*?)\[\/b\]/s', - 'replace' => '$1', - 'content' => '$1', - ], - 'italic' => [ - 'pattern' => '/\[i\](.*?)\[\/i\]/s', - 'replace' => '$1', - 'content' => '$1', - ], - 'underline' => [ - 'pattern' => '/\[u\](.*?)\[\/u\]/s', - 'replace' => '$1', - 'content' => '$1', - ], - 'linethrough' => [ - 'pattern' => '/\[s\](.*?)\[\/s\]/s', - 'replace' => '$1', - 'content' => '$1', - ], - 'header' => [ - 'pattern' => '/\[header\](.*?)\[\/header\]/s', - 'replace' => '

    $1

    ', - 'content' => '$1', - ], - 'size' => [ - 'pattern' => '/\[size\=([1-7])\](.*?)\[\/size\]/s', - 'replace' => '$2', - 'content' => '$2', - ], - 'color' => [ - 'pattern' => '/\[color\=(#[A-f0-9]{6}|#[A-f0-9]{3})\](.*?)\[\/color\]/s', - 'replace' => '$2', - 'content' => '$2', - ], - 'center' => [ - 'pattern' => '/\[center\](.*?)\[\/center\]/s', - 'replace' => '
    $1
    ', - 'content' => '$1', - ], - 'left' => [ - 'pattern' => '/\[left\](.*?)\[\/left\]/s', - 'replace' => '
    $1
    ', - 'content' => '$1', - ], - 'right' => [ - 'pattern' => '/\[right\](.*?)\[\/right\]/s', - 'replace' => '
    $1
    ', - 'content' => '$1', - ], - 'align' => [ - 'pattern' => '/\[align\=(left|center|right)\](.*?)\[\/align\]/s', - 'replace' => '
    $2
    ', - 'content' => '$2', - ], - 'quote' => [ - 'pattern' => '/\[quote\](.*?)\[\/quote\]/s', - 'replace' => '
    $1
    ', - 'content' => '$1', - ], - 'namedquote' => [ - 'pattern' => '/\[quote\=(.*?)\](.*)\[\/quote\]/s', - 'replace' => '
    $1$2
    ', - 'content' => '$2', - ], - 'link' => [ - 'pattern' => '/\[url\](.*?)\[\/url\]/s', - 'replace' => '$1', - 'content' => '$1', - ], - 'namedlink' => [ - 'pattern' => '/\[url\=(.*?)\](.*?)\[\/url\]/s', - 'replace' => '$2', - 'content' => '$2', - ], - 'image' => [ - 'pattern' => '/\[img\](.*?)\[\/img\]/s', - 'replace' => '$1', - 'content' => '$1', - ], - 'orderedlistnumerical' => [ - 'pattern' => '/\[list=1\](.*?)\[\/list\]/s', - 'replace' => '
      $1
    ', - 'content' => '$1', - ], - 'orderedlistalpha' => [ - 'pattern' => '/\[list=a\](.*?)\[\/list\]/s', - 'replace' => '
      $1
    ', - 'content' => '$1', - ], - 'unorderedlist' => [ - 'pattern' => '/\[list\](.*?)\[\/list\]/s', - 'replace' => '', - 'content' => '$1', - ], - 'listitem' => [ - 'pattern' => '/\[\*\](.*)/', - 'replace' => '
  • $1
  • ', - 'content' => '$1', - ], - 'code' => [ - 'pattern' => '/\[code\](.*?)\[\/code\]/s', - 'replace' => '
    $1
    ', - 'content' => '$1', - ], - 'youtube' => [ - 'pattern' => '/\[youtube\](.*?)\[\/youtube\]/s', - 'replace' => '', - 'content' => '$1', - ], - 'linebreak' => [ - 'pattern' => '/\r\n|\r|\n/', - 'replace' => '
    ', - 'content' => '', - ], - ]; - - /** - * Parse the emoticons. - * @param string $text - * @return string - */ - public static function parseEmoticons($text) - { - // Get emoticons from the database - $emotes = DB::table('emoticons') - ->get(); - - // Parse all emoticons - foreach ($emotes as $emote) { - $image = "{$emote->emote_string}"; - $icon = preg_quote($emote->emote_string, '#'); - $text = preg_replace("#{$icon}#", $image, $text); - } - - // Return the parsed text - return $text; - } - - /** - * Convert the parsed text to HTML. - * @param string $text - * @return string - */ - public static function toHTML($text) - { - $text = self::parseEmoticons($text); - - foreach (self::$parsers as $parser) { - $text = preg_replace($parser['pattern'], $parser['replace'], $text); - } - - return $text; - } -} diff --git a/app/Comment.php b/app/Comment.php index 2eaf835..beb106d 100644 --- a/app/Comment.php +++ b/app/Comment.php @@ -166,7 +166,7 @@ class Comment public function parsed() { if (!$this->parsedCache) { - $this->parsedCache = BBcode::parseEmoticons(clean_string($this->text)); + $this->parsedCache = BBCode\Parser::parseEmoticons(clean_string($this->text)); } return $this->parsedCache; diff --git a/app/Controllers/HelperController.php b/app/Controllers/HelperController.php index 7d431e3..fe147ec 100644 --- a/app/Controllers/HelperController.php +++ b/app/Controllers/HelperController.php @@ -6,7 +6,7 @@ namespace Sakura\Controllers; -use Sakura\BBcode; +use Sakura\BBCode\Parser as BBParser; /** * Helper controller. @@ -21,6 +21,6 @@ class HelperController extends Controller */ public function bbcodeParse() { - return BBcode::toHTML(htmlentities($_POST['text'] ?? '')); + return BBParser::toHTML(htmlentities($_POST['text'] ?? '')); } } diff --git a/app/Forum/Post.php b/app/Forum/Post.php index 6c31f86..784059d 100644 --- a/app/Forum/Post.php +++ b/app/Forum/Post.php @@ -125,7 +125,7 @@ class Post } // Parse the markup - $this->parsed = BBcode::toHTML(htmlentities($this->text)); + $this->parsed = BBCode\Parser::toHTML(htmlentities($this->text)); } /** diff --git a/app/User.php b/app/User.php index add61bc..8037549 100644 --- a/app/User.php +++ b/app/User.php @@ -902,7 +902,7 @@ class User */ public function userPage() { - return BBcode::toHTML(htmlentities($this->page)); + return BBCode\Parser::toHTML(htmlentities($this->page)); } /** @@ -911,7 +911,7 @@ class User */ public function signature() { - return BBcode::toHTML(htmlentities($this->signature)); + return BBCode\Parser::toHTML(htmlentities($this->signature)); } /**