The Markdown Escape PHP library includes a powerful templating system that allows you to generate complex Markdown documents while ensuring all dynamic content is properly escaped.
- Introduction
- Getting Started
- Template Syntax
- Built-in Templates
- Custom Templates
- Template Loading
- Advanced Features
- Best Practices
- API Reference
The templating system combines PHP's native templating capabilities with the library's context-aware escaping functionality. This ensures that:
- Dynamic content is always properly escaped for Markdown
- Templates are easy to write and understand
- The system is extensible and customizable
- Performance is optimized through caching
- PHP Short Tags: Use familiar PHP syntax with
<?= ?>for output - Automatic Escaping: Access to all escaping methods within templates
- Template Inheritance: Override and extend templates using loaders
- Multiple Sources: Load templates from files, arrays, or custom sources
- Built-in Templates: Ready-to-use templates for common use cases
use Markdown\Escape\MarkdownTemplate;
// Create a template instance
$template = MarkdownTemplate::commonMark(); // or ::gfm() for GitHub Flavored
// Render a string template
$result = $template->renderString(
'Hello, <?= $md->escapeContent($name) ?>!',
['name' => '**World**']
);
// Output: Hello, \*\*World\*\*!// Render a table
$result = $template->render('table', [
'headers' => ['Name', 'Status', 'Progress'],
'rows' => [
['Feature **A**', 'Complete', '100%'],
['Feature *B*', 'In Progress', '75%'],
]
]);Templates use PHP short tags for dynamic content. The following variables are always available:
$mdor$markdown- The MarkdownEscape instance$escapers- Object providing access to all escapers$context- The template context with all variables- All variables passed to
render()orrenderString()
// General content (paragraphs, list items, etc)
<?= $md->escapeContent($text) ?>
// URLs for links and images
<?= $md->escapeUrl($url) ?>
// Inline code
<?= $md->escapeInlineCode($code) ?>
// Code blocks
<?= $md->escapeCodeBlock($code) ?>// Access escapers through the $escapers object
<?= $escapers->content($text) ?>
<?= $escapers->url($link) ?>
<?= $escapers->inlineCode($code) ?>
// Custom escapers (if added)
<?= $escapers->custom($text) ?><?php if ($showSection): ?>
## <?= $md->escapeContent($sectionTitle) ?>
<?php endif; ?>
<?php foreach ($items as $item): ?>
- <?= $md->escapeContent($item) ?>
<?php endforeach; ?>The library includes several pre-built templates:
Creates a structured document with title, description, and sections.
$template->render('document', [
'title' => 'My Document',
'description' => 'Document description',
'sections' => [
['title' => 'Section 1', 'content' => 'Content...'],
['title' => 'Section 2', 'content' => 'More content...'],
]
]);Renders a Markdown table with proper escaping.
$template->render('table', [
'headers' => ['Column 1', 'Column 2'],
'rows' => [
['Row 1 Col 1', 'Row 1 Col 2'],
['Row 2 Col 1', 'Row 2 Col 2'],
]
]);Creates bulleted lists with optional sub-items.
$template->render('list', [
'items' => [
'Simple item',
[
'text' => 'Item with sub-items',
'subItems' => ['Sub 1', 'Sub 2']
],
]
]);Displays code with syntax highlighting and optional output.
$template->render('code-example', [
'title' => 'Example',
'description' => 'Shows how to use the feature',
'language' => 'php',
'code' => '<?php echo "Hello"; ?>',
'output' => 'Hello'
]);Creates a list of links with optional descriptions.
$template->render('link-list', [
'links' => [
['text' => 'GitHub', 'url' => 'https://github.com'],
['text' => 'Docs', 'url' => './docs', 'description' => 'Documentation'],
]
]);Documents API endpoints.
$template->render('api-method', [
'method' => 'GET',
'endpoint' => '/api/users/{id}',
'description' => 'Get user by ID',
'parameters' => [
['name' => 'id', 'type' => 'integer', 'required' => true, 'description' => 'User ID'],
],
'example' => [
'language' => 'json',
'code' => '{"id": 1, "name": "John"}'
]
]);Create a .php file with your template:
// templates/article.php
# <?= $md->escapeContent($title) ?>
*By <?= $md->escapeContent($author) ?> on <?= date('F j, Y', strtotime($date)) ?>*
<?php if (!empty($tags)): ?>
**Tags**: <?= implode(', ', array_map(function($tag) use ($md) {
return '`' . $md->escapeInlineCode($tag) . '`';
}, $tags)) ?>
<?php endif; ?>
<?= $md->escapeContent($content) ?>
<?php if (!empty($related)): ?>
## Related Articles
<?php foreach ($related as $article): ?>
- [<?= $md->escapeContent($article['title']) ?>](<?= $md->escapeUrl($article['url']) ?>)
<?php endforeach; ?>
<?php endif; ?>// Add a directory containing templates
$template->addPath('/path/to/templates');
// Add templates with namespace
$template->addPath('/path/to/admin/templates', 'admin');
// Add default templates programmatically
$template->addDefaultTemplates([
'greeting' => 'Hello, <?= $md->escapeContent($name) ?>!',
]);
// Render custom template
$result = $template->render('article', [
'title' => 'My Article',
'author' => 'John Doe',
'date' => '2024-01-15',
'content' => 'Article content...',
'tags' => ['php', 'markdown'],
]);The library supports multiple template loading strategies:
Loads templates from the filesystem:
use Markdown\Escape\Template\Loader\FileTemplateLoader;
$loader = new FileTemplateLoader('/path/to/templates');
$loader->addPath('admin', '/path/to/admin/templates');Stores templates in memory:
use Markdown\Escape\Template\Loader\ArrayTemplateLoader;
$loader = new ArrayTemplateLoader([
'welcome' => 'Welcome, <?= $name ?>!',
'goodbye' => 'Goodbye, <?= $name ?>!',
]);Combines multiple loaders with priority:
use Markdown\Escape\Template\Loader\ChainTemplateLoader;
$defaultLoader = new ArrayTemplateLoader($defaultTemplates);
$defaultLoader->setPriority(10);
$customLoader = new FileTemplateLoader('/custom/templates');
$customLoader->setPriority(20); // Higher priority
$chainLoader = new ChainTemplateLoader([$defaultLoader, $customLoader]);Create custom escapers for special formatting:
use Markdown\Escape\Escaper\AbstractEscaper;
class HighlightEscaper extends AbstractEscaper {
public function escape(string $text): string {
// Escape markdown and wrap in highlight syntax
$escaped = $this->dialect->escapeSpecialCharacters($text);
return "=={$escaped}==";
}
}
// Add to renderer
$renderer = $template->getRenderer();
$renderer->addEscaper('highlight', new HighlightEscaper($context, $dialect));
// Use in templates
// <?= $escapers->highlight($importantText) ?>Access and manipulate the template context:
// In template:
<?php $context->set('computed', $value * 2); ?>
<?= $context->get('computed') ?>
// Check if variable exists
<?php if ($context->has('optional')): ?>
<?= $md->escapeContent($context->get('optional')) ?>
<?php endif; ?>Render templates within templates:
// In a template:
<?php
$renderer = $context->getMarkdownEscape()->getFactory()->createRenderer();
echo $renderer->render('partial', ['data' => $data] + $context->all());
?>Handle template errors gracefully:
use Markdown\Escape\Exception\TemplateNotFoundException;
use Markdown\Escape\Exception\TemplateRenderException;
try {
$result = $template->render('my-template', $data);
} catch (TemplateNotFoundException $e) {
echo "Template not found: " . $e->getTemplateName();
echo "Searched in: " . implode(', ', $e->getSearchPaths());
} catch (TemplateRenderException $e) {
echo "Render error in template: " . $e->getTemplateName();
echo "Error: " . $e->getMessage();
}Never output user content without escaping:
// ❌ Bad
<?= $userInput ?>
// ✅ Good
<?= $md->escapeContent($userInput) ?>Choose the appropriate escaper for the context:
// For general content
<?= $md->escapeContent($text) ?>
// For URLs in links
[Link](<?= $md->escapeUrl($url) ?>)
// For inline code
Use `<?= $md->escapeInlineCode($code) ?>` for this
// For code blocks``
### 3. Organize Templates
Use namespaces to organize templates:
```php
$template->addPath('/templates/emails', 'email');
$template->addPath('/templates/docs', 'docs');
// Use namespaced templates
$template->render('email/welcome', $data);
$template->render('docs/api', $data);
The file loader automatically caches loaded templates. For production, ensure the template cache directory is writable.
Validate data before passing to templates:
// Ensure required fields exist
$data = array_merge([
'title' => 'Untitled',
'content' => '',
'author' => 'Anonymous',
], $userData);
$result = $template->render('article', $data);For complex templates, document expected variables:
<?php
/**
* Article Template
*
* @var string $title Article title
* @var string $author Author name
* @var string $date Publication date (Y-m-d)
* @var string $content Article content
* @var array $tags Array of tag strings
* @var array $related Array of related articles
*/
?>
# <?= $md->escapeContent($title) ?>Main facade for template rendering.
__construct(?DialectInterface $dialect = null)- Create instancestatic commonMark()- Create CommonMark instancestatic gfm()- Create GitHub Flavored instancerender(string $template, array $vars = [])- Render templaterenderString(string $content, array $vars = [])- Render stringaddPath(string $path, string $namespace = '')- Add template directoryaddDefaultTemplates(array $templates)- Add default templatesgetRenderer()- Get the renderer instancegetMarkdownEscape()- Get the escape instance
Core rendering engine.
render(string $name, array $vars = [])- Render template by namerenderString(string $content, array $vars = [])- Render string templatesetLoader(TemplateLoaderInterface $loader)- Set template loadersetEngine(TemplateEngineInterface $engine)- Set template enginesetMarkdownEscape(MarkdownEscape $escape)- Set escape instanceaddEscaper(string $name, EscaperInterface $escaper)- Add custom escaper
__construct($paths = [], string $extension = '.php')- Create file loaderaddPath(string $namespace, $path)- Add search path
__construct(array $templates = [])- Create array loaderaddTemplate(string $name, $template)- Add template
__construct(array $loaders = [])- Create chain loaderaddLoader(TemplateLoaderInterface $loader)- Add loader to chain
- Examples - Working examples of template usage
- Architecture - Understanding the system design
- User Guide - General usage documentation