This guide covers all aspects of using the Markdown Escape PHP library.
Markdown Escape PHP is a library designed to safely escape content that will be embedded in Markdown documents. It prevents your content from being interpreted as Markdown formatting, ensuring that special characters are displayed literally.
When programmatically generating Markdown content, you often need to include user-provided text or data that might contain Markdown special characters. Without proper escaping, these characters could break your document's formatting or create unintended structures.
Install the library using Composer:
composer require schlessera/markdown-escapeuse Markdown\Escape\MarkdownEscape;
// Default instance (uses CommonMark dialect)
$escape = new MarkdownEscape();
// Specific dialect instances
$commonMark = MarkdownEscape::commonMark();
$gfm = MarkdownEscape::gfm();The library provides convenient methods for common escaping scenarios:
// Escape general content
$text = 'This has *asterisks* and _underscores_';
$escaped = $escape->escapeContent($text);
// Result: This has \*asterisks\* and \_underscores\_
// Escape URLs
$url = 'https://example.com/path with spaces';
$escaped = $escape->escapeUrl($url);
// Result: https://example.com/path%20with%20spaces
// Escape inline code
$code = 'Use `backticks` for inline code';
$escaped = $escape->escapeInlineCode($code);
// Result: ``Use `backticks` for inline code``
// Escape code blocks
$code = "function test() {\n return true;\n}";
$escaped = $escape->escapeCodeBlock($code, [
'use_fences' => true,
'language' => 'php'
]);Contexts define what type of content you're escaping and how it should be handled.
Used for regular text that might appear anywhere in a Markdown document:
use Markdown\Escape\Context\GeneralContentContext;
$context = new GeneralContentContext();
$escaped = $escape->escape('# Not a heading', $context);Specifically for URLs that will be used in links:
use Markdown\Escape\Context\UrlContext;
$context = new UrlContext(['encode_unicode' => true]);
$escaped = $escape->escape('https://example.com/página', $context);For content that will be wrapped in backticks:
use Markdown\Escape\Context\InlineCodeContext;
$context = new InlineCodeContext();
$escaped = $escape->escape('const a = `template`', $context);For multi-line code that will be in code blocks:
use Markdown\Escape\Context\CodeBlockContext;
$context = new CodeBlockContext([
'use_fences' => true,
'language' => 'javascript'
]);
$escaped = $escape->escape($code, $context);The default dialect, supporting standard CommonMark features:
$escape = MarkdownEscape::commonMark();Features:
- Basic emphasis (italic and bold)
- Links and images
- Code blocks and inline code
- Lists (ordered and unordered)
- Blockquotes
- Headings
- Tables
Extended dialect with GitHub-specific features:
$escape = MarkdownEscape::gfm();Additional features:
- Task lists
- Strikethrough (
text) - Autolinks
- Mentions (@username)
- Emoji (:emoji:)
- Footnotes
You can switch dialects on an existing instance:
$escape = new MarkdownEscape();
$gfmDialect = new GitHubFlavoredMarkdownDialect();
$newEscape = $escape->withDialect($gfmDialect);The library includes a powerful templating system for generating complex Markdown documents. Templates use PHP short tags and automatically handle escaping.
use Markdown\Escape\MarkdownTemplate;
// Create a template instance
$template = MarkdownTemplate::gfm();
// Render a string template
$result = $template->renderString(
'Hello, <?= $md->escapeContent($name) ?>!',
['name' => '**World**']
);
// Output: Hello, \*\*World\*\*!
// Use built-in templates
$result = $template->render('table', [
'headers' => ['Name', 'Value'],
'rows' => [['Item *1*', '100%']]
]);The library includes several ready-to-use templates:
document- Structured documents with sectionstable- Markdown tableslist- Bulleted lists with sub-itemscode-example- Code examples with outputlink-list- Lists of linksapi-method- API endpoint documentation
Create your own templates:
// Add a directory containing templates
$template->addPath('/path/to/templates');
// Add default templates programmatically
$template->addDefaultTemplates([
'greeting' => 'Hello, <?= $md->escapeContent($name) ?>!',
]);Templates have access to:
$mdor$markdown- The MarkdownEscape instance$escapers- Object providing access to all escapers- All variables passed to render()
For comprehensive templating documentation, see the Templating Guide.
Create your own context by extending AbstractContext:
use Markdown\Escape\Context\AbstractContext;
class CustomContext extends AbstractContext
{
public const NAME = 'custom';
protected function configure(): void
{
$this->escapingTypes = ['custom_type'];
}
}For more control, work directly with the factory:
use Markdown\Escape\EscaperFactory;
use Markdown\Escape\Context\GeneralContentContext;
use Markdown\Escape\Dialect\CommonMarkDialect;
$factory = new EscaperFactory();
$context = new GeneralContentContext();
$dialect = new CommonMarkDialect();
$escaper = $factory->createEscaper($context, $dialect);
$escaped = $escaper->escape('Content to escape');$factory = new EscaperFactory();
$factory->registerEscaper('custom', 'commonmark', $customEscaper);Always use the most specific context for your use case:
// Good: Using URL context for URLs
$escaped = $escape->escapeUrl($userProvidedUrl);
// Bad: Using general content context for URLs
$escaped = $escape->escapeContent($userProvidedUrl);Use the appropriate dialect for where your Markdown will be rendered:
// For GitHub
$escape = MarkdownEscape::gfm();
// For standard Markdown parsers
$escape = MarkdownEscape::commonMark();Always escape user-provided content before including it in Markdown:
$comment = $_POST['comment'];
$safeComment = $escape->escapeContent($comment);
$markdown = "## User Comment\n\n" . $safeComment;When escaping code blocks, specify the language for syntax highlighting:
$code = file_get_contents('example.php');
$escaped = $escape->escapeCodeBlock($code, [
'use_fences' => true,
'language' => 'php'
]);For international URLs, consider Unicode encoding:
$url = 'https://example.com/文档';
$escaped = $escape->escapeUrl($url, ['encode_unicode' => true]);$escape = MarkdownEscape::gfm();
$title = $escape->escapeContent($userTitle);
$description = $escape->escapeContent($userDescription);
$codeSnippet = $escape->escapeCodeBlock($userCode, [
'use_fences' => true,
'language' => 'php'
]);
$markdown = <<<MD
# {$title}
{$description}
## Example Code
{$codeSnippet}
For more information, visit [{$escape->escapeContent($linkText)}]({$escape->escapeUrl($linkUrl)})
MD;$headers = ['Name', 'Value*', 'Description'];
$rows = [
['config_file', '/etc/app.conf', 'Main [configuration] file'],
['log|level', 'debug', 'Logging level (debug|info|warn|error)']
];
$markdown = "| " . implode(" | ", array_map([$escape, 'escapeContent'], $headers)) . " |\n";
$markdown .= "|" . str_repeat(" --- |", count($headers)) . "\n";
foreach ($rows as $row) {
$markdown .= "| " . implode(" | ", array_map([$escape, 'escapeContent'], $row)) . " |\n";
}