Die MediaWiki-Software entwickelt sich ständig weiter und wird dabei immer besser und technisch trickreicher. Jüngste, großartige Erneuerung, ist die Möglichkeit der Vorlagenprogrammierung. Hier sollen neue Features erdacht, ersonnen, diskutiert, ... werden, die die Vorlagenprogrammierung noch effizienter gestalten können.

Daher mein Aufruf an alle die das hier lesen: Besorgt euch MediaZilla-Accounts und votet für die Bugs!

Ich habe neulich einen Bug gefiled der direkt mit einem Patch daherkam. Dieser führt eine neue Funktion {{#for:}} ein. Bisher scheint der Bug keine große Beachtung gefunden zu haben, insbesondere steht die Frage nach der verlangten Rechenkapazität zur Diskussion. Ich habe daher auch Vorschläge gepostet, wie man diese einschränken kann.

Anbei kopiere ich die Beschreibung des Patch und den Quelltext:

Bug #6794: ParserFunction: {{#for: }}

 The syntax is {{#for: loopcount | text to loop | join-character | initial value
 | step size }}
 
 each parameter can be ommited, but ommiting the first 2 ones won't make any
 sense.
 
 Examples:
 
 <wikitext>List of some Years: {{subst:#for: 9 | * [[19$i]] | $n | 10 |
 10}}</wikitext>
 
 will be
 
 <wikitext>* [[1910]]
 * [[1920]]
 * [[1930]]
 * [[1940]]
 * [[1950]]
 * [[1960]]
 * [[1970]]
 * [[1980]]
 * [[1990]]</wikitext>
 
 $n is a magic string in join-string cause it is not possible to insert a normal
 new line there.
 
 simpler example:
 
 Let'c count: {{#for 9 | $i!}}
 
 You can also go backwards:
 {{#for: 9 | Value: $i | $b | 0 | -1}}
 ($b is another magic thing for >>|<< (vertical bar))
 
 Countdown:
 {{#for: 10 | $i, || 10 | -1}} '''Lift off!'''

Der Patch

Bearbeiten

Dies ist die neue ParserFunctions.php (latest snapshots: http://svn.wikimedia.org/viewvc/mediawiki/trunk/extensions/ParserFunctions/ http://svn.wikimedia.org/svnroot/mediawiki/trunk/extensions/ParserFunctions/ParserFunctions.php)

<?php

if ( !defined( 'MEDIAWIKI' ) ) {
	die( 'This file is a MediaWiki extension, it is not a valid entry point' );
}

$wgExtensionFunctions[] = 'wfSetupParserFunctions';
$wgExtensionCredits['parserhook'][] = array( 'name' => 'ParserFunctions', 'url' => 'http://meta.wikimedia.org/wiki/ParserFunctions', 'author' => 'Tim Starling' );

$wgHooks['LanguageGetMagic'][]       = 'wfParserFunctionsLanguageGetMagic';

class ExtParserFunctions {
	var $mExprParser;

	function &getExprParser() {
		if ( !isset( $this->mExpr ) ) {
			if ( !class_exists( 'ExprParser' ) ) {
				require( dirname( __FILE__ ) . '/Expr.php' );
				ExprParser::addMessages();
			}
			$this->mExprParser = new ExprParser;
		}
		return $this->mExprParser;
	}

	function expr( &$parser, $expr = '' ) {
		$exprParser =& $this->getExprParser();
		$result = $exprParser->doExpression( $expr );
		if ( $result === false ) {
			return $exprParser->lastErrorMessage;
		} else {
			return $result;
		}
	}

	function ifexpr( &$parser, $expr = '', $then = '', $else = '' ) {
		$exprParser =& $this->getExprParser();	
		$result = $exprParser->doExpression( $expr );
		if ( $result === false ) {
			return $exprParser->lastErrorMessage;
		} elseif ( $result ) {
			return $then;
		} else {
			return $else;
		}
	}

	function ifHook( &$parser, $test = '', $then = '', $else = '' ) {
		if ( $test !== '' ) {
			return $then;
		} else {
			return $else;
		}
	}

	function ifeq( &$parser, $left = '', $right = '', $then = '', $else = '' ) {
		if ( $left == $right ) {
			return $then;
		} else {
			return $else;
		}
	}
	
	function switchHook( &$parser /*,...*/ ) {
		$args = func_get_args();
		array_shift( $args );
		$value = trim(array_shift($args));
		$found = false;
		$parts = null;
		$default = null;
		foreach( $args as $arg ) {
			$parts = array_map( 'trim', explode( '=', $arg, 2 ) );
			if ( count( $parts ) == 2 ) {
				if ( $found || $parts[0] == $value ) {
					return $parts[1];
				} else {
					$mwDefault =& MagicWord::get( 'default' );
					if ( $mwDefault->matchStartAndRemove( $parts[0] ) ) {
						$default = $parts[1];
					} # else wrong case, continue
				}
			} elseif ( count( $parts ) == 1 ) {
				# Multiple input, single output
				# If the value matches, set a flag and continue
				if ( $parts[0] == $value ) {
					$found = true;
				}
			} # else RAM corruption due to cosmic ray?
		}
		# Default case
		# Check if the last item had no = sign, thus specifying the default case
		if ( count( $parts ) == 1) {
			return $parts[0];
		} elseif ( !is_null( $default ) ) {
			return $default;
		} else {
			return '';
		}
	}
	
	function ifexist( &$parser, $title = '', $then = '', $else = '' ) {
		$title = Title::newFromText( $title );
		return is_object( $title ) && $title->exists() ? $then : $else;
	}

	/**
	 * @author <Warhog aka Julian Fleischer (mediazilla@warhog.net)>
	 * @date 2006-07-23 16:57
	 */
	function forHook( &$parser, $loopcount = 0, $text = '', $joinby = '', $init = 1, $step = 1 ) {
		// when there is no text to loop the whole loop is useless
		if (strlen($text) < 1) {
			return ''; // no error needs to be printed, it's simply nothing
		}
		// convert arguments to integers so what we don't have to worry about texts etc
		$loopcount = (int) $loopcount;
		$init = (int) $init;
		$step = (int) $step;

		// Check for the sense of the given parameters
		if ($loopcount < 1) { // a loop which will not be looped is senseless
			return ''; // maybe print an error-message here?
		}
		if ($step == 0) { // a loop that does not progress is also senseless
			return ''; // maybe print an error-message here?
		}

		// if the function has not exited due to senseless parameters yet
		// then do the action here
		// calculate the min/max value for $i according to loopcount
		$max = $init + $loopcount * $step;

		$rows = array();
		// does the loop count upwards or downwards?
		if ($step > 0) { // the loop counts upwards, so we have an upper limit
			for ($i = $init; $i < $max; $i = $i + $step) {
				// replace '$i' with $i so that we can access the iterator and the loop makes sense
				$rows[] = str_replace('$i', "$i", $text);
			}
		} else { // the loop counts downwards, so we have a lower limit
			for ($i = $init; $i > $max; $i = $i + $step) {
				// same thing here
				$rows[] = str_replace('$i', "$i", $text);	
			}
		}

		// prepare glue string
		$joinby = (string) $joinby;
		// do some magic: new line, this is regarded as whitespace by the parser
		// so we would not have a chance to add new-lines via parameters
		// important for e.g. {{for: 3 | * List Item $i | $n }}
		$joinby = str_replace('$n', "\n", $joinby);
		$joinby = str_replace('$b', "|", $joinby);

		// return the generated rows joint by the glue-string $joinby
		return join($joinby, $rows);
	}

}

function wfSetupParserFunctions() {
	global $wgParser, $wgMessageCache, $wgExtParserFunctions;

	$wgExtParserFunctions = new ExtParserFunctions;

	$wgParser->setFunctionHook( 'expr', array( &$wgExtParserFunctions, 'expr' ) );
	$wgParser->setFunctionHook( 'if', array( &$wgExtParserFunctions, 'ifHook' ) );
	$wgParser->setFunctionHook( 'ifeq', array( &$wgExtParserFunctions, 'ifeq' ) );
	$wgParser->setFunctionHook( 'ifexpr', array( &$wgExtParserFunctions, 'ifexpr' ) );
	$wgParser->setFunctionHook( 'switch', array( &$wgExtParserFunctions, 'switchHook' ) );
	$wgParser->setFunctionHook( 'ifexist', array( &$wgExtParserFunctions, 'ifexist' ) );
	$wgParser->setFunctionHook( 'for', array( &$wgExtParserFunctions, 'forHook' ) );	
}

function wfParserFunctionsLanguageGetMagic( &$magicWords, $langCode ) {
	switch ( $langCode ) {
		case 'he':
			$magicWords['expr']    = array( 0, 'חשב',         'expr' );
			$magicWords['if']      = array( 0, '×ª× ××™',        'if' );
			$magicWords['ifeq']    = array( 0, 'שווה',        'ifeq' );
			$magicWords['ifexpr']  = array( 0, 'חשב ×ª× ××™',    'ifexpr' );
			$magicWords['switch']  = array( 0, 'בחר',         'switch' );
			$magicWords['default'] = array( 0, '#ברירת מחדל', '#default' );
			$magicWords['ifexist'] = array( 0, 'קיים',         'ifexist' );
			$magicWords['for']     = array( 0, '-tobedone-',         'for' ); // DONT KNOW ABOUT HEBREW !?
			break;
		default:
			$magicWords['expr']    = array( 0, 'expr' );
			$magicWords['if']      = array( 0, 'if' );
			$magicWords['ifeq']    = array( 0, 'ifeq' );
			$magicWords['ifexpr']  = array( 0, 'ifexpr' );
			$magicWords['switch']  = array( 0, 'switch' );
			$magicWords['default'] = array( 0, '#default' );
			$magicWords['ifexist'] = array( 0, 'ifexist' );
			$magicWords['for']     = array( 0, 'for' );
	}
	return true;
}

?>

Bekannte Fehler / Konzeptschwächen

Bearbeiten
  • Ich möchte gar nicht wissen was passiert wenn man {{#for:}}-Schleifen schachtelt.
  • Die Schleifen könnten von Vandalen bösartig ausgenutzt werden (obgleich ich bezweifle dass diese entsprechend tief sich in die Materie hineinlesen)

Eure Meinungen dazu

Bearbeiten

Ich mache damit mal den Anfang: Ich fände eine solche Vorlage, und wenn es nur für subst:-Zwecke wäre, enorm praktisch. Da es im Normalfall keine Listen gibt die man erstellen müsste die über 100 Einträge lang sind gäbe es auch keine "unglaubliche Serverbelastung". Die Babel-X Vorlage ist mit 100 If-Abfragen da schon fast rechenintensiver. (Meine Meinung - aber ich bin als Autor des Patch wohl wenig objektiv ;-D )--Warhog (???, +/-) 04:19, 25. Jul 2006 (CEST)

Ich verstehe nicht ganz, soll diese Funktion denn schon implementiert sein? Ein #for: kann ich nämlich nicht verwenden.
Prizipiell ist die Idee jedoch gut, da diese Funktionalität notwendig ist. Einen Iterator habe ich bereits auf rekursive Weise implementiert. Die Anwendung ist jedoch leider umständlich. Ein fertig implementierter Iterator wie dieses "for-statement" ist daher in jedem Falle vorzuziehen. Die Bedenken die Du aufgeführt hast teile ich jedoch. Gruß, -- zOiDberg (Δ | Α & Ω) 13:33, 14. Sep 2006 (CEST)

ROOTPAGENAME

Bearbeiten

Siehe Wikipedia:WikiProjekt_Vorlagen/Projekt:Optimierung_durch_Programmierung#Vorlage:Archiv.

Bug 6747: Feature-Request: Introduce a new Variable {{ROOTPAGENAME}} (similar to {{BASEPAGENAME}}

Wir benötigen dringend den Einbau dieser Stringfunktionen derart, dass man damit Vorlagen programmieren kann.  Augiasstallputzer   10:37, 2. Aug 2006 (CEST)