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.
composer require league/commonmark
use League\CommonMark\CommonMarkConverter;
$converter = new CommonMarkConverter();
echo $converter->convert('# Hello World!');
// Output: <h1>Hello World!</h1>
CommonMarkConverter (strict CommonMark)GithubFlavoredMarkdownConverter (GFM support)$converter = new CommonMarkConverter([
'html_input' => 'strip', // Sanitize HTML input
'allow_unsafe_links' => false, // Security best practice
]);
$gfmConverter = new GithubFlavoredMarkdownConverter();
echo $gfmConverter->convert('```\n code\n```'); // Fixed tab-indented code blocks
echo $gfmConverter->convert('| Tables | Are | Cool |'); // Tables work as expected
use League\CommonMark\Environment\Environment;
use League\CommonMark\Extension\CommonMark\CommonMarkCoreExtension;
$env = new Environment();
$env->addExtension(new CommonMarkCoreExtension());
$converter = new CommonMarkConverter($env);
$gfmConverter = new GithubFlavoredMarkdownConverter();
echo $gfmConverter->convert(<<<'MD'
- List item with code:
```php
echo "Fixed!";
MD );
#### 5. Parsing Without HTML Output (AST Access)
```php
$parser = new Parser();
$document = $parser->parse('# Heading');
$xmlConverter = new MarkdownToXmlConverter($env);
echo $xmlConverter->convert($document);
// In a service provider
app()->bind('markdown', function () {
return new CommonMarkConverter();
});
// In a Blade view
{{ app('markdown')->convert($markdownContent) }}
use League\CommonMark\MarkdownParser;
public function rules()
{
return [
'content' => ['required', function ($attribute, $value, $fail) {
$parser = new MarkdownParser();
$parser->parse($value); // Validate syntax
}]
];
}
$converter = new CommonMarkConverter();
return response()->json([
'content' => $converter->convert($request->markdown)
]);
$converter = new CommonMarkConverter([
'allow_unsafe_links' => false,
'unsafe_link_targets' => ['example.com'] // Explicitly allow safe domains
]);
HTML Injection Risks
html_input => 'skip' with untrusted inputhtml_input => 'strip'Encoding Issues
mb_convert_encoding() for non-UTF-8 inputExtension Conflicts
Performance with Large Documents
Fenced Code Blocks in Lists (Fixed in 2.8.3)
XML Output for Debugging
$xmlConverter = new MarkdownToXmlConverter($env);
echo $xmlConverter->convert($document);
AST Inspection
$parser = new Parser();
$document = $parser->parse('# Test');
$walker = new NodeWalker($document);
$walker->walk(function (Node $node) {
echo get_class($node) . "\n";
});
Common Issues
GithubFlavoredMarkdownConverterCommonMarkCoreExtensionUnsafe Link Filtering (Fixed in 2.8.3)
// Previously might incorrectly block:
// https://example.com/vbscript:alert(1)
// Now properly handles URLs with vbscript:, file:, or data: after domain
Environment Order Matters
// Wrong: Extensions may override each other
$env->addExtension(new ExtensionA());
$env->addExtension(new ExtensionB());
// Better: Use EnvironmentBuilder
$env = EnvironmentBuilder::create()
->withExtensions([new ExtensionA(), new ExtensionB()])
->build();
Renderer Priority
// Last renderer added has highest priority
$env->addRenderer(new CustomRenderer());
GFM Selective Enablement
$env = EnvironmentBuilder::create()
->withExtensions([
new CommonMarkCoreExtension(),
new TableExtension(), // Only enable tables
])
->build();
Link Security Configuration
// Explicitly allow specific domains
$converter = new CommonMarkConverter([
'allow_unsafe_links' => false,
'unsafe_link_targets' => ['trusted.com', 'api.example.org']
]);
Custom Node Types
class CustomNode extends AbstractNode {
// Implement node logic
}
$env->addNodeType(new CustomNodeType());
Inline Parser Extensions
$env->addInlineParser(new CustomInlineParser());
Block Parser Extensions
$env->addBlockParser(new CustomBlockParser());
Renderer Overrides
$env->addRenderer(new CustomHeadingRenderer());
Disable Unused Extensions
$env = EnvironmentBuilder::create()
->withExtensions([new CommonMarkCoreExtension()])
->withoutExtensions([TableExtension::class]) // Disable tables
->build();
Cache Parsed Documents
$cache = new ArrayCache();
$parser = new Parser();
$document = $parser->parse($markdown);
$cache->set('markdown_'.$hash, $document, 3600);
Use StringInput for Simple Cases
$converter = new CommonMarkConverter();
$html = $converter->convertToHtml($markdownString);
Test Edge Cases After Upgrade
// Verify fixed issues in 2.8.3
$testCases = [
'Tab-indented code in lists',
'URLs with vbscript: after domain',
'Complex nested lists with code blocks'
];
foreach ($testCases as $case) {
$converter->convert($case);
}
How can I help you explore Laravel packages today?