核心概念
在 DokuWiki 的解析系统中,重新解析是指当插件或特殊语法捕获了一段文本后,需要手动创建新的解析器实例来重新处理这段文本中的 wiki 语法。
为什么需要重新解析
解析器的单次遍历设计
DokuWiki 的词法分析器采用单次遍历设计。在主解析循环中: Lexer.php:142-154
一旦文本被某个模式匹配和消费,就不会再回到解析队列中重新处理。
DOKU_LEXER_UNMATCHED 状态的处理
当文本在当前解析上下文中没有被任何模式匹配时,会被标记为 DOKU_LEXER_UNMATCHED
状态,这些文本会直接转换为 cdata
指令: handler.php:314-316
在 XHTML 渲染器中,cdata()
方法只是简单地转义 HTML 实体,不进行任何语法解析:
重新解析的实现方法
核心函数组合
重新解析主要通过 p_render()
+ p_get_instructions()
组合实现:
这个函数创建全新的解析器和处理器实例,重新应用所有解析模式来处理文本。
插件中的使用
DokuWiki 提供了 render_text()
方法供插件使用:
使用场景
1. 语法插件中的嵌套渲染
当插件捕获包含 wiki 语法的文本块时,需要重新解析以支持嵌套语法。
2. 命令行工具
在命令行渲染工具中处理任意文本:
3. 页面预览功能
处理用户输入的文本片段:
容器类插件的特殊情况
即使是容器类插件也需要手动解析。容器类插件在解析器模式分类中属于特殊类型:
但它们在处理 DOKU_LEXER_UNMATCHED
文本时,仍然遵循相同的机制。
getAllowedTypes() 的作用
getAllowedTypes()
方法定义插件可以接受的嵌套模式类型: SyntaxPlugin.php:40-43
这个方法在 accepts()
中被使用来构建允许的模式列表: SyntaxPlugin.php:111-130
但这只是声明性配置,告诉解析器插件的能力,实际的内容处理仍需手动实现。
格式化语法的处理
对于嵌套格式化标签(如粗体、斜体等),DokuWiki 使用 nestingTag()
方法统一处理: handler.php:251-264
这些格式化语法都通过相同的模式处理: handler.php:411-415
设计理念
这种设计让插件开发者可以灵活控制内容处理:
- 代码块插件需要保持原始文本,不解析任何语法
- 容器类插件可能需要支持完整的 wiki 语法解析
DokuWiki 让插件自己决定是否需要重新解析内容,确保了解析的可控性和性能优化。
Notes
重新解析机制是 DokuWiki 支持复杂嵌套语法的关键,但会带来性能开销。这就是为什么 DokuWiki 在正常页面渲染中使用缓存机制,并让插件开发者明确选择是否需要重新解析的原因。
1. 容器类插件示例
容器类插件需要在容器内支持标准的 wiki 语法。从代码中可以看到,插件可以使用 PluginTrait
提供的 render_text()
方法: PluginTrait.php:274-277
一个典型的容器类插件实现示例:
public function render($format, Doku_Renderer $renderer, $data) {
if ($format == 'xhtml') {
$renderer->doc .= '<div class="wrap-container">';
// 重新解析容器内的 wiki 语法
$html = $this->render_text($data['content'], 'xhtml');
$renderer->doc .= $html;
$renderer->doc .= '</div>';
return true;
}
return false;
}
2. 包含类插件示例
包含类插件需要渲染包含的页面内容。DokuWiki 核心已经提供了这种功能的实现,可以在模板函数中看到:
包含类插件的实现示例:
public function render($format, Doku_Renderer $renderer, $data) {
if ($format == 'xhtml') {
// 使用 p_wiki_xhtml 渲染包含的页面
$html = p_wiki_xhtml($data['pageid'], '', false);
$renderer->doc .= $html;
return true;
}
return false;
}
3. 模板类插件示例
模板类插件需要处理模板内容中的 wiki 语法。在页面预览功能中可以看到类似的实现: PageView.php:56
模板类插件的实现示例:
public function render($format, Doku_Renderer $renderer, $data) {
if ($format == 'xhtml') {
// 处理模板变量替换
$template_content = str_replace('{{title}}', $data['title'], $data['template']);
$template_content = str_replace('{{content}}', $data['content'], $template_content);
// 重新解析模板中的 wiki 语法
$instructions = p_get_instructions($template_content);
$html = p_render('xhtml', $instructions, $info);
$renderer->doc .= $html;
return true;
}
return false;
}
核心解析函数
所有这些插件都依赖于 DokuWiki 的核心解析函数: parserutils.php:219-234
这个函数创建新的解析器实例,重新应用所有解析模式来处理文本。
Notes
这些示例展示了不同类型插件如何使用重新解析机制。容器类插件使用 render_text()
方法,包含类插件使用 p_wiki_xhtml()
函数,而模板类插件直接使用 p_render()
+ p_get_instructions()
组合。所有这些方法的核心都是重新创建解析器来处理嵌套的 wiki 语法。