markup-carve / carve-php
PHP parser for Carve, a human-centered lightweight markup language derived from djot-php.
Fund package maintenance!
Requires
- php: ^8.2
Requires (Dev)
- nikic/php-fuzzer: ^0.0.11
- php-collective/code-sniffer: dev-master
- phpstan/phpstan: ^2.1.32
- phpunit/phpunit: ^11.0 || ^12.0 || ^13.0
Suggests
- ext-intl: Recommended for heading-ID transliteration: enables ICU romanization of non-Latin scripts (e.g. CJK, Arabic). Without it, a baked map covers Latin/Cyrillic/punctuation identically and other scripts fall back to the generated `section` id.
This package is auto-updated.
Last update: 2026-06-15 01:05:27 UTC
README
PHP parser and renderer for Carve, a post-Markdown lightweight markup language with visual mnemonics and human-centered design.
Status
Carve's syntax is implemented - the delimiter swaps, table changes, captions, and custom extension syntax - and passes the shared Carve spec corpus.
Origins
Carve-PHP is a hard fork of djot-php by the PHP Collective. The fork preserves the architecture, AST, renderer pipeline, profiles, and extensions, and replaces Djot's syntax rules with Carve's. The MIT license carries over; copyright lines remain in LICENSE.
For the original Djot implementation, use php-collective/djot instead.
Installation
composer require markup-carve/carve-php
Usage
use Carve\CarveConverter; $converter = new CarveConverter(); $html = $converter->toHtml('# Hello /Carve/');
Besides HTML, the same AST renders to Markdown, plain text, and ANSI via the
CarveConverter::markdown(), ::plainText(), and ::ansi() factories:
$markdown = CarveConverter::markdown()->convert('# Hello /Carve/'); $ansi = CarveConverter::ansi()->convert('# Hello /Carve/');
CLI
The package ships a bin/carve executable that reads Carve from a file or
stdin and writes the rendered output to stdout. HTML is the default; pass a
format flag for another output:
bin/carve README.crv > README.html # HTML (default) bin/carve --markdown README.crv # Markdown bin/carve --plain README.crv # plain text bin/carve --ansi README.crv # ANSI-colored terminal text echo '# Hello' | bin/carve # render from stdin
--html / --markdown (--md) / --plain (--plain-text) / --ansi select
the format. -o FILE writes to a file; -w/--warnings and --strict report
parse warnings (exit 1 under --strict); -x/--xhtml and -s/--safe apply
to HTML output only. Run bin/carve --help for the full list.
Sandbox
Try this implementation live in the Carve sandbox - explore syntax and extensions, inspect output, and share snippets via pastebin-style links. It also powers the wp-carve WordPress plugin.
Extension Matchers
Carve-PHP supports parse-stage extension matchers alongside render hooks and document transforms. Matchers are tried only where core syntax declines, so core parsing always wins first.
use Carve\CarveConverter; use Carve\Node\Inline\Text; use Carve\Parser\MatcherContext; $converter = new CarveConverter(); $converter->getParser()->getInlineParser()->addInlineMatcher( function (string $text, int $pos, MatcherContext $ctx): ?array { if (!preg_match('/\G\{\{([a-z]+)\}\}/', $text, $m, 0, $pos)) { return null; } return ['node' => new Text('VAR:' . $m[1]), 'end' => $pos + strlen($m[0])]; }, );
MatcherContext exposes definition tables (getReference(), hasFootnote(),
getAbbreviation()) and recursive parse helpers (parseInlines(),
parseBlocks()). Matchers run by descending priority, then registration
order. addInlinePattern() and addBlockPattern() remain available as regex
sugar over the same matcher contract.
The normative extension contract lives in
carve/docs/extensions.md.
Extensions bundled with this package (such as PlusBulletExtension) are
documented in docs/extensions.md.
License
MIT — see LICENSE.