Jump to content

Manual:解析器函数

本頁使用了標題或全文手工轉換
From mediawiki.org
This page is a translated version of the page Manual:Parser functions and the translation is 100% complete.
魔术字

MediaWiki extensions

解析器函数(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
這個magicWords數組不是可選的。如果省略,解析器函數將無法工作;{{#example: hello | hi}}將被渲染,就像沒有安裝擴展一樣。 如果只初始化语言特定的数组而不是magicWords数组本身,其它扩展中的翻译泄漏过来时,会导致本地化错误。 您可以在PHP中行内关联魔术词,而不是通过i18n文件。 这在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;
}

参阅

通用和相关指导:

代码:

范例: