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; } }