Extension:Loops

From MediaWiki.org

Jump to: navigation, search
Manual on MediaWiki Extensions
List of MediaWiki Extensions
Loops

Release status: experimental

Implementation Parser function
Description Loops, man! LOOPS!
Author(s) David M. Sledge (talk)
Version 0.2.0 (2008-03-04)
MediaWiki ≥ 1.12alpha
License GNU GPL 2.0 or later
Download See below
Parameters ExtLoops::$maxLoops
Hooks used

ParserFirstCallInit
LanguageGetMagic
ParserLimitReport
ParserClearState

Contents

[edit] What can this extension do?

While and do-while loops similar to those used in programming languages.

[edit] Usage

[edit] Syntax

By quirk or design, the preprocessor in v1.12alpha handles the first parameter differently than the rest, so it's ignored for the #while and #dowhile parser functions in order for them to work. See bug 12842.

[edit] #while

{{#while}} performs a loop (i.e. it repeatedly parses a given wiki markup block statement) so long as the condition mark-up evaluates to non-whitespace.

{{
  #while:
  | <condition text>
  | <block statement>
}}

[edit] Examples

Note: The following examples use the VariablesExtension.

The wiki markup:

{{ #vardefine: i | 0 }}{{
  #while:
  | {{ #ifexpr: {{ #var: i }} < 5 | true }}
  |<nowiki/>
* {{ #var: i }}{{ #vardefine: i | {{ #expr: {{ #var: i }} + 1 }} }}
}}

produces the following:

  • 0
  • 1
  • 2
  • 3
  • 4

{{#while}} can also be used in a template to simulate a numbered array. If the page "Template:Loops Test" contains

{{
  #vardefine: i | 0
}}{{
  #while:
  | {{{ arg{{#var: i }} |}}}
  |<nowiki/>
* {{{ arg{{#var: i }} }}}{{
    #vardefine: i
    | {{ #expr: {{ #var: i }} + 1 }}
  }}
}}

then the wiki-markup

{{Loops Test
|arg0=zero
|arg1=one
|arg2=two
|arg3=three
|arg4=four
}}

produces

  • zero
  • one
  • two
  • three
  • four

[edit] #dowhile

{{#dowhile}} performs exactly like {{#while}}, with the exception that the block statement is guaranteed to be parsed and displayed (if it results in displayable text) at least once. This done before the condition text is evaluated.

[edit] #foreachnamedarg

{{#foreachnamedarg}} is to be used in templates. It takes arguments that are passed to the template and puts them in variables accessible by VariablesExtension's {{#var:}} parser function.

{{
  #foreachnamedarg: <prefix>
  | <key>
  | <value>
  | <block statement>
}}

This function iterates through each argument whose name begins with <prefix>. With each iteration it puts the argument name minus <prefix> into <key> as if calling {{#vardefine: <key> }}. It then takes the value of the argument and puts it into <value> in a similar method. The block statement is then expanded. The block statement may contain {{#var: <key> }} and {{#var: <value> }} to access the stored arguments.

[edit] Example

If the page "Template:Loops Test" contains

{{
  #foreachnamedarg: arg
  | key
  | value
  | <nowiki/>
* {{#var: key}} = {{#var: value}}
}}

then the wiki markup

{{Loops Test
| arg1=val1
| spam=spammity
| arg5=val5
| argument=value
}}

produces

  • 1 = val1
  • 5 = val5
  • ument = value

This parser function only deals with named arguments. Numbered arguments are ignored.

[edit] Download instructions

Please cut and paste the code found below and place it in $IP/extension/Loops/Loops.php. Note: $IP stands for the root directory of your MediaWiki installation, the same directory that holds LocalSettings.php.

[edit] Installation

As of v0.2.0 this extension requires VariablesExtension to be installed. Once that is installed, Loops is installed by adding the following to LocalSettings.php:

require_once( "$IP/extensions/Loops/Loops.php" );

[edit] Configuration parameters

[edit] ExtLoops::$maxLoops

This parameter set the maximum number of loops a page is allowed to perform. Setting it to a negative value lets the loops run within the limits of php's environment. This parameter does not affect {{#foreachnamedarg:}}.

[edit] Code

[edit] Loops.php

<?php
 
/**
 * @package MediaWiki
 * @subpackage Extensions
 *
 * @link http://www.mediawiki.org/wiki/Extension:Loops Documentation
 *
 * @author David M. Sledge
 * @copyright Copyright © 2008 David M. Sledge
 * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0
 *     or later
 * @version 0.1.0
 *     initial creation.
 * @version 0.2.0
 *     #foreachnamedarg added
 *
 * @todo allow other functions in the extension to work without
 *     VariablesExtension.
 * @todo #foreacharg and #foreachnumarg
 * @todo inline documentation
 */
 
if ( !defined( "MEDIAWIKI" ) ) {
    die( "This file is a MediaWiki extension, it is not a valid entry point" );
}
 
$wgHooks['ParserFirstCallInit'][] = "ExtLoops::setup";
$wgExtensionCredits['parserhook'][] = array(
    'author'      => 'David M. Sledge',
    'name'        => 'Loops',
    'version'     => ExtLoops::VERSION,
    'description' => 'Parser functions for performing loops',
    'url'         => 'http://www.mediawiki.org/wiki/Extension:Loops',
);
 
$wgHooks['LanguageGetMagic'][]  = 'ExtLoops::languageGetMagic';
$wgHooks['ParserLimitReport'][] = 'ExtLoops::parserLimitReport';
 
class ExtLoops {
    const VERSION = "0.2.0";
    private static $parserFunctions = array(
        'dowhile'         => 'dowhile',
        'while'           => 'whileHook',
        'foreachnamedarg' => 'foreachNamedArg',
    );
    public static $maxLoops = 100;  // maximum number of loops allowed
                                    // (-1 = no limit).  #foreachnamedarg is
                                    // not limited by this.
 
    public static function setup( &$parser ) {
        global $wgMessageCache, $wgHooks;
 
        // These functions accept DOM-style arguments
        foreach( self::$parserFunctions as $hook => $function )
            $parser->setFunctionHook( $hook,
                array( __CLASS__, $function ), SFH_OBJECT_ARGS );
 
        require_once( dirname( __FILE__ ) . '/Loops.i18n.php' );
 
        foreach( Loops_i18n::getMessages() as $lang => $messages )
            $wgMessageCache->addMessages( $messages, $lang );
 
        $wgHooks['ParserClearState'][] = __CLASS__ . '::parserClearState';
 
        return true;
    }
 
    public static function languageGetMagic( &$magicWords, $langCode ) {
        require_once( dirname( __FILE__ ) . '/Loops.i18n.php' );
 
        foreach( Loops_i18n::magicWords( $langCode ) as $word => $trans )
            $magicWords[$word] = $trans;
 
        return true;
    }
 
    public static function parserLimitReport( $parser, &$report ) {
        if ( isset( $parser->loops_count ) ) {
            $report .= "#(do)while count: {$parser->loops_count}/" .
                self::$maxLoops . "\n";
        }
 
        return true;
    }
 
    public static function whileHook( &$parser, $frame, $args ) {
        // bug 12842:  first argument is automatically
        //   expanded, so we ignore this one
        array_shift( $args );
        $test = array_shift( $args );
        $loopStatement = array_shift( $args );
        $output = '';
 
        while ( isset( $test ) && trim( $frame->expand( $test ) ) !== '' ) {
            if ( self::$maxLoops >= 0 &&
                ++$parser->loops_count > self::$maxLoops )
                return wfMsgForContent( 'loops_max' );
 
            $output .= isset( $loopStatement ) ?
                trim( $frame->expand( $loopStatement ) ) : '';
        }
 
        //return '<pre><nowiki>'. $output . '</nowiki></pre>';
        return $output;
    }
 
    public static function dowhile( &$parser, $frame, $args ) {
        // bug 12842:  first argument is automatically
        //   expanded, so we ignore this one
        array_shift( $args );
        $test = array_shift( $args );
        $loopStatement = array_shift( $args );
        $output = '';
 
        do {
            if ( self::$maxLoops >= 0 &&
                ++$parser->loops_count > self::$maxLoops )
                return wfMsgForContent( 'loops_max' );
 
            $output .= isset( $loopStatement ) ?
                trim( $frame->expand( $loopStatement ) ) : '';
        }
        while ( isset( $test ) && trim( $frame->expand( $test ) ) !== '' );
 
        //return '<pre><nowiki>'. $output . '</nowiki></pre>';
        return $output;
    }
 
    public static function foreachNamedArg( &$parser, $frame, $args ) {
        global $wgExtVariables;
 
        // The first arg is already expanded, but this is a good habit to have.
        $filter = isset( $args[0] ) ? trim( $frame->expand( $args[0] ) ) : '';
        // name of the variable to store the argument name.  this
        // will be accessed in the loop by using {{#var:}}
        $keyVarName = isset( $args[1] ) ?
            trim( $frame->expand( $args[1] ) ) : '';
        // name of the variable to store the argument value.
        $valueVarName = isset( $args[2] ) ?
            trim( $frame->expand( $args[2] ) ) : '';
        $loopStatement = isset( $args[3] ) ? $args[3] : '';
        $output = '';
 
        foreach ( array_keys( $frame->namedArgs ) as $argName ) {
            if ( $filter == '' || strpos( $argName, $filter ) === 0 )
            {
                if ( $keyVarName !== $valueVarName )
                    $wgExtVariables->vardefine( $parser, $keyVarName,
                        substr( $argName, strlen( $filter ) ) );
 
                $argVal = $frame->getNamedArgument( $argName );
                $wgExtVariables->vardefine( $parser, $valueVarName, $argVal );
                $output .= trim( $frame->expand( $loopStatement ) );
            }
        }
 
        return $output;
    }
 
    public static function parserClearState( &$parser ) {
        $parser->loops_count = 0;
 
        return true;
    }
}

[edit] Loops.i18n.php

<?php
 
class Loops_i18n {
    private static $words = array(
    // English
        'en' => array(
            'dowhile'         => array( 0, 'dowhile' ),
            'while'           => array( 0, 'while' ),
            'foreachnamedarg' => array( 0, 'foreachnamedarg' ),
        ),
    );
 
    private static $messages = array(
    // English
        'en' => array(
            'loops_max' => "Maximum number of loops have been performed",
        )
    );
 
    /**
     * Get translated magic words, if available
     *
     * @param string $lang Language code
     * @return array
     */
    public static function magicWords( $lang ) {
        // English is used as a fallback, and the English synonyms are
        // used if a translation has not been provided for a given word
        return ( $lang == 'en' || !isset( self::$words[$lang] ) ) ?
            self::$words['en'] :
            array_merge( self::$words['en'], self::$words[$lang] );
    }
 
    public static function getMessages() {
        return self::$messages;
    }
}

[edit] See also

Personal tools