Weave Code
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

Commonmark Laravel Package

league/commonmark

Extensible PHP Markdown parser supporting the full CommonMark spec and GitHub-Flavored Markdown. Works with PHP 7.4+ (mbstring) and provides simple converters to turn Markdown into HTML with configurable safety options.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require league/commonmark
    

    Ensure mbstring PHP extension is enabled.

  2. Basic Conversion:

    use League\CommonMark\CommonMarkConverter;
    
    $converter = new CommonMarkConverter();
    $html = $converter->convert('# Hello World!');
    // Outputs: <h1>Hello World!</h1>
    
  3. GitHub-Flavored Markdown (GFM):

    use League\CommonMark\GithubFlavoredMarkdownConverter;
    
    $gfmConverter = new GithubFlavoredMarkdownConverter();
    $html = $gfmConverter->convert('| Tables | Are | Cool |');
    

First Use Case: Rendering User-Generated Content

$converter = new CommonMarkConverter([
    'html_input' => 'strip', // Sanitize HTML input
    'allow_unsafe_links' => false, // Block unsafe links
]);

$markdown = "**User's** *content* with [links](https://example.com)";
echo $converter->convert($markdown);

Key Docs:


Implementation Patterns

Core Workflows

1. Basic Conversion Pipeline

// Initialize with default config
$converter = new CommonMarkConverter();

// Convert Markdown to HTML
$html = $converter->convert($markdown);

// Convert HTML back to Markdown (if needed)
$converter->getEnvironment()->addExtension(new \League\CommonMark\Extension\CommonMark\CommonMarkCoreExtension());
$markdown = $converter->convert($html);

2. Customizing Extensions

use League\CommonMark\Extension\CommonMark\CommonMarkCoreExtension;
use League\CommonMark\Extension\GithubFlavored\GithubFlavoredMarkdownExtension;

$environment = new \League\CommonMark\Environment\Environment();
$environment->addExtension(new CommonMarkCoreExtension());
$environment->addExtension(new GithubFlavoredMarkdownExtension());

$converter = new CommonMarkConverter(['environment' => $environment]);

3. Laravel Integration (Service Provider)

// config/services.php
'commonmark' => [
    'html_input' => 'strip',
    'allow_unsafe_links' => false,
    'extensions' => [
        \League\CommonMark\Extension\CommonMark\CommonMarkCoreExtension::class,
        \League\CommonMark\Extension\GithubFlavored\GithubFlavoredMarkdownExtension::class,
    ],
],

// app/Providers/AppServiceProvider.php
public function register()
{
    $this->app->singleton(\League\CommonMark\CommonMarkConverter::class, function ($app) {
        $config = $app['config']['services.commonmark'];
        $environment = new \League\CommonMark\Environment\Environment();
        foreach ($config['extensions'] as $extension) {
            $environment->addExtension(new $extension());
        }
        return new \League\CommonMark\CommonMarkConverter($config + ['environment' => $environment]);
    });
}

4. Dynamic Content Rendering (Blade Directives)

// app/Providers/BladeServiceProvider.php
Blade::directive('markdown', function ($expression) {
    return "<?php echo app('\\League\\CommonMark\\CommonMarkConverter')->convert(trim($expression)); ?>";
});

// Usage in Blade:
@markdown($post->content)

5. Event-Driven Extensions

use League\CommonMark\Event\DocumentParsed;
use League\CommonMark\Extension\ExtensionInterface;

class CustomExtension implements ExtensionInterface
{
    public function register(EnvironmentInterface $environment)
    {
        $environment->addEventListener(DocumentParsed::class, function (DocumentParsed $event) {
            // Modify AST after parsing
            $event->getDocument()->firstChild()->setAttribute('data-custom', 'processed');
        });
    }
}

Integration Tips

Caching Parsed Markdown

$cacheKey = 'markdown_'.$hash($markdown);
$html = cache()->remember($cacheKey, now()->addHours(1), function () use ($converter, $markdown) {
    return $converter->convert($markdown);
});

Handling Large Documents

// Stream processing for large files
$converter = new CommonMarkConverter();
$handle = fopen('large_file.md', 'r');
while (!feof($handle)) {
    $chunk = fread($handle, 8192);
    echo $converter->convert($chunk);
}
fclose($handle);

Testing Markdown Parsing

use League\CommonMark\CommonMarkConverter;
use PHPUnit\Framework\TestCase;

class MarkdownTest extends TestCase
{
    public function testConversion()
    {
        $converter = new CommonMarkConverter();
        $html = $converter->convert('# Heading');
        $this->assertEquals('<h1>Heading</h1>', $html);
    }
}

Gotchas and Tips

Pitfalls

1. Security Misconfigurations

  • Unsafe Links: Always set allow_unsafe_links => false for user-generated content.
    $converter = new CommonMarkConverter(['allow_unsafe_links' => false]);
    
  • HTML Injection: Use html_input => 'strip' to prevent XSS.
    $converter = new CommonMarkConverter(['html_input' => 'strip']);
    

2. Extension Conflicts

  • GFM vs CommonMark: Ensure you’re using the correct converter (GithubFlavoredMarkdownConverter for GFM features like tables).
  • Duplicate Extensions: Adding the same extension twice may cause unexpected behavior.

3. Performance Issues

  • Regex Overhead: Complex extensions (e.g., AutolinkExtension) can slow down parsing. Benchmark before production use.
  • Memory Leaks: Large documents may cause memory issues. Use streaming for files >1MB.

4. Encoding Problems

  • Non-UTF-8 Input: The library only supports UTF-8. Convert input with:
    $markdown = mb_convert_encoding($input, 'UTF-8', 'auto');
    

5. AST Modifications

  • Internal Methods: Avoid using @internal methods (e.g., CommonMarkConverter::getEnvironment() directly). Use the public API.
  • Breaking Changes: AST structure may change between minor versions. Test upgrades thoroughly.

Debugging Tips

1. Inspecting the AST

$converter = new CommonMarkConverter();
$document = $converter->getEnvironment()->createParser()->parse($markdown);
$ast = $document->dump(); // Debug the Abstract Syntax Tree

2. Logging Parsing Events

$environment = new \League\CommonMark\Environment\Environment();
$environment->addEventListener(\League\CommonMark\Event\DocumentParsed::class, function ($event) {
    \Log::debug('Document parsed', ['ast' => $event->getDocument()->dump()]);
});
$converter = new CommonMarkConverter(['environment' => $environment]);

3. Validating GFM Features

// Check if a GFM feature is enabled
$environment = $converter->getEnvironment();
$hasTables = $environment->getExtension('tables') !== null;

4. Common Errors & Fixes

Error Cause Solution
Invalid UTF-8 sequence Non-UTF-8 input Use mb_convert_encoding()
Unsupported extension Missing dependency Install via Composer (composer require)
HTML not stripped html_input misconfigured Set 'html_input' => 'strip'
SSRF/XSS vulnerabilities Unsafe links/HTML Enable allow_unsafe_links => false

Extension Points

1. Creating Custom Extensions

use League\CommonMark\Extension\ExtensionInterface;
use League\CommonMark\Environment\EnvironmentInterface;

class CustomInlineParserExtension implements ExtensionInterface
{
    public function register(EnvironmentInterface $environment)
    {
        $environment->addInlineParser(
            new CustomInlineParser(),
            150 // Priority (higher = earlier parsing)
        );
    }
}

class CustomInlineParser extends \League\CommonMark\InlineParser\InlineParserInterface
{
    public function parse($offset, $length, $inlineParser, $context)
    {
        // Implement custom parsing logic
    }
}

2. Modifying Renderers

use League\CommonMark\Renderer\NodeRendererInterface;
use League\CommonMark\Node\Element\AbstractBlock;

class CustomHeadingRenderer implements NodeRendererInterface
{
    public function render(AbstractBlock $block, NodeRendererContext $context)
    {
        return '<custom-heading
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.
hamzi/corewatch
minionfactory/raw-hydrator
hexters/coinpayment
rjcodes/rjcms
act-training/laravel-permissions-manager
alimarchal/laravel-chart-of-accounts
babenkoivan/elastic-scout-driver
mkwebdesign/filament-watchdog-v5
renatomarinho/laravel-page-speed
zedmagdy/filament-business-hours
renatovdemoura/blade-elements-ui
devgeek/beacon-admin
benjamin-rqt/data-watcher-bundle
atriumphp/atrium
sandermuller/package-boost-laravel
sandermuller/boost-skills
redaxo/core
yusufgenc/filament-api-forge
l3aro/rating-star-for-filament
leek/filament-subtenant-scope