1. 自动嵌套语法处理(使用getAllowedTypes()和UNMATCHED状态)
这种方法通过声明允许的语法类型,让DokuWiki自动处理嵌套语法。
class syntax_plugin_example extends \dokuwiki\Extension\SyntaxPlugin {
public function getAllowedTypes() {
return ['formatting', 'substition', 'disabled', 'protected'];
}
public function handle($match, $state, $pos, Doku_Handler $handler) {
switch ($state) {
case DOKU_LEXER_UNMATCHED:
// 直接返回未匹配内容,系统自动处理嵌套语法
return ['state' => $state, 'text' => $match];
}
}
}
2. 手动解析和渲染(使用p_get_instructions和nest)
这种方法手动创建解析上下文,适用于需要特殊处理的情况。
public function render($mode, Doku_Renderer $renderer, $data) {
if ($mode !== 'xhtml') return false;
// 手动解析内容中的嵌套语法
$instructions = p_get_instructions($data['content']);
$renderer->nest($instructions);
return true;
}
3. 直接插件处理(调用其他插件的handle()方法)
这种方法直接使用其他插件的功能,避免重复实现。
public function handle($match, $state, $pos, Doku_Handler $handler) {
// 直接使用另一个插件处理内容
$plugin = plugin_load('syntax', 'markdown');
if ($plugin) {
return $plugin->handle($match, $state, $pos, $handler);
}
return false;
}
4. 直接处理器调用(使用addCall)
这种方法直接向处理器添加指令,提供底层控制。
public function handle($match, $state, $pos, Doku_Handler $handler) {
// 直接添加处理器指令
$handler->addCall('table_open', [], $pos);
$handler->addCall('tablerow_open', [], $pos);
$handler->addCall('tablecell_open', [1, 'left'], $pos);
$handler->addCall('cdata', ['Cell content'], $pos);
$handler->addCall('tablecell_close', [], $pos);
$handler->addCall('tablerow_close', [], $pos);
$handler->addCall('table_close', [], $pos);
return true;
}
5. 使用CallWriter类
这种方法创建自定义的CallWriter来处理特定的语法结构。
class MyCustomCallWriter implements CallWriterInterface {
protected $calls = [];
public function writeCall($call) {
// 自定义处理逻辑
$this->calls[] = $call;
}
public function finalise() {
// 完成处理
}
}
public function handle($match, $state, $pos, Doku_Handler $handler) {
// 设置自定义CallWriter
$customWriter = new MyCustomCallWriter();
$handler->setCallWriter($customWriter);
return true;
}
6. 使用Nest类处理嵌套内容
这种方法使用专门的Nest类来处理嵌套内容。
public function handle($match, $state, $pos, Doku_Handler $handler) {
if ($state == DOKU_LEXER_ENTER) {
// 创建嵌套上下文
$handler->setCallWriter(new \dokuwiki\Parsing\Handler\Nest(
$handler->getCallWriter(),
'my_plugin_close'
));
} else if ($state == DOKU_LEXER_EXIT) {
// 处理嵌套内容
$writer = $handler->getCallWriter();
$handler->setCallWriter($writer->process());
}
return true;
}
7. 使用事件系统
这种方法使用DokuWiki的事件系统来修改解析过程。
class action_plugin_example extends \dokuwiki\Extension\ActionPlugin {
public function register(Doku_Event_Handler $controller) {
$controller->register_hook('PARSER_HANDLER_DONE', 'AFTER', $this, 'handleParserDone');
}
public function handleParserDone(Doku_Event $event, $param) {
$handler = $event->data;
// 修改最终的指令列表
// 例如添加额外的指令或修改现有指令
}
}
8. 使用自定义渲染器
这种方法创建自定义的渲染器来处理特定的输出格式。
class renderer_plugin_example extends Doku_Renderer {
public function getFormat() {
return 'example';
}
// 重写渲染方法
public function header($text, $level, $pos) {
// 自定义标题渲染
$this->doc .= "<h$level class='custom'>$text</h$level>";
}
// 其他渲染方法...
}
9. 组合使用多种方法
这种方法结合使用多种技术来处理复杂的语法需求。
class syntax_plugin_complex extends \dokuwiki\Extension\SyntaxPlugin {
public function getAllowedTypes() {
return ['formatting', 'substition'];
}
public function handle($match, $state, $pos, Doku_Handler $handler) {
switch ($state) {
case DOKU_LEXER_ENTER:
// 使用Nest处理嵌套内容
$handler->setCallWriter(new \dokuwiki\Parsing\Handler\Nest(
$handler->getCallWriter(), 'complex_close'
));
return ['state' => $state];
case DOKU_LEXER_MATCHED:
// 对于匹配内容,直接添加处理器指令
$handler->addCall('strong_open', [], $pos);
$handler->addCall('cdata', [$match], $pos);
$handler->addCall('strong_close', [], $pos);
return ['state' => $state];
case DOKU_LEXER_UNMATCHED:
// 对于未匹配内容,让系统自动处理嵌套语法
return ['state' => $state, 'text' => $match];
case DOKU_LEXER_EXIT:
// 处理嵌套内容
$writer = $handler->getCallWriter();
$handler->setCallWriter($writer->process());
return ['state' => $state];
}
}
public function render($mode, Doku_Renderer $renderer, $data) {
// 渲染逻辑...
}
}
10. 使用Block处理器
这种方法使用Block处理器来处理块级元素,确保正确的嵌套。
public function handle($match, $state, $pos, Doku_Handler $handler) {
// 添加块级元素
$handler->addCall('p_open', [], $pos);
$handler->addCall('cdata', [$match], $pos);
$handler->addCall('p_close', [], $pos);
// Block处理器会自动重写这些调用以确保正确的嵌套
return true;
}
总结比较
方法 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
1. 自动嵌套语法处理 | 简单,由DokuWiki自动处理 | 控制有限,只适用于UNMATCHED内容 | 容器语法,需要支持多种内嵌语法 |
2. 手动解析和渲染 | 完全控制解析过程,适用于任何状态 | 需要手动创建解析上下文,可能效率较低 | 需要特殊处理的内容,如表格单元格 |
3. 直接插件处理 | 高效,精确控制,避免重复实现 | 紧密耦合,依赖特定插件 | 扩展或使用其他插件的功能 |
4. 直接处理器调用 | 底层控制,直接生成处理器指令 | 需要手动管理所有指令,容易出错 | 需要精确控制输出结构的场景 |
5. 使用CallWriter类 | 高度自定义的处理逻辑 | 实现复杂,需要了解CallWriter机制 | 需要特殊处理指令流的场景 |
6. 使用Nest类 | 自动处理嵌套内容的开始和结束 | 仅适用于嵌套内容 | 处理复杂的嵌套结构 |
7. 使用事件系统 | 不修改原始插件即可修改解析结果 | 间接控制,可能与其他插件冲突 | 扩展现有插件的功能 |
8. 使用自定义渲染器 | 完全控制输出格式 | 需要实现所有渲染方法 | 创建新的输出格式 |
9. 组合使用多种方法 | 灵活,可以处理复杂需求 | 复杂,需要深入了解多种机制 | 复杂的语法插件 |
10. 使用Block处理器 | 确保正确的文档结构和嵌套 | 控制有限,依赖Block处理器的行为 | 处理块级元素 |
这些方法可以根据具体需求单独使用或组合使用。选择合适的方法取决于您的插件需要处理的语法复杂性、性能要求以及与其他插件的交互需求。
Notes:
- 示例代码是基于DokuWiki的语法插件系统编写的,展示了每种方法的基本用法
- 实际应用中可能需要更复杂的逻辑和错误处理
- DokuWiki的解析系统是基于状态机的,理解DOKU_LEXER_ENTER、DOKU_LEXER_MATCHED、DOKU_LEXER_UNMATCHED和DOKU_LEXER_EXIT等状态对于开发插件非常重要