Hướng dẫn:Hàm phân tích cú pháp

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

Các hàm phân tích cú pháp được thêm vào trong MediaWiki 1.7, là một loại tiện mở rộng tích hợp chặt chẽ với trình phân tích cú pháp. Không nên nhầm lẫn cụm từ "hàm phân tích cú pháp" với Extension:ParserFunctions , đây là tập hợp các hàm phân tích cú pháp đơn giản. (Xem Trợ giúp:Tiện ích mở rộng:ParserFunctions .)

Miêu tả

Trong khi phần mở rộng thẻ dự kiến ​​sẽ lấy văn bản chưa được xử lý và trả lại HTML cho trình duyệt, chức năng phân tích cú pháp có thể 'tương tác' với các phần tử wiki khác trong trang. Ví dụ: đầu ra của hàm trình phân tích cú pháp có thể được sử dụng làm tham số bản mẫu hoặc trong việc xây dựng liên kết.

Cú pháp điển hình cho một hàm phân tích cú pháp là:

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

Để biết thêm thông tin, hãy xem tài liệu cho Parser::setFunctionHook ( $id, $callback, $flags = 0 ). Tài liệu này nêu rõ:

Hàm gọi lại phải có dạng:
function myParserFunction( $parser, $arg1, $arg2, $arg3 ) { ... }
Hoặc với SFH_OBJECT_ARGS:
function myParserFunction( $parser, $frame, $args ) { ... }

Biến số đầu tiên của hàm gọi chuyển tất cả các đối số dưới dạng văn bản thuần túy. Biến số thứ hai bỏ qua các luận lý như một mảng của PPNode , ngoại trừ biến thứ nhất ($args[0]), hiện đang là văn bản, mặc dù điều này có thể thay đổi sau. Chúng đại diện cho mã wiki chưa được mở rộng. Tham số $frame có thể được sử dụng để mở rộng các luận lý này khi cần. Phần này thường được sử dụng để xử lý điều kiện, do đó chỉ các trường hợp "true" mới được đánh giá bằng hàm phân tích cú pháp giống if hoặc switch. Đối tượng frame còn có thể trèo lên cây tài liệu để lấy thông tin về hàm gọi và có các hàm để xác định và quản lý độ sâu hàm gọi, time-to-live và liệu kết quả của hàm phân tích cú pháp có biến động hay không.

Việc tạo hàm phân tích cú pháp phức tạp hơn một chút so với việc tạo thẻ mới vì tên hàm phải là từ ma thuật, một từ khóa hỗ trợ nhiều tên giả và khả năng địa phương hóa.

Ví dụ đơn giản

Dưới đây là một ví dụ về tiện ích mở rộng tạo ra chức năng phân tích cú pháp.

Việc đăng ký được đặt trong extension.json và mã nguồn được đặt trong src/ExampleExtensionHooks.php tương ứng:

{
	"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 {
   // Đăng ký bất kỳ callback render nào với trình phân tích cú pháp
   public static function onParserFirstCallInit( Parser $parser ) {

      // Tạo một hook hàm liên kết từ ma thuật "ví dụ" với renderExample()
      $parser->setFunctionHook( 'example', [ self::class, 'renderExample' ] );
   }

   // Kết xuất đầu ra của {{#example:}}.
   public static function renderExample( Parser $parser, $param1 = '', $param2 = '', $param3 = '' ) {

      // Các tham số đầu vào là wikitext với các bản mẫu được mở rộng.
      // Đầu ra cũng phải là mã wiki.
      $output = "param1 is $param1 and param2 is $param2 and param3 is $param3";

      return $output;
   }
}

Một tập tin khác, ExampleExtension.i18n.php, trong thư mục mở rộng của bạn (Không có trong thư mục con src/) sẽ chứa:

<?php
/**
 * @license GPL-2.0-or-later
 * @author Your Name (YourUserName)
 */

$magicWords = [];

/** English
 * @author Your Name (YourUserName)
 */
$magicWords['en'] = [
   'example' => [ 0, 'example' ],
];

Khi tiện ích mở rộng này được bật,

  • {{#example: hello | hi | hey}}

cho ra:

  • param1 là hello và param2 là hi và param3 là hey
Mảng magicWords này là bắt buộc. Nếu thiếu nó, hàm phân tích cú pháp sẽ không hoạt động; nội dung của biến {{#example: hello | hi}} sẽ được hiển thị như thể tiện ích mở rộng chưa được cài đặt. Nếu chỉ khởi tạo mảng ngôn ngữ cụ thể mà không khởi tạo chính bản thâng mảng magicWords, điều này có thể gây ra lỗi khi địa phương hóa vì bản dịch từ các tiện ích mở rộng khác có thể rò rỉ sang tiện ích của bạn. Bạn có thể liên kết các từ ma thuật nội tuyến trong PHP thay vì thông qua tập tin i18n. Điều này rất hữu ích khi xác định hook trong LocalSettings.php
MediaWiki\MediaWikiServices::getInstance()->getContentLanguage()->mMagicExtensions['wikicodeToHtml'] = ['MAG_CUSTOM', 'custom'];

Trong 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];
}

Chức năng dài hơn

Đối với các hàm dài hơn, bạn có thể muốn tách các hàm hook thành tệp _body.php hoặc .hooks.php và biến chúng thành các hàm tĩnh của một lớp. Sau đó, bạn có thể tải lớp bằng $wgAutoloadClasses và gọi các hàm tĩnh trong móc nối(hook); ví dụ.:

Đặt cái này vào tệp extension.json của bạn:

"Hooks": {
	"ParserFirstCallInit": "ExampleExtensionHooks::onParserFirstCallInit"
},
"AutoloadClasses": {
	"ExampleExtensionHooks": "src/ExampleExtensionHooks.php"
}

Sau đó đặt cái này vào tệp src/ExampleExtensionHooks.php của bạn:

class ExampleExtensionHooks {
      public static function onParserFirstCallInit( Parser $parser ) {
           $parser->setFunctionHook( 'example', [ self::class, 'renderExample' ] );
      }
}

Giao diện trình phân tích cú pháp

Kiểm soát phân tích cú pháp đầu ra

Để văn bản wiki do hàm trình phân tích cú pháp của bạn trả về được phân tích cú pháp đầy đủ (bao gồm cả phần mở rộng của bản mẫu), hãy đặt tùy chọn noparse thành `false` khi trả về:

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

Có vẻ như giá trị mặc định cho noparse đã thay đổi từ sai thành đúng, ít nhất là trong một số trường hợp, vào khoảng phiên bản 1.12.

Ngược lại, để hàm phân tích cú pháp của bạn trả về HTML vẫn chưa được phân tích cú pháp, thay vì trả về wikitext, hãy sử dụng:

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

Naming

Theo mặc định, MW thêm một ký tự băm (ký hiệu số, "#") vào tên của mỗi chức năng trình phân tích cú pháp. Để loại bỏ phần bổ sung đó (và lấy hàm trình phân tích cú pháp không có tiền tố "#"), hãy bao gồm hằng số SFH_NO_HASH trong đối số cờ tùy chọn cho setFunctionHook, như được mô tả bên dưới.

Khi chọn một tên không có tiền tố băm, hãy lưu ý rằng việc nhúng một trang có tên bắt đầu bằng tên hàm đó theo sau bởi dấu hai chấm là không thể nữa. Đặc biệt, tránh đặt tên hàm bằng tên không gian tên. Trong trường hợp bật tính năng chuyển ngữ liên wiki [1], cũng nên tránh các tên hàm tương đương với tiền tố liên wiki.

The setFunctionHook hook

For more details of the interface into the parser, see the documentation for setFunctionHook in includes/Parser.php. Here's a (possibly dated) copy of those comments:

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

Parameters:

  • string $id - The magic word ID
  • mixed $callback - The callback function (and object) to use
  • integer $flags - Optional, set it to the SFH_NO_HASH constant to call the function without "#".

Return value: The old callback function for this name, if any

Create a function, e.g., {{#sum:1|2|3}}. The callback function should have the form:

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

The callback may either return the text result of the function, or an array with the text in element 0, and a number of flags in the other elements. The names of the flags are specified in the keys. Valid flags are:

Name Type Default Description
found Boolean true true if the text returned is valid and processing of the template must stop.
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 ? true if the returned text is HTML and must be armoured against wikitext transformation. But 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 he 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.

Named parameters

Parser functions do not support named parameters the way templates and tag extensions do, but it is occasionally useful to fake it. Users are often accustomed to using vertical bars ( | ) to separate arguments, so it's nice to be able to do that in the parser function context, too. Here's a simple example of how to accomplish this:

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;
}

See also

General and related guides:

Code:

  • The Parser Hooks PHP library, which provides an object orientated interface for declarative parser hooks

Examples: