在 DokuWiki 中,语法插件的 getType()
方法返回值有以下几种类型:
container
- 容器类型,可以包含其他模式baseonly
- 只允许在基本模式中使用的类型formatting
- 文本格式化类型substition
- 简单的标记替换类型protected
- 内容按原样保留的类型disabled
- 无 wiki 标记但不保留空白的类型paragraphs
- 段落边界标记类型
这些类型在 DokuWiki 的解析器系统中用于确定语法插件如何与其他语法元素交互。
详细解释
在 DokuWiki 中,每个语法插件都必须继承 SyntaxPlugin
类并实现 getType()
方法。 SyntaxPlugin.php:18-29
getType()
方法需要返回 Parser.php 中定义的 $PARSER_MODES
中的一种模式类型。这些类型决定了插件在解析过程中的行为方式。
根据 Parser and Renderer 的 wiki 页面,这些模式类型分为几个类别:
- Container(容器):复杂的模式,可以包含其他模式,例如 listblock、table、quote、hr
- Base-only(仅基本):只允许在基本模式中使用的模式,例如 header
- Formatting(格式化):文本样式模式,例如 strong、emphasis、underline 等
- Substitution(替换):简单的标记替换模式,例如 internallink、media、externallink 等
- Protected(受保护):内容按原样保留,例如 preformatted、code、file
- Disabled(禁用):无 wiki 标记但不保留空白,例如 unformatted
- Paragraphs(段落):段落边界标记,例如 eol
这些类型在解析器系统中用于确定语法插件如何与其他语法元素交互,以及它们在文档结构中的位置。
在解析过程中,DokuWiki 会根据语法插件的类型来决定如何处理它们。例如,在 p_get_parsermodes()
函数中,会收集所有语法模式及其对象,然后按照正确的顺序对它们进行排序并添加到解析器中。 parserutils.php:555-587
当语法插件被加载时,它的 getType()
方法返回的值会被注册到 $PARSER_MODES
数组中,这样解析器就知道如何处理该插件。 parserutils.php:580
Notes
这个回答基于 DokuWiki 的代码库,特别是 SyntaxPlugin 类和解析器系统的实现。getType()
方法是每个语法插件必须实现的方法,它返回的值决定了插件在解析过程中的行为方式。
1. container
类型插件完整实现
容器类型的插件可以包含其他模式,例如表格、列表等。
<?php
/**
* 表格语法插件示例 - 容器类型
*
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
*/
// 必须是这个格式的类名: syntax_plugin_插件名称
class syntax_plugin_example_table extends \dokuwiki\Extension\SyntaxPlugin {
/**
* 返回插件类型
*/
public function getType() {
return 'container';
}
/**
* 返回段落类型
*/
public function getPType() {
return 'block';
}
/**
* 返回允许的模式类型
*/
public function getAllowedTypes() {
return ['formatting', 'substition', 'disabled', 'protected'];
}
/**
* 返回模式排序值
*/
public function getSort() {
return 50;
}
/**
* 将模式连接到词法分析器
*/
public function connectTo($mode) {
$this->Lexer->addEntryPattern('<table>(?=.*?</table>)', $mode, 'plugin_example_table');
}
public function postConnect() {
$this->Lexer->addExitPattern('</table>', 'plugin_example_table');
}
/**
* 处理匹配的数据
*/
public function handle($match, $state, $pos, \Doku_Handler $handler) {
switch ($state) {
case DOKU_LEXER_ENTER:
return [
'state' => $state,
'pos' => $pos
];
case DOKU_LEXER_UNMATCHED:
return [
'state' => $state,
'text' => $match,
'pos' => $pos
];
case DOKU_LEXER_EXIT:
return [
'state' => $state,
'pos' => $pos
];
}
return false;
}
/**
* 创建输出
*/
public function render($format, \Doku_Renderer $renderer, $data) {
if ($format == 'xhtml') {
switch ($data['state']) {
case DOKU_LEXER_ENTER:
$renderer->doc .= '<div class="example_table">';
break;
case DOKU_LEXER_UNMATCHED:
$renderer->doc .= $renderer->_xmlEntities($data['text']);
break;
case DOKU_LEXER_EXIT:
$renderer->doc .= '</div>';
break;
}
return true;
}
return false;
}
}
2. baseonly
类型插件完整实现
只允许在基本模式中使用的类型,例如标题。
<?php
/**
* 标题语法插件示例 - 仅基本类型
*
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
*/
class syntax_plugin_example_header extends \dokuwiki\Extension\SyntaxPlugin {
/**
* 返回插件类型
*/
public function getType() {
return 'baseonly';
}
/**
* 返回段落类型
*/
public function getPType() {
return 'block';
}
/**
* 返回允许的模式类型
*/
public function getAllowedTypes() {
return [];
}
/**
* 返回模式排序值
*/
public function getSort() {
return 10;
}
/**
* 将模式连接到词法分析器
*/
public function connectTo($mode) {
$this->Lexer->addSpecialPattern('={3,}[^\n]+={3,}', $mode, 'plugin_example_header');
}
/**
* 处理匹配的数据
*/
public function handle($match, $state, $pos, \Doku_Handler $handler) {
// 去除等号并修剪空白
$title = trim(preg_replace('/^={3,}(.+)={3,}$/', '$1', $match));
return [
'title' => $title,
'level' => 3, // 假设这是一个 h3 标题
'pos' => $pos
];
}
/**
* 创建输出
*/
public function render($format, \Doku_Renderer $renderer, $data) {
if ($format == 'xhtml') {
$renderer->header($data['title'], $data['level'], $data['pos']);
return true;
}
return false;
}
}
3. formatting
类型插件完整实现
文本格式化类型的插件,用于文本样式,如高亮等。
<?php
/**
* 文本高亮语法插件示例 - 格式化类型
*
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
*/
class syntax_plugin_example_highlight extends \dokuwiki\Extension\SyntaxPlugin {
/**
* 返回插件类型
*/
public function getType() {
return 'formatting';
}
/**
* 返回段落类型
*/
public function getPType() {
return 'normal';
}
/**
* 返回允许的模式类型
*/
public function getAllowedTypes() {
return ['formatting', 'substition'];
}
/**
* 返回模式排序值
*/
public function getSort() {
return 150;
}
/**
* 将模式连接到词法分析器
*/
public function connectTo($mode) {
$this->Lexer->addEntryPattern('%%(?=.*?%%)', $mode, 'plugin_example_highlight');
}
public function postConnect() {
$this->Lexer->addExitPattern('%%', 'plugin_example_highlight');
}
/**
* 处理匹配的数据
*/
public function handle($match, $state, $pos, \Doku_Handler $handler) {
switch ($state) {
case DOKU_LEXER_ENTER:
return [
'state' => $state,
'pos' => $pos
];
case DOKU_LEXER_UNMATCHED:
return [
'state' => $state,
'text' => $match,
'pos' => $pos
];
case DOKU_LEXER_EXIT:
return [
'state' => $state,
'pos' => $pos
];
}
return false;
}
/**
* 创建输出
*/
public function render($format, \Doku_Renderer $renderer, $data) {
if ($format == 'xhtml') {
switch ($data['state']) {
case DOKU_LEXER_ENTER:
$renderer->doc .= '<span class="highlight">';
break;
case DOKU_LEXER_UNMATCHED:
$renderer->doc .= $renderer->_xmlEntities($data['text']);
break;
case DOKU_LEXER_EXIT:
$renderer->doc .= '</span>';
break;
}
return true;
}
return false;
}
}
4. substition
类型插件完整实现
简单的标记替换类型,例如特殊链接。
<?php
/**
* 特殊链接语法插件示例 - 替换类型
*
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
*/
class syntax_plugin_example_speciallink extends \dokuwiki\Extension\SyntaxPlugin {
/**
* 返回插件类型
*/
public function getType() {
return 'substition';
}
/**
* 返回段落类型
*/
public function getPType() {
return 'normal';
}
/**
* 返回允许的模式类型
*/
public function getAllowedTypes() {
return [];
}
/**
* 返回模式排序值
*/
public function getSort() {
return 300;
}
/**
* 将模式连接到词法分析器
*/
public function connectTo($mode) {
$this->Lexer->addSpecialPattern('\[\[special>.+?\]\]', $mode, 'plugin_example_speciallink');
}
/**
* 处理匹配的数据
*/
public function handle($match, $state, $pos, \Doku_Handler $handler) {
// 提取链接文本
$link = trim(substr($match, 11, -2)); // 去除 [[special> 和 ]]
// 分割链接和标题(如果有)
$parts = explode('|', $link, 2);
$url = trim($parts[0]);
$title = isset($parts[1]) ? trim($parts[1]) : $url;
return [
'url' => $url,
'title' => $title,
'pos' => $pos
];
}
/**
* 创建输出
*/
public function render($format, \Doku_Renderer $renderer, $data) {
if ($format == 'xhtml') {
$renderer->doc .= '<a href="' . $renderer->_xmlEntities($data['url']) . '" class="speciallink">' .
$renderer->_xmlEntities($data['title']) . '</a>';
return true;
}
return false;
}
}
5. protected
类型插件完整实现
内容按原样保留的类型,例如代码块。
<?php
/**
* 代码块语法插件示例 - 受保护类型
*
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
*/
class syntax_plugin_example_codeblock extends \dokuwiki\Extension\SyntaxPlugin {
/**
* 返回插件类型
*/
public function getType() {
return 'protected';
}
/**
* 返回段落类型
*/
public function getPType() {
return 'block';
}
/**
* 返回允许的模式类型
*/
public function getAllowedTypes() {
return [];
}
/**
* 返回模式排序值
*/
public function getSort() {
return 200;
}
/**
* 将模式连接到词法分析器
*/
public function connectTo($mode) {
$this->Lexer->addEntryPattern('<code\b.*?>(?=.*?</code>)', $mode, 'plugin_example_codeblock');
}
public function postConnect() {
$this->Lexer->addExitPattern('</code>', 'plugin_example_codeblock');
}
/**
* 处理匹配的数据
*/
public function handle($match, $state, $pos, \Doku_Handler $handler) {
switch ($state) {
case DOKU_LEXER_ENTER:
// 提取语言(如果有)
$language = '';
if (preg_match('/<code\b(.*?)>/', $match, $matches)) {
if (!empty($matches[1])) {
$language = trim($matches[1]);
}
}
return [
'state' => $state,
'language' => $language,
'pos' => $pos
];
case DOKU_LEXER_UNMATCHED:
return [
'state' => $state,
'text' => $match,
'pos' => $pos
];
case DOKU_LEXER_EXIT:
return [
'state' => $state,
'pos' => $pos
];
}
return false;
}
/**
* 创建输出
*/
public function render($format, \Doku_Renderer $renderer, $data) {
if ($format == 'xhtml') {
switch ($data['state']) {
case DOKU_LEXER_ENTER:
$class = !empty($data['language']) ? ' class="' . $renderer->_xmlEntities($data['language']) . '"' : '';
$renderer->doc .= '<pre' . $class . '><code>';
break;
case DOKU_LEXER_UNMATCHED:
$renderer->doc .= $renderer->_xmlEntities($data['text']);
break;
case DOKU_LEXER_EXIT:
$renderer->doc .= '</code></pre>';
break;
}
return true;
}
return false;
}
}
6. disabled
类型插件完整实现
无 wiki 标记但不保留空白的类型。
<?php
/**
* 无格式文本语法插件示例 - 禁用类型
*
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
*/
class syntax_plugin_example_plaintext extends \dokuwiki\Extension\SyntaxPlugin {
/**
* 返回插件类型
*/
public function getType() {
return 'disabled';
}
/**
* 返回段落类型
*/
public function getPType() {
return 'normal';
}
/**
* 返回允许的模式类型
*/
public function getAllowedTypes() {
return [];
}
/**
* 返回模式排序值
*/
public function getSort() {
return 250;
}
/**
* 将模式连接到词法分析器
*/
public function connectTo($mode) {
$this->Lexer->addEntryPattern('<nowiki>(?=.*?</nowiki>)', $mode, 'plugin_example_plaintext');
}
public function postConnect() {
$this->Lexer->addExitPattern('</nowiki>', 'plugin_example_plaintext');
}
/**
* 处理匹配的数据
*/
public function handle($match, $state, $pos, \Doku_Handler $handler) {
switch ($state) {
case DOKU_LEXER_ENTER:
return [
'state' => $state,
'pos' => $pos
];
case DOKU_LEXER_UNMATCHED:
return [
'state' => $state,
'text' => $match,
'pos' => $pos
];
case DOKU_LEXER_EXIT:
return [
'state' => $state,
'pos' => $pos
];
}
return false;
}
/**
* 创建输出
*/
public function render($format, \Doku_Renderer $renderer, $data) {
if ($format == 'xhtml') {
switch ($data['state']) {
case DOKU_LEXER_ENTER:
$renderer->doc .= '<span class="nowiki">';
break;
case DOKU_LEXER_UNMATCHED:
// 在禁用模式中,我们保留文本但不保留空白格式
$text = $data['text'];
// 将多个空白字符替换为单个空格
$text = preg_replace('/\s+/', ' ', $text);
$renderer->doc .= $renderer->_xmlEntities($text);
break;
case DOKU_LEXER_EXIT:
$renderer->doc .= '</span>';
break;
}
return true;
}
return false;
}
}
7. paragraphs
类型插件完整实现
段落边界标记类型。
<?php
/**
* 段落处理语法插件示例 - 段落类型
*
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
*/
class syntax_plugin_example_paragraph extends \dokuwiki\Extension\SyntaxPlugin {
/**
* 返回插件类型
*/
public function getType() {
return 'paragraphs';
}
/**
* 返回段落类型
*/
public function getPType() {
return 'block';
}
/**
* 返回允许的模式类型
*/
public function getAllowedTypes() {
return [];
}
/**
* 返回模式排序值
*/
public function getSort() {
return 400;
}
/**
* 将模式连接到词法分析器
*/
public function connectTo($mode) {
$this->Lexer->addSpecialPattern('\n{2,}', $mode, 'plugin_example_paragraph');
}
/**
* 处理匹配的数据
*/
public function handle($match, $state, $pos, \Doku_Handler $handler) {
return [
'count' => strlen($match), // 计算连续换行符的数量
'pos' => $pos
];
}
/**
* 创建输出
*/
public function render($format, \Doku_Renderer $renderer, $data) {
if ($format == 'xhtml') {
// 关闭当前段落并开始一个新段落
$renderer->p_close();
// 如果有多个换行符,可以添加额外的垂直空间
if ($data['count'] > 2) {
$renderer->doc .= '<div class="extra_spacing" style="margin-top: ' .
(($data['count'] - 2) * 0.5) . 'em;"></div>';
}
$renderer->p_open();
return true;
}
return false;
}
}
这些完整的实现示例展示了如何创建不同类型的 DokuWiki 语法插件。每种类型的插件都有其特定的行为和用途,通过 getType()
方法返回的值来定义。
disabled
类型的插件特别适用于需要显示原始文本但不需要保留空白格式的情况,例如 <nowiki>
标签。这种类型的插件会禁用所有 wiki 标记的解析,但不会像 protected
类型那样保留空白和换行。
Notes
这些示例代码展示了 DokuWiki 语法插件系统的灵活性和可扩展性。每种类型的插件都有其特定的用途和行为特征,开发者可以根据需要选择适当的类型。
在 DokuWiki 中,语法插件的类型(通过 getType()
返回)决定了它在解析过程中的行为方式,而 getAllowedTypes()
方法则决定了哪些其他语法元素可以在该插件内部使用。这两个方法共同构成了 DokuWiki 强大而灵活的语法系统。
在 DokuWiki 的语法插件系统中,getAllowedTypes()
方法用于定义该插件内部可以包含哪些其他类型的语法模式。
getAllowedTypes()
方法的作用
getAllowedTypes()
方法返回一个数组,包含该插件允许在其内部使用的其他语法模式类型。这些类型必须是 $PARSER_MODES
中定义的类型之一,如 'container'、'formatting'、'substition' 等。 SyntaxPlugin.php:31-39
例如,当一个插件返回 ['formatting', 'substition']
时,这意味着该插件内部可以包含格式化类型(如粗体、斜体)和替换类型(如链接、媒体)的语法元素,但不能包含其他类型(如容器类型)的元素。
返回多个值的意义
返回多个值(数组)意味着该插件允许在其内部使用多种不同类型的语法模式。这对于创建复杂的、可嵌套的语法结构非常重要。
例如,在您之前看到的容器类型插件示例中:
public function getAllowedTypes() {
return ['formatting', 'substition', 'disabled', 'protected'];
}
这表示该表格插件允许在其内部使用:
- 格式化类型(如粗体、斜体)
- 替换类型(如链接、媒体)
- 禁用类型(如无格式文本)
- 受保护类型(如代码块)
但它不允许在其内部使用容器类型(如嵌套表格)或仅基本类型(如标题)。
实际工作原理
当 DokuWiki 解析文档时,它会根据插件的 getAllowedTypes()
方法返回的类型来决定哪些语法可以在该插件内部使用。这是通过 accepts()
方法实现的: SyntaxPlugin.php:111-129
这段代码展示了 DokuWiki 如何使用 getAllowedTypes()
方法的返回值来构建允许的模式列表。它首先获取允许的模式类型,然后从全局 $PARSER_MODES
数组中获取这些类型对应的所有具体模式。
例如,如果 getAllowedTypes()
返回 ['formatting']
,那么 $this->allowedModes
将包含所有格式化类型的模式,如 'strong'、'emphasis'、'underline' 等。
与 getType()
的区别
需要注意的是,getAllowedTypes()
与 getType()
是两个不同的方法:
getType()
定义了插件本身的类型(如 'container'、'formatting' 等)getAllowedTypes()
定义了插件内部允许使用的其他语法类型
这两个方法共同决定了插件在 DokuWiki 语法系统中的行为和与其他语法元素的交互方式。
Notes
getAllowedTypes()
方法是 DokuWiki 语法插件系统中的一个重要部分,它允许插件定义自己的"语法作用域",即哪些其他语法元素可以在该插件内部使用。这对于创建复杂的、可嵌套的语法结构非常重要,同时也确保了语法的一致性和可预测性。
默认情况下,SyntaxPlugin
类的 getAllowedTypes()
方法返回空数组,表示不允许任何其他语法模式在其内部使用。插件开发者需要根据自己的需求覆盖这个方法。