Manual:解析器函数

解析器函数(Parser function),在MediaWiki 1.7版中加入,是一种与解析器紧密集成的扩展。 “解析器函数”一詞不应与Extension:ParserFunctions混淆,后者是简单解析器函数的一個集合。 (请参阅Help:Extension:解析器函数。)
描述
虽然标签扩展需要获取未处理的文本并将HTML返回给浏览器,但解析器函数可以与页面中的其他wiki元素“交互”。例如,解析器函数的输出可以用作模板参数或用于构造链接。
解析器函数的典型语法是:
{{ #functionname: param1 | param2 | param3 }}
有关更多信息,请参阅Parser::setFunctionHook ( $id, $callback, $flags = 0 )的文档。
此文档声明:
- 回調函數應具有以下形式:
function myParserFunction( $parser, $arg1, $arg2, $arg3 ) { ... }
- 或使用
function myParserFunction( $parser, $frame, $args ) { ... }
调用的第一种变体会把所有参數传递成纯文本。
第二种变体把所有参數传递成一个PPNode的数组,第一个参數($args[0])是除外,它目前是文本,不过未来可能会有改动。
这些东西表示未展开的wikitext。
$frame参數可以用来依照需要來展开这些引數。
這通常用於條件處理,以便僅在使用像"if或switch"這類的解析器函數的true情況時求值。
帧对象还可以顺着文档树向上获取调用方的信息,并且有函数可以确认和管理调用的深度、生存期、和解析器函數的結果是否易失。
創建一個解析器函數比創建一個新的標記稍微複雜一些,因為函數名稱必須是一個魔術字、一個能支援別名和本地化的關鍵字。
简单例子
下面是一個創建解析器函數的扩展的示例。
各自將注册写进extension.json、程式碼写进src/ExampleExtensionHooks.php:
| 标准做法: | 使用HookHandler接口: |
|---|---|
| extension.json | |
{
"name": "ExampleExtension",
"author": "Me",
"version": "1.0.0",
"url": "https://www.mediawiki.org/wiki/Extension:ExampleExtension",
"descriptionmsg": "exampleextension-desc",
"license-name": "GPL-2.0-or-later",
"type": "parserhook",
"MessagesDirs": {
"ExampleExtension": [
"i18n"
]
},
"AutoloadClasses": {
"ExampleExtensionHooks": "src/ExampleExtensionHooks.php"
},
"ExtensionMessagesFiles": {
"ExampleExtensionMagic": "ExampleExtension.i18n.php"
},
"Hooks": {
"ParserFirstCallInit": "ExampleExtensionHooks::onParserFirstCallInit"
},
"manifest_version": 1
}
|
{
"name": "ExampleExtension",
"author": "Me",
"version": "1.0.0",
"url": "https://www.mediawiki.org/wiki/Extension:ExampleExtension",
"descriptionmsg": "exampleextension-desc",
"license-name": "GPL-2.0-or-later",
"type": "parserhook",
"MessagesDirs": {
"ExampleExtension": [
"i18n"
]
},
"AutoloadClasses": {
"ExampleExtensionHooks": "src/ExampleExtensionHooks.php"
},
"ExtensionMessagesFiles": {
"ExampleExtensionMagic": "ExampleExtension.i18n.php"
},
"Hooks": {
"ParserFirstCallInit": "onParserFirstCallInit"
},
"HookHandlers": {
"ExampleExtensionHooks": {
"class": "MediaWiki\\Extension\\ExampleExtension\\Hooks"
}
},
"manifest_version": 1
}
|
| ExampleExtensionHooks.php | |
<?php
class ExampleExtensionHooks {
// 使用解析器註冊任何渲染回調
public static function onParserFirstCallInit( Parser $parser ) {
// 創建一個函數鉤子,將魔術詞<code>example</code>與renderExample()關聯
$parser->setFunctionHook( 'example', [ self::class, 'renderExample' ] );
}
// 渲染{{#example:}}的輸出。
public static function renderExample( Parser $parser, $param1 = '', $param2 = '', $param3 = '' ) {
// 輸入參數是展開了模板的wikitext。
// 輸出也應該是wikitext。
$output = "param1 is $param1 and param2 is $param2 and param3 is $param3";
return $output;
}
}
|
<?php
class ExampleExtensionHooks implements ParserFirstCallInitHook {
// 使用解析器註冊任何渲染回調
public function onParserFirstCallInit( $parser ) {
// Create a function hook associating the example magic word with renderExample()
$parser->setFunctionHook( 'example', [ $this, 'renderExample' ] );
}
// 渲染{{#example:}}的輸出。
public function renderExample( $parser, $param1 = '', $param2 = '', $param3 = '' ) {
// 輸入參數是展開了模板的wikitext。
// 輸出也應該是wikitext。
$output = "param1 is $param1 and param2 is $param2 and param3 is $param3";
return $output;
}
}
|
擴展目錄(不在src/子目錄中)的另一個文件ExampleExtension.i18n.php應該包含:
<?php
/**
* @license GPL-2.0-or-later
* @author 你的名字 (YourUserName)
*/
$magicWords = [];
/** English
* @author 你的名字 (YourUserName)
*/
$magicWords['en'] = [
'example' => [ 0, 'example' ],
];
啟用此擴展後,
- {{#example: hello | hi | hey}}
会产生:
- param1 is hello and param2 is hi and param3 is hey
LocalSettings.php中定义钩子时很有用。
MediaWiki\MediaWikiServices::getInstance()->getContentLanguage()->mMagicExtensions['wikicodeToHtml'] = ['MAG_CUSTOM', 'custom'];
在LocalSettings.php内
魔术字和它们处理的解析器函数可以全部在LocalSettings.php中定义。
$wgHooks['ParserFirstCallInit'][] = function ( Parser $parser )
{
MediaWiki\MediaWikiServices::getInstance()->getContentLanguage()->mMagicExtensions['wikicodeToHtml'] = ['wikicodeToHtml', 'wikicodeToHtml'];
$parser->setFunctionHook( 'wikicodeToHtml', 'wikicodeToHtml' );
};
function wikicodeToHtml( Parser $parser, $code = '' )
{
$title = $parser->getTitle();
$options = $parser->Options();
$options->enableLimitReport(false);
$parser = $parser->getFreshParser();
return [$parser->parse($code, $title, $options)->getText(), 'isHTML' => true];
}
更长的函数
對於更長的函數,您可能希望將鉤子函數拆分為_body.php或.hooks.php文件,並使它們成為類的靜態函數。然後你可以用$wgAutoloadClasses加載類並在鉤子中調用靜態函數;例如:
在你的extension.json文件中写上:
"Hooks": {
"ParserFirstCallInit": "ExampleExtensionHooks::onParserFirstCallInit"
},
"AutoloadClasses": {
"ExampleExtensionHooks": "src/ExampleExtensionHooks.php"
}
- 請參閱:編寫事件處理程序以了解其他写法。
然后在你的src/ExampleExtensionHooks.php文件中写上:
class ExampleExtensionHooks {
public static function onParserFirstCallInit( Parser $parser ) {
$parser->setFunctionHook( 'example', [ self::class, 'renderExample' ] );
}
}
解析器接口
控制輸出的解析
要使解析器函數返回的wikitext被完全解析(包括展開模板),請在返回時將noparse選項設置為false:
return [ $output, 'noparse' => false ];
似乎在1.12版本左右,noparse的默認值從false變為true,至少在某些情況下如此。
相反地,要使您的解析器函數返回未解析的HTML,而不是返回wikitext,請使用:
return [ $output, 'noparse' => true, 'isHTML' => true ];
命名
默认情况下,MediaWiki会在每个解析器函数的名称中添加一个井号(#)。
要抑制这种添加(并获得没有#前缀的解析器函数),请在setFunctionHook的可选参数flags中包含SFH_NO_HASH常量,如下文所述。
選擇沒有井號前綴的名稱時,要注意將來再也不可能刪除名稱以該函數名稱開頭後跟冒號的頁面。特別是,避免使用和命名空間名稱相同的函數名稱。在啟用跨wiki嵌入[1]的情況下,還要避免使用和跨wiki前綴相同的函數名稱。
setFunctionHook钩子
有關解析器接口的更多詳細信息,請參閱includes/Parser.php中setFunctionHook的文檔。 以下抄录了一些(可能过时的)注释:
function setFunctionHook( $id, $callback, $flags = 0 )
参数:
- string $id – 魔术字ID
- mixed $callback – 要使用的回調函數(和對象)
- integer $flags – 可选。允许的值:
SFH_NO_HASH(1) – 如果你不使用#调用函数时要用的常量SFH_OBJECT_ARGS(2) – 如果你要传递一个PPFrame对象和一个由实参组成的数组,而不是传递一系列函数实参,即上文所述- 默认为0(无旗标)。
返回值:此名稱原先对应的舊回調函數(如果有)
創建一個函數,例如{{#sum:1|2|3}}。回調函數應具有以下形式:
function myParserFunction( $parser, $arg1, $arg2, $arg3 ) { ... }
回調可以返回函數的文本結果,也可以返回一个數組,文本结果作为第0个元素,旗标作为其他元素。旗标的名稱在鍵中指定。有效的旗标为:
| 名称 | 类型 | 默认值 | 描述 |
|---|---|---|---|
| found | 布尔型 | true
|
如果返回的文本有效,模板处理必须停止,则为true。
|
| text | ? | ? | 要从函数返回的文本。 如果指定了isChildObj或isLocalObj,则应该为一个DOM节点。 |
| noparse | 布尔型 | true
|
如果文本不应被预处理成DOM树,则为true,例如不应剥除不安全的HTML标签
|
| isHTML | 布尔型 | ? | 如果返回的文本是HTML且必须免除wikitext转换,则为true。 但是……参见讨论
|
| nowiki | 布尔型 | 通常是false
|
如果應該轉義返回值(文本)中的Wiki標記,则为true。
|
| isChildObj | 布尔型 | ? | 如果文本是一个需要在子帧中展开的DOM节点,则为true。
|
| isLocalObj | 布尔型 | ? | 如果文本是一个需要在当前帧中展开的DOM节点,则为true。 默认值取决于其它的旗标值和结果。
|
| preprocessFlags | ? | false
|
可选的PPFrame旗标,解析返回值文本时使用。 仅在noparse为false时应用。
|
| title | ? | false
|
文本来自的Title对象。 |
| forceRawInterwiki | 布尔型 | ? | 如果跨wiki嵌入必须以原始模式完成且不渲染,则为true。
|
高开销的解析器函数
一些解析器函数代表wiki资源的大量使用,需要标记为“高开销”。 任何页面上能使用的高开销解析器函数数量由$wgExpensiveParserFunctionLimit设置限制。 “何为高开销”由函数本身决定,不过典型做法是,如果导致的延迟超过简单数据处理应有的时长,就需要掂量。 这包括数据库读写,同步地启动一个shell脚本,或操纵文件之类的事情。 另一方面,也不是所有这样的函数都一定要标记。 例如语义MediaWiki就只把其一部分数据库写入记作高开销。 这是因为在一些数据密集型的页面中,很容易超过一般高开销解析器函数的限制。 在这样的情况下,不标记高开销导致的明显更慢的性能则是与SMW功能之间的取舍。
如需标记你的解析器函数为高开销,在函数体的代码中使用$result = $parser->incrementExpensiveFunctionCount();。
如果达到或超过高开销函数限制,则返回值为false。
命名參數
解析器函數不像模板和標簽擴展那樣支持命名參數,但偽造它會偶爾有用。用戶通常習慣使用豎線(|)來分隔參數,因此能夠在解析器函數上下文中做到這一點也很好。以下是如何完成此操作的簡單示例:
function ExampleExtensionRenderParserFunction( &$parser ) {
// 假设用户像这样调用解析器函数:
// {{#myparserfunction: foo=bar | apple=orange | banana }}
$options = extractOptions( array_slice( func_get_args(), 1 ) );
// 于是你得到这样的一个数组:
// [foo] => 'bar'
// [apple] => 'orange'
// [banana] => true
// 继续编写你的代码……
}
/**
* 将值的形式为 [0] => "名称=值" 的数组转换成值的形式为 [名称] => 值 这样的关联数组
* 如果没有=,假定值为true,如同:[名称] => true
*
* @param array string $options
* @return array $results
*/
function extractOptions( array $options ) {
$results = [];
foreach ( $options as $option ) {
$pair = array_map( 'trim', explode( '=', $option, 2 ) );
if ( count( $pair ) === 2 ) {
$results[ $pair[0] ] = $pair[1];
}
if ( count( $pair ) === 1 ) {
$results[ $pair[0] ] = true;
}
}
return $results;
}
参阅
通用和相关指导:
- Manual:开发扩展,或关于扩展的更通用信息,见Manual:扩展和Extensions FAQ。
- Manual:标签扩展
- Manual:魔术字
代码:
- 手册:Parser.php
- Manual:Hooks/ParserFirstCallInit
- Parser function hooks – 由核心和扩展提供的解析器函数一览表(不完整)
- Parser Hooks PHP 库,为声明性解析器钩子提供面向对象的接口
- Manual:Extension data
范例:
- ParserFunctions擴展是一個广为人知的解析器函數集合。
- Help:Extension:解析器函数
- Category:解析器函数扩展