sanitize = config('documents.markdown.sanitize', true);
// 创建环境配置
$config = [
'html_input' => $this->sanitize ? 'strip' : 'allow', // 根据配置决定是否剥离 HTML 标签
'allow_unsafe_links' => false, // 不允许不安全的链接
'max_nesting_level' => 10, // 最大嵌套层级
];
// 创建环境并添加扩展
$environment = new Environment($config);
$environment->addExtension(new CommonMarkCoreExtension());
$environment->addExtension(new TableExtension()); // 支持表格
$environment->addExtension(new StrikethroughExtension()); // 支持删除线
$environment->addExtension(new TaskListExtension()); // 支持任务列表
// 创建转换器
$this->converter = new MarkdownConverter($environment);
}
/**
* 将 Markdown 内容渲染为 HTML
*
* @param string $markdown Markdown 内容
* @return string 渲染后的 HTML
*/
public function render(string $markdown): string
{
try {
// 转换 Markdown 为 HTML
$html = $this->converter->convert($markdown)->getContent();
// 清理和美化 HTML
$html = $this->sanitize($html);
return $html;
} catch (\Exception $e) {
// 如果渲染失败,返回错误信息
return '
Markdown 渲染失败:' . htmlspecialchars($e->getMessage()) . '
';
}
}
/**
* 清理 HTML 内容,防止 XSS 攻击
*
* @param string $html HTML 内容
* @return string 清理后的 HTML
*/
public function sanitize(string $html): string
{
// CommonMark 已经配置了 html_input => 'strip',会自动剥离 HTML 标签
// 这里我们添加额外的样式包装
$styledHtml = '';
// 添加基本的 Markdown 样式
$styledHtml .= $this->getMarkdownStyles();
$styledHtml .= $html;
$styledHtml .= '
';
return $styledHtml;
}
/**
* 获取 Markdown 内容的 CSS 样式
*
* @return string CSS 样式
*/
protected function getMarkdownStyles(): string
{
return '';
}
/**
* 从 Markdown 内容中提取摘要
*
* @param string $markdown Markdown 内容
* @param int|null $length 摘要长度(字符数),如果为 null 则使用配置文件中的默认值
* @return string 摘要文本
*/
public function extractPreview(string $markdown, ?int $length = null): string
{
// 如果未指定长度,使用配置文件中的默认值
if ($length === null) {
$length = config('documents.markdown.preview_length', 500);
}
// 移除 Markdown 标记,获取纯文本
$text = $this->stripMarkdown($markdown);
// 移除多余的空白字符
$text = preg_replace('/\s+/', ' ', $text);
$text = trim($text);
// 截取指定长度
if (mb_strlen($text) > $length) {
$text = mb_substr($text, 0, $length) . '...';
}
return $text;
}
/**
* 移除 Markdown 标记,返回纯文本
*
* @param string $markdown Markdown 内容
* @return string 纯文本
*/
protected function stripMarkdown(string $markdown): string
{
// 移除代码块
$text = preg_replace('/```[\s\S]*?```/', '', $markdown);
$text = preg_replace('/`[^`]+`/', '', $text);
// 移除标题标记
$text = preg_replace('/^#{1,6}\s+/m', '', $text);
// 移除链接,保留文本
$text = preg_replace('/\[([^\]]+)\]\([^\)]+\)/', '$1', $text);
// 移除图片
$text = preg_replace('/!\[([^\]]*)\]\([^\)]+\)/', '', $text);
// 移除粗体和斜体标记
$text = preg_replace('/\*\*([^\*]+)\*\*/', '$1', $text);
$text = preg_replace('/\*([^\*]+)\*/', '$1', $text);
$text = preg_replace('/__([^_]+)__/', '$1', $text);
$text = preg_replace('/_([^_]+)_/', '$1', $text);
// 移除删除线
$text = preg_replace('/~~([^~]+)~~/', '$1', $text);
// 移除引用标记
$text = preg_replace('/^>\s+/m', '', $text);
// 移除列表标记
$text = preg_replace('/^[\*\-\+]\s+/m', '', $text);
$text = preg_replace('/^\d+\.\s+/m', '', $text);
// 移除水平线
$text = preg_replace('/^[\-\*_]{3,}$/m', '', $text);
return $text;
}
}