markup-carve / symfony-carve
Symfony bundle to render Carve markup to HTML via carve-php.
Package info
github.com/markup-carve/symfony-carve
Type:symfony-bundle
pkg:composer/markup-carve/symfony-carve
Fund package maintenance!
Requires
- php: ^8.2
- markup-carve/carve-php: ^0.1.9
- psr/log: ^1.1 || ^2.0 || ^3.0
- symfony/config: ^6.4 || ^7.0
- symfony/dependency-injection: ^6.4 || ^7.0
- symfony/http-kernel: ^6.4 || ^7.0
Requires (Dev)
- php-collective/code-sniffer: ^0.6.0
- phpstan/phpstan: ^2.0
- phpunit/phpunit: ^11.0 || ^12.0 || ^13.0
- symfony/framework-bundle: ^6.4 || ^7.0
- symfony/twig-bundle: ^6.4 || ^7.0
- twig/twig: ^3.0
Suggests
- symfony/twig-bundle: To use the {{ value|carve }} Twig filter and carve() function
Provides
None
Conflicts
None
Replaces
None
README
Symfony bundle that renders Carve markup to HTML using carve-php.
Carve is a lightweight markup language for structured documents, with clear, consistent syntax.
Installation
composer require markup-carve/symfony-carve
Register the bundle (Symfony Flex does this automatically; otherwise add it to config/bundles.php):
return [ // ... MarkupCarve\SymfonyCarve\CarveBundle::class => ['all' => true], ];
Usage
Service
use MarkupCarve\SymfonyCarve\CarveRenderer; public function show(CarveRenderer $carve): Response { $html = $carve->render('# Hello *world*'); $text = $carve->renderText('# Hello *world*'); $markdown = $carve->renderMarkdown('# Hello *world*'); return new Response($html); }
Twig
{# filter #} {{ article.body|carve }} {# function #} {{ carve('# Inline /snippet/') }} {# plain text and Markdown filters (escaped normally by Twig) #} {{ article.body|carve_text }} {{ article.body|carve_markdown }}
Only the HTML output from carve is marked safe, so Twig does not double-escape it. The
carve_text and carve_markdown filters are not HTML and Twig escapes them normally. Safe mode
only affects HTML rendering; profiles apply to all three output formats.
Configuration
# config/packages/carve.yaml carve: safe_mode: true # sanitize HTML (default: true) raw_html: strip # strip | escape | allow (default: strip) profile: null # null | full | article | comment | minimal (default: null) diagrams: [] # diagram presets to enable (default: none) include_root: null # absolute trusted filesystem root (default: off)
| Key | Type | Default | Description |
|---|---|---|---|
safe_mode |
bool | true |
Enable HTML sanitization. Keep this on for untrusted input. |
raw_html |
enum | strip |
How raw HTML is handled when safe_mode is on: strip, escape, allow. |
profile |
enum|null | null |
Restrict markup features using full, article, comment, or minimal. |
diagrams |
string[] | [] |
Diagram fenced-block presets to enable (see below). Off by default. |
include_root |
string|null | null |
Absolute containment root for file-backed rendering. |
Setting safe_mode: false disables sanitization entirely. Only do this for fully trusted input.
Safe mode only affects HTML output. Profiles restrict available constructs for HTML, plain-text,
and Markdown output; null leaves all constructs available.
File includes
Twig filters and CarveRenderer::render() accept anonymous strings, so they
never read files. Set an absolute include_root and call the explicit file API
for trusted, file-backed content:
$report = $renderer->renderFileWithReport('/srv/docs/book/main.crv'); $html = $report['value']; $warnings = $report['warnings']; $dependencies = $report['dependencies'];
renderFile() is the HTML-only convenience form. Relative includes resolve
from the file containing each directive and cannot traverse or follow a symlink
outside the configured root. Warning reports omit resolver details and replace
outside paths before they reach the optional PSR logger.
include_root has to be an absolute path, and the renderer refuses a relative
one rather than resolving it against the working directory, which is arbitrary
with respect to the document.
The dependency list contains resolved and attempted targets. Applications that cache rendered HTML must include those identities in their invalidation policy, including missing targets, so creating a formerly missing file invalidates its parent. The bundle does not prescribe a cache implementation.
Diagrams
By default a fenced block like ``` plantuml renders as a plain code block. Listing a
preset under diagrams turns that fence into a hydration element for a client-side renderer:
# config/packages/carve.yaml carve: diagrams: ['plantuml', 'mermaid']
Now ``` plantuml renders as <pre class="plantuml">...</pre> and ``` mermaid
as <pre class="mermaid">...</pre>, ready for a browser library to pick up.
| Preset | Fence word(s) | Output |
|---|---|---|
mermaid |
mermaid |
<pre class="mermaid"> |
plantuml |
plantuml, puml |
<pre class="plantuml"> |
d2 |
d2 |
<pre class="d2"> |
graphviz |
dot, graphviz |
<pre class="graphviz"> |
wavedrom |
wavedrom |
<pre class="wavedrom"> |
vega_lite |
vega-lite |
<div class="vega-lite"><script type="application/json"> |
chart |
chart |
<div class="chart"><script type="application/json"> |
abc |
abc |
<pre class="abc"> |
The bundle only emits the markup - it does not ship or load any renderer. You supply the client side:
- Graphviz, D2 render fully offline (no server, no external call) with the WebAssembly helpers
from
@markup-carve/carve-grammars:renderDiagrams(orrenderGraphvizDiagrams/renderD2Diagrams). - PlantUML has no practical in-browser renderer; render it via a Kroki
server with the same package's
renderKrokiDiagramshelper.⚠️ Privacy / GDPR: the default Kroki server is the public
https://kroki.io, so the PlantUML source is sent to a third party outside your domain. For sensitive content, or to stay offline, point the helper'sserveroption at a self-hosted or localhost Kroki, and disclose the external call to end users where required. - Mermaid, WaveDrom, Vega-Lite, Chart.js, ABC each need their own browser library loaded on the page (mermaid.js, wavedrom, vega-embed, chart.js, abcjs).
Unknown names in the whitelist are rejected by config validation; the accepted values are exactly the presets above.
Demo
A full runnable demo app lives at symfony-carve-demo: the Twig filter and function, the CarveRenderer service, a live editor, a safe-mode comparison, and a syntax gallery.
