antonioprimera / md-parser
自定义Markdown处理器
Requires
- php: >=8.1
- antonioprimera/php-helpers: ^0.1.0
- illuminate/support: >=10
Requires (Dev)
- pestphp/pest: ^2.34
README
这是一个高度可定制的Markdown解析器,它包含一个预定义的Markdown风味,但允许您轻松创建任何其他Markdown风味,无论是标准的还是您刚刚发明的。
例如,您可以创建一个Markdown风味,允许您通过ID(存储在数据库中的对象的ID)或slug嵌入图像,或者可能是一个允许您通过名称嵌入hero-icon的自定义标签。您甚至可以决定段落应该由一个|
字符而不是换行符来分隔。
以下是一些您可以使用此解析器轻松完成的示例
An image:   A list of items with hero-icons: ## Contact [heroicon:phone] [+10 (1234) 567 890](tel:+10 1234.567.890) [heroicon:envelope] [Email me](abc@def.test) A gallery: !! Or even a special internal link to an article with a slug: [Read more about this](article:my-article-slug)
基本用法(框架无关)
主解析器是一个类,它通过一组内联解析器、一组块解析器、可选的块分割器和可选的配置来实例化。实例化后,您可以调用parse
方法,传入Markdown字符串,它将返回解析结果。
首先,通过composer导入包
composer require antonio-primera/md-parser
然后,您可以使用默认风味,或创建自己的。
如果您想使用默认风味,可以这样做
$parser = \AntonioPrimera\Md\MarkdownFlavors\BasicMarkdownParser::create(); $html = $parser->parse($markdownString);
如果您想创建自己的解析器,或只是更改默认解析器的行为,请查看下面的高级用法部分。
在Laravel项目中使用
如果您在Laravel项目中使用此包,所有设置都已为您完成,因此您可以像这样使用Md
外观
use AntonioPrimera\Md\Facades\Md; $html = Md::parse($markdownString);
默认情况下,此包的服务提供者将使用config/md-config.php
文件中的配置来创建解析器实例。如果您想更改配置,可以通过运行以下命令发布配置文件
php artisan vendor:publish --tag=md-config
您有两个主要选项使用配置来构建解析器
- 在配置文件中提供一个
flavor.factory
条目,其中包含您的工厂类的类,该工厂将返回一个解析器实例,并可选地提供一个flavor.config
条目,其中包含传递给工厂的配置。 - 为解析器提供必要的构建块:
flavor.inlineParsers
、flavor.blockParsers
、flavor.splitter
(这是可选的)和flavor.config
(这也是可选的)。
高级用法
如果您想创建自己的风味,可以通过创建一个返回AntonioPrimera\Md\Parser
实例的工厂类来实现,该实例加载了您的自定义块和内联解析器,或者混合和匹配现有的。
以下是默认风味是如何创建的
class BasicMarkdownParser { public static function create(array $config = []): Parser { return new Parser( inlineParsers: [ new ImageParser(), //this has to go before the LinkParser, because it uses a similar syntax new LinkParser(), new BoldParser(), new ItalicParser(), new LineBreakParser(), //this has to go last, because other parsers may use "|" in their syntax ], blockParsers: [ new HeadingParser(['baseHeadingLevel' => $config['baseHeadingLevel'] ?? 2]), new UnorderedListItemParser(), new BlockQuoteParser(), new ParagraphParser(), ], config: $config ); } }
块解析器
块解析器是一个继承自AntonioPrimera\Md\BlockParser
类的类。它有两个方法:matches
用于检查块是否匹配此解析器并应由它处理,以及parse
用于解析块。一旦块被块解析器匹配和解析,结果将被添加到解析结果中,并停止其解析。
一个简单的块解析器,它会解析空白行(只包含空白字符的块)将看起来像这样
class EmptyBlockParser extends BlockParser { public function matches(string $text, array $parsedBlocks = []): bool { //this parser will match empty blocks (blocks that contain only whitespace) return empty(trim($text)); } public function parse(string $text, array &$parsedBlocks = []): string|MarkdownBlock|null { //instead of returning an empty string, we return a special class that will be styled with CSS return '<p class="empty-block"></p>'; } }
内联解析器
内联解析器是一个继承自 AntonioPrimera\Md\InlineParser
类的类。它有一个单独的 parse
方法,用于在给定文本中查找和替换内联元素。解析器首先在文本上运行所有内联解析器,然后将文本分割成块,并对每个块运行块解析器。
一个简单的内联解析器,用于解析类似 [heroicon:icon-name]
的英雄图标语法,可能看起来像这样
class HeroIconParser extends InlineParser { public function parse(string $text): string { $pattern = '/\[heroicon:(.+)\]/'; return preg_replace_callback($pattern, fn($matches) => "<i class='heroicon heroicon-$matches[1]'></i>", $text); } }
分割器
Markdown 分割器是一个扩展自 AntonioPrimera\Md\Splitters\MarkdownSplitter
类的类。它有一个单独的 split
方法,接收一个 Markdown 字符串并返回一个块数组。然后这些块被块解析器解析。
在实例化解析器时,你可以传递一个分割器实例(如果你自己实现的),或者你可以传递一个字符串,该字符串将被用作分割 Markdown 字符串为块的分隔符。默认情况下,使用换行符 \n
。
配置
在实例化解析器时,你可以为每个内联解析器和块解析器实例传递一个配置,或者你可以只传递一个配置数组,该数组将被部分转发到所有解析器,基于它们的别名。
这样做是为了将配置保存在一个地方,或者允许动态配置(例如,基于存储在数据库中的某些用户输入)。
每个内联解析器和块解析器都有一个别名,默认情况下是去掉 parser
后缀的 kebab-case 类基名(例如,HeroIconParser
的别名为 hero-icon
)。如果你想在解析器类中使用不同的别名,可以覆盖别名方法。然后你可以使用别名作为配置数组中的键来传递该解析器的配置。
$parser = new Parser( inlineParsers: [new HeroIconParser()], //and other inline parsers blockParsers: [new HeadingParser()], //and other block parsers config: [ 'hero-icon' => ['iconClass' => 'heroicon', 'iconSubset' => 'heroicon-solid'], 'heading' => ['baseHeadingLevel' => 3] ] );
这与直接传递配置到解析器实例相同
$parser = new Parser( inlineParsers: [new HeroIconParser(['iconClass' => 'heroicon', 'iconSubset' => 'heroicon-solid'])], blockParsers: [new HeadingParser(['baseHeadingLevel' => 3])], );
贡献
还需要完成什么
- 优化解析器(例如,进行基准测试和找到瓶颈)
- 添加更多测试
- 添加更多节点解析器(如代码块解析器、有序列表解析器等)
- 添加更多内联解析器(如英雄图标解析器等)
- 使这份文档更好
如果你想贡献,请随意fork仓库并提交一个pull request。如果你有任何问题,请随时提问。
Markdown解析器的多数规范基于以下资源