Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Bbcodeparser Laravel Package

golonka/bbcodeparser

A PHP BBCode parser that converts BBCode markup into safe HTML. It supports tags, smileys, and configurable parsing rules, making it easy to render forum-style content in Laravel or any PHP application.

View on GitHub
Deep Wiki
Context7

Getting Started

golonka/bbcodeparser is a thin, regex-based BBCode → HTML transformer with a Laravel service provider and facade bolted on. It is a small fork of the original juiceland/BBCodeParser, last tagged in July 2019, with effectively zero dependents — so treat it as source you are adopting, not a library you can lean on for support. It earns its place only if you have legacy forum/message content that already contains BBCode and you need a quick render path.

Install and pin.

composer require golonka/bbcodeparser:^2.0

Pin the exact version ("golonka/bbcodeparser": "2.0.1" or whatever your resolve lands on) — there will be no patch releases. Then commit composer.lock and, ideally, copy the src/ directory into your own namespace so a future composer update can't silently change render output.

Where to look first. Open vendor/golonka/bbcodeparser/src/BBCodeParser.php before writing any code. Everything you care about is in that one file:

  • the $parsers map (tag → handler method) — this is the authoritative list of supported tags (b, i, u, s, url, img, email, color, size, quote, code, list, youtube, plus smileys)
  • the parse() entry point and the order in which passes run
  • the per-tag preg_replace_callback implementations, which tell you exactly what attributes are accepted and whether URLs are validated (they usually aren't)

Also check composer.json for the illuminate/support constraint — that single line determines whether the package installs at all on your Laravel version.

Register in Laravel (5.4-era; auto-discovery almost certainly isn't wired up):

// config/app.php
'providers' => [
    Golonka\BBCode\BBCodeParserServiceProvider::class,
],
'aliases' => [
    'BBCode' => Golonka\BBCode\Facades\BBCodeParser::class,
],

First use case — rendering a legacy post body:

use Golonka\BBCode\BBCodeParser;

$parser = new BBCodeParser();

echo $parser->parse('[b]Hello[/b] [url=https://example.com]site[/url]');
// <strong>Hello</strong> <a href="https://example.com">site</a>

Run that in php artisan tinker with three or four real posts from your database before you build anything on top of it. The output is your contract; you need to see it before you design around it.

Implementation Patterns

Wrap it in your own service, don't inject the package class. The facade is convenient but untestable and it leaks a 2019 class into every constructor. Bind a small adapter:

// app/Services/BbcodeRenderer.php
namespace App\Services;

use Golonka\BBCode\BBCodeParser;

class BbcodeRenderer
{
    public function __construct(private BBCodeParser $parser) {}

    public function toHtml(string $bbcode): string
    {
        $escaped = htmlspecialchars($bbcode, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
        return $this->parser->parse($escaped);
    }
}
// app/Providers/AppServiceProvider.php
$this->app->singleton(\Golonka\BBCode\BBCodeParser::class);
$this->app->singleton(\App\Services\BbcodeRenderer::class);

Now the package is one use statement in one file, and swapping to a maintained parser later is a one-class change.

Render once, store the HTML. parse() runs a preg_replace_callback per tag on every call. For a forum index showing 25 posts, that's dozens of regex passes per request. Add a body_html column (or a cache key) and render on save:

// app/Models/Post.php
protected static function booted()
{
    static::saving(function (Post $post) {
        if ($post->isDirty('body')) {
            $post->body_html = app(BbcodeRenderer::class)->toHtml($post->body);
        }
    });
}

Backfill existing rows with a queued job or an artisan command rather than a migration — you want it re-runnable when you change tag handling:

Post::query()->chunkById(500, function ($posts) {
    foreach ($posts as $post) {
        $post->body_html = app(BbcodeRenderer::class)->toHtml($post->body);
        $post->saveQuietly();
    }
});

Blade integration. Prefer a model accessor over a directive, because it keeps the escaping decision in one place:

{{-- body_html is already safe HTML produced by BbcodeRenderer --}}
{!! $post->body_html !!}

If you must render on the fly, a directive is fine but mark it clearly as trusted output:

// AppServiceProvider::boot()
Blade::directive('bbcode', fn ($expr) => "<?php echo app(\App\Services\BbcodeRenderer::class)->toHtml($expr); ?>");

Custom tags — the main extension point. The parser's $parsers map can be extended, which is how you add [spoiler], [mention], [attach], or a [quote=username] variant. Subclass rather than mutate the shared instance:

class AppBbcodeParser extends \Golonka\BBCode\BBCodeParser
{
    protected function parseSpoiler($text)
    {
        return preg_replace_callback(
            '/\[spoiler\](.*?)\[\/spoiler\]/is',
            fn ($m) => '<details><summary>Spoiler</summary>' . $m[1] . '</details>',
            $text
        );
    }
}

Register the new name in the parsers map and make sure the tag name is included in whatever allowlist the class uses. Keep the callback strictly mechanical — never interpolate user input into HTML attributes without htmlspecialchars.

Sanitize after parsing, not just before. Escaping the input protects you from raw <script>, but it does not protect you from [url=javascript:alert(1)]x[/url], because the parser emits the href itself. Either run the output through HTMLPurifier with a tight allowlist, or post-process anchors:

$html = preg_replace_callback(
    '/href="([^"]*)"/i',
    function ($m) {
        $scheme = strtolower(parse_url(html_entity_decode($m[1]), PHP_URL_SCHEME) ?? '');
        return in_array($scheme, ['http', 'https', 'mailto'], true)
            ? $m[0]
            : 'href="#"';
    },
    $html
);

Smileys/emoticons are configured through the package config (publish it with php artisan vendor:publish --provider="Golonka\BBCode\BBCodeParserServiceProvider"). Point the asset path at your CDN and disable the emoji pass entirely if your editor already handles emoji — double-rendering :) inside a [code] block is a common complaint.

Tests. Snapshot the HTML for a corpus of real legacy posts, including the ugly ones: unclosed tags, [b][i]mixed[/b][/i], [url] with no scheme, nested quotes four deep. This corpus is your regression suite for the subclassing work above and for any future migration off the package.

Gotchas and Tips

  • It is a regex chain, not a parser. Sequential preg_replace passes cannot understand nesting. [b][i]text[/b][/i] will produce unbalanced HTML. Malformed input can produce output worse than the input. If your content has deeply nested quotes or lists, budget time for cleanup or accept cosmetic breakage.
  • Unclosed tags leak through. [b]bold forever stays literal, which is usually fine, but [quote] with no closing tag will swallow the rest of the document in some implementations. Test it.
  • XSS is your responsibility, twice. The package does no escaping and no scheme validation. Escape the input and validate the emitted markup. [img] in particular will happily emit a src pointing anywhere — consider a proxy or an allowlist of hosts.
  • Attribute quoting. BBCode attributes can contain quotes and angle brackets. Check how the parser handles [url="http://x.com/?a=1&b=2"] — ampersands need to be &amp; in the final HTML, and the parser won't do it for you.
  • Laravel version conflicts. The illuminate/support constraint from 2019 will conflict with Laravel 10/11. Rather than fight --ignore-platform-reqs, extract the class: it has no meaningful framework dependency, so copying BBCodeParser.php into app/Support/ and dropping the provider is usually the right call.
  • PHP 8 deprecations. Expect passing null to parameter of type string notices from preg_* on modern PHP, and possibly dynamic-property warnings. Run your test corpus with error_reporting(E_ALL) once before shipping; then wrap the call site to suppress notices rather than patching vendor code.
  • Parse order matters. Smileys are typically applied before or after tag parsing, and [code] blocks should not have smileys or other tags expanded inside them. If that's broken, place your [code] handler early in the parsers map and have it stash its contents in placeholders before the other passes run, restoring them at the end.
  • Facade static state. If you register custom parsers on the facade-resolved singleton, they persist for the request. Fine in a request lifecycle, surprising in queues and long-running Octane workers — reset or use a scoped binding.
  • No maintenance signal. Zero dependents and a 2019 tag mean no security advisories will ever be filed. Subscribe to the repository's release feed yourself, or vendor the code and own it. If you need a long-term answer, evaluate scrivo/highlight.php-style maintained alternatives or a real BBCode parser (e.g. xemlock/htmlpurifier-html5 combined with s9e/text-formatter, which is actively maintained and designed for exactly this).
  • Debugging tip. When output looks wrong, bisect the passes: call parse() on a string with a single tag, then two tags, and compare. Because the passes are independent regexes, the failure is almost always one specific handler plus a nesting case — ten minutes of bisecting beats reading the whole class.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package