Manual:パーサー関数

This page is a translated version of the page Manual:Parser functions and the translation is 50% complete.
MediaWiki extensions

MediaWiki 1.7で追加されたパーサー関数は、パーサーと密接に連動する拡張機能の一種です。 「パーサー関数」という言葉を、単純なパーサー関数の集まりである Extension:ParserFunctions と混同しないようにしてください。 (これについては Help:Extension:ParserFunctions を参照してください。)

説明

タグ拡張機能は未処理のテキストを取り込んでブラウザに HTML を返すのに対して、パーサー関数はページ上のその他の要素と「相互作用」ができます。たとえばパーサ関数の出力はテンプレート変数として利用したりリンクの作成に使うことができます。

パーサー関数の典型的な構文は以下の通りです:

{{ #functionname: param1 | param2 | param3 }}

詳細について、Parser::setFunctionHook ( $id, $callback, $flags = 0 )の説明はthe documentationを参照してください。その説明文書は次の内容を扱います。

コールバック関数の構文は次のとおりです。
function myParserFunction( $parser, $arg1, $arg2, $arg3 ) { ... }
あるいは SFH_OBJECT_ARGS:
function myParserFunction( $parser, $frame, $args ) { ... }

The first variant of the call passes all arguments as plain text. The second passes all arguments as an array of PPNode s, except for the first ($args[0]), which is currently text, though this may change in the future. These represent the unexpanded wikitext. The $frame parameter can be used to expand these arguments as needed. This is commonly used for conditional processing so that only the true case is evaluated with an if- or switch-like parser function. The frame object can also climb up the document tree to get information about the caller and has functions to determine and manage call depth, time-to-live, and whether the result of the parser function is volatile.

パーサー関数の作成は、タグフックを作成する場合よりも多少込み入っています。これは、関数名がマジックワードでなければならないためです; マジックワードは、別名および多言語対応をサポートする一種のキーワードです。

単純な例

パーサー関数を生成する拡張機能の例を以下に示します。

登録は extension.json に、コードは src/ExampleExtensionHooks.php にそれぞれ入ります:

{
	"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
}
<?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 = '' ) {

      // 入力パラメーターがウィキテキストでテンプレートを展開します。
      // 出力もウィキテキストになります。
      $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 Your Name (YourUserName)
 */

$magicWords = [];

/** English
 * @author Your Name (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}} は拡張機能がインストールされていない状態として表示されます。 If only the language-specific array is initialized and not the magicWords array itself, this can cause localization errors as translations from other extensions leak into yours. マジックワードの関連付けは、国際化ファイルではなく、PHP のインラインで行うことができます。 これは、LocalSettings.php でフックを定義する際に便利です
MediaWiki\MediaWikiServices::getInstance()->getContentLanguage()->mMagicExtensions['wikicodeToHtml'] = ['MAG_CUSTOM', 'custom'];

Within LocalSettings.php

Magic words and their handling parser functions can be defined entirely in 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' ] );
      }
}

パーサー インターフェイス

出力の構文解析の制御

作成したパーサー関数が返すウィキテキストが完全に構文解析されるようにする (テンプレートの展開を含む) には、戻り値の noparse オプションに false を設定します:

return [ $output, 'noparse' => false ];

noparse の既定値は、ある時点、バージョン 1.12 付近のどこかで、false から true に変更されたようです。

逆に、作成したパーサー関数が、ウィキテキストを返すのではなく、構文解析されないままの HTML を返すようにするには、以下のように指定します:

return [ $output, 'noparse' => true, 'isHTML' => true ];

命名

MW は既定でそれぞれのパーサー関数名の先頭にハッシュ記号 (#) を追加します。 追加させない (パーサー関数名に接頭辞 # を付けない) ためには、以下で説明しているように、SFH_NO_HASH 定数をオプションの flags 引数の setFunctionHook に挿入します。

もしハッシュ記号を接頭辞に使わない名称を選ぶ場合は、その関数名の後ろにコロンを付けて作ったページ名を参照呼出しできないことにご留意ください。特に、関数名は名前空間名と同名にしてはいけません。ウィキ間参照読み込みの[1]を有効にした場合には、関数名をウィキ間接頭辞と同名にしてはいけません。

setFunctionHook フック

パーサーに関連するインターフェースの詳細は、includes/Parser.php 内の setFunctionHook の説明文を参照してください。 以下はこれらのコメントを転写したものです (古い可能性あり) 。

function setFunctionHook( $id, $callback, $flags = 0 )

パラメーター:

  • string $id - マジックワード ID
  • mixed $callback - 使用するコールバック関数 (とオブジェクト)
  • integer $flags - (省略可能) 値:
  • SFH_NO_HASH (1) constant if you call the function without #.
  • SFH_OBJECT_ARGS (2) if you pass a PPFrame object and array of arguments instead of a series of function arguments, for which see above.
  • Defaults to 0 (no flags).

戻り値: この名前の元のコールバック関数 (ある場合)

たとえば{{#sum:1|2|3}}など関数の作成。コールバック関数の構文の書き方を示します。

function myParserFunction( $parser, $arg1, $arg2, $arg3 ) { ... }

コールバックの戻り値は関数が出力した文字列もしくはまたはテキストを含む配列要素0と、他の要素のフラグいくつかを示します。フラグの名前はキーで指定します。 有効なフラグは以下のとおりです。

Name Type Default Description
found Boolean true 戻り値のテキストが有効であり、テンプレートの処理を停止する必要がある場合は true
text ? ? The text to return from the function. If isChildObj or isLocalObj are specified, this should be a DOM node instead.
noparse Boolean true true if text should not be preprocessed to a DOM tree, e.g. unsafe HTML tags should not be stripped, etc.
isHTML Boolean ? 戻り値のテキストが HTML であり、ウィキテキスト変換から保護する必要がある場合は trueBut see discussion
nowiki Boolean usually false true if wiki markup in the return value (text) should be escaped.
isChildObj Boolean ? true if the text is a DOM node needing expansion in a child frame.
isLocalObj Boolean ? true if the text is a DOM node needing expansion in the current frame. The default value depends on other values and outcomes.
preprocessFlags ? false Optional PPFrame flags to use when parsing the returned text. This only applies when noparse is false.
title ? false The Title object where the text came from.
forceRawInterwiki Boolean ? true if interwiki transclusion must be forced to be done in raw mode and not rendered.

Expensive parser functions

Some parser functions represent a significant use of a wiki's resources and should be marked as "expensive". The number of expensive parser functions on any given page is limited by the $wgExpensiveParserFunctionLimit setting. What counts as expensive is left up to the function itself, but typically, anything that is likely to cause a delay that extends beyond simple processing of data should be considered. This includes things like database reads and writes, launching a shell script synchronously, or file manipulation. On the other hand, not all such functions should necessarily be tagged. Semantic MediaWiki, for example, only marks a percentage of its database reads as expensive. This is due to the fact that on certain data-intensive pages, it could easily overflow the normal expensive parser function limits. In cases like this, the potential for noticeably slower performance that doesn't get flagged as expensive is a trade-off to having the functionality that SMW offers.

To mark your parser function as expensive, from within the body of the function's code, use $result = $parser->incrementExpensiveFunctionCount();. The return value will be false if the expensive function limit has been reached or exceeded.

名前付きパラメーター

パーサー関数はテンプレートもしくはタグ拡張機能と異なり、名前付きパラメーターをサポートしないものの、ときにはそれを回避する役に立ちます。利用者は引数を分けるために縦棒 ( | ) を使い慣れているため、パーサー関数のコンテキストでも同様の記述ができると便利です。これを実現する方法の簡単な例を示します。

function ExampleExtensionRenderParserFunction( &$parser ) {
	// Suppose the user invoked the parser function like so:
	// {{#myparserfunction: foo=bar | apple=orange | banana }}

	$options = extractOptions( array_slice( func_get_args(), 1 ) );

	// Now you've got an array that looks like this:
	// [foo] => 'bar'
	// [apple] => 'orange'
	// [banana] => true
	// Continue writing your code...
}

/**
 * Converts an array of values in form [0] => "name=value"
 * into a real associative array in form [name] => value
 * If no = is provided, true is assumed like this: [name] => 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;
}

関連項目

General and related guides:

Code:

Examples: