Extension:ParserFunctions/Extended

From MediaWiki.org

Jump to: navigation, search

Contents

This extension consists of ParserFunctions after applying patch [1] by Carl Fürstenberg. The next section gives the resulting Expr.php after applying the patch to [2]). The following section gives the resulting Expr.php after applying the patch to [3], and adapting it to the changed error handling [4].

With this extension scientific notation is not only produced as output, but also allowed as input. Also the power sign ^ works.

Further it includes the PHP functions abs, floor, ceil, fmod, and sqrt, and a function idiv which applies conversion to an integer by the PHP function "(int)" (rounding towards zero) to the quotient of the arguments.

Finally it contains the functions #min and #max, which are the PHP functions min and max, for the case that each argument is numeric.

Examples:

  • {{#expr:2e3^10}} gives 1.024E+033
  • {{#expr:-10 fmod 7}} gives -3
  • {{#expr:-10 idiv 7}} gives -1
  • {{#expr:floor (-10/7)}} gives -2
  • {{#expr:ceil (-10/7)}} gives -1
  • {{#expr:abs-3}} gives 3
  • {{#expr:sqrt3}} gives 1.7320508075689
  • {{#min:4e3|3e4|35*450}} gives 4000
  • {{#max:4e3|3e4|35*450}} gives 30000

[edit] Expr.php (extended)

<?php
 
if ( !defined( 'MEDIAWIKI' ) ) {
	die( 'This file is a MediaWiki extension, it is not a valid entry point' );
}
 
// Character classes
define( 'EXPR_WHITE_CLASS', " \t\r\n" );
define( 'EXPR_NUMBER_CLASS', '0123456789.' );
 
// Token types
define( 'EXPR_WHITE', 1 );
define( 'EXPR_NUMBER', 2 );
define( 'EXPR_NEGATIVE', 3 );
define( 'EXPR_POSITIVE', 4 );
define( 'EXPR_PLUS', 5 );
define( 'EXPR_MINUS', 6 );
define( 'EXPR_TIMES', 7 );
define( 'EXPR_DIVIDE', 8 );
define( 'EXPR_MOD', 9 );
define( 'EXPR_OPEN', 10 );
define( 'EXPR_CLOSE', 11 );
define( 'EXPR_AND', 12 );
define( 'EXPR_OR', 13 );
define( 'EXPR_NOT', 14 );
define( 'EXPR_EQUALITY', 15 );
define( 'EXPR_LESS', 16 );
define( 'EXPR_GREATER', 17 );
define( 'EXPR_LESSEQ', 18 );
define( 'EXPR_GREATEREQ', 19 );
define( 'EXPR_NOTEQ', 20 );
define( 'EXPR_ROUND', 21 );
define( 'EXPR_INT_DIVIDE', 22); 
define( 'EXPR_CEIL', 23); 
define( 'EXPR_FLOOR', 24); 
define( 'EXPR_ABS', 25); 
define( 'EXPR_FLOAT_MOD', 26); 
define( 'EXPR_POW', 27); 
define( 'EXPR_SQRT', 28); 
define( 'EXPR_E', 29); 
 
class ExprError extends Exception {
	public function __construct($msg, $parameter = ''){
		$this->message = wfMsgForContent( "expr_$msg", htmlspecialchars( $parameter ) );
	}
}
 
class ExprParser {
	var $maxStackSize = 100;
 
	var $precedence = array( 
		EXPR_NEGATIVE => 10,
		EXPR_POSITIVE => 10,
                EXPR_E => 10,
		EXPR_NOT => 9,
		EXPR_ABS => 9, 
 		EXPR_CEIL => 9, 
 		EXPR_FLOOR => 9, 
 		EXPR_POW => 9, 
 		EXPR_SQRT => 9, 
		EXPR_TIMES => 8,
		EXPR_DIVIDE => 8,
		EXPR_INT_DIVIDE => 8,
		EXPR_MOD => 8,
		EXPR_FLOAT_MOD => 8,
		EXPR_PLUS => 6,
		EXPR_MINUS => 6,
		EXPR_ROUND => 5,
		EXPR_EQUALITY => 4,
		EXPR_LESS => 4,
		EXPR_GREATER => 4,
		EXPR_LESSEQ => 4,
		EXPR_GREATEREQ => 4,
		EXPR_NOTEQ => 4,
		EXPR_AND => 3,
		EXPR_OR => 2,
		EXPR_OPEN => -1,
		EXPR_CLOSE => -1
	);
 
	var $names = array(
		EXPR_NEGATIVE => '-',
		EXPR_POSITIVE => '+',
		EXPR_NOT => 'not',
		EXPR_TIMES => '*',
		EXPR_DIVIDE => '/',
		EXPR_MOD => 'mod',
		EXPR_FLOAT_MOD => 'fmod',
		EXPR_PLUS => '+',
		EXPR_MINUS => '-',
		EXPR_ROUND => 'round',
		EXPR_EQUALITY => '=',
		EXPR_LESS => '<',
		EXPR_GREATER => '>',
		EXPR_LESSEQ => '<=',
		EXPR_GREATEREQ => '>=',
		EXPR_NOTEQ => '<>',
		EXPR_AND => 'and',
		EXPR_OR => 'or',
		EXPR_INT_DIVIDE => 'idiv', 
 		EXPR_ABS => 'abs', 
 		EXPR_CEIL => 'ceil', 
 		EXPR_FLOOR => 'floor', 
 		EXPR_POW => '^', 
 		EXPR_SQRT => 'sqrt', 
 		EXPR_E => 'e', 
 
	);
 
 
	var $words = array(
		'mod' => EXPR_MOD,
		'and' => EXPR_AND,
		'or' => EXPR_OR,
		'not' => EXPR_NOT,
		'round' => EXPR_ROUND,
		'div' => EXPR_DIVIDE,
		'idiv' => EXPR_INT_DIVIDE, 
 		'floor' => EXPR_FLOOR, 
 		'ceil' => EXPR_CEIL, 
 		'abs' => EXPR_ABS, 
 		'fmod' => EXPR_FLOAT_MOD, 
 		'sqrt' => EXPR_SQRT, 
 		'e' => EXPR_E,  
	);
 
 
	/**
	 * Add expression messages to the message cache
	 * @static
	 */
	function addMessages() {
		global $wgMessageCache;
		$wgMessageCache->addMessages( array(
			'expr_stack_exhausted' => 'Expression error: Stack exhausted',
			'expr_unexpected_number' => 'Expression error: Unexpected number',
			'expr_preg_match_failure' => 'Expression error: Unexpected preg_match failure',
			'expr_unrecognised_word' => 'Expression error: Unrecognised word "$1"',
			'expr_unexpected_operator' => 'Expression error: Unexpected $1 operator',
			'expr_missing_operand' => 'Expression error: Missing operand for $1',
			'expr_unexpected_closing_bracket' => 'Expression error: Unexpected closing bracket',
			'expr_unrecognised_punctuation' => 'Expression error: Unrecognised punctuation character "$1"',
			'expr_unclosed_bracket' => 'Expression error: Unclosed bracket',
			'expr_division_by_zero' => 'Division by zero',
			'expr_unknown_error' => 'Expression error: Unknown error ($1)',
			'expr_not_a_number' => 'In $1: result is not a number',
		));
	}
	/**
	 * Evaluate a mathematical expression
	 *
	 * The algorithm here is based on the infix to RPN algorithm given in
	 * http://montcs.bloomu.edu/~bobmon/Information/RPN/infix2rpn.shtml
	 * It's essentially the same as Dijkstra's shunting yard algorithm.
	 */
	function doExpression( $expr ) {
		$operands = array();
		$operators = array();
 
		# Unescape inequality operators
		$expr = strtr( $expr, array( '&lt;' => '<', '&gt;' => '>' ) );
 
		$p = 0;
		$end = strlen( $expr );
		$expecting = 'expression';
 
 
		while ( $p < $end ) {
			if ( count( $operands ) > $this->maxStackSize || count( $operators ) > $this->maxStackSize ) {
				throw new ExprError('stack_exhausted');
			}
			$char = $expr[$p];
			$char2 = substr( $expr, $p, 2 );
 
			// Mega if-elseif-else construct
			// Only binary operators fall through for processing at the bottom, the rest 
			// finish their processing and continue
 
			// First the unlimited length classes
 
			if ( false !== strpos( EXPR_WHITE_CLASS, $char ) ) {
				// Whitespace
				$p += strspn( $expr, EXPR_WHITE_CLASS, $p );
				continue;
			} elseif ( false !== strpos( EXPR_NUMBER_CLASS, $char ) ) {
				// Number
				if ( $expecting != 'expression' ) {
					throw new ExprError('unexpected_number');
				}
 
				// Find the rest of it
				$length = strspn( $expr, EXPR_NUMBER_CLASS, $p );
				// Convert it to float, silently removing double decimal points
				$operands[] = floatval( substr( $expr, $p, $length ) );
				$p += $length;
				$expecting = 'operator';
				continue;
			} elseif ( ctype_alpha( $char ) ) {
				// Word
				// Find the rest of it
				$remaining = substr( $expr, $p );
				if ( !preg_match( '/^[A-Za-z]*/', $remaining, $matches ) ) {
					// This should be unreachable
					throw new ExprError('preg_match_failure');
				}
				$word = strtolower( $matches[0] );
				$p += strlen( $word );
 
				// Interpret the word
				if ( !isset( $this->words[$word] ) ){
					throw new ExprError('unrecognised_word', $word);
				}
				$op = $this->words[$word];
				// Unary operator
				switch($op){
				case EXPR_NOT:
				case EXPR_CEIL: 
 				case EXPR_FLOOR: 
 				case EXPR_ABS: 
 				case EXPR_SQRT: 
					if ( $expecting != 'expression' ) {
						throw new ExprError('unexpected_operator', $word);
					}
					$operators[] = $op;
					continue 2;
				}
				// Binary operator, fall through
				$name = $word;
			}
 
			// Next the two-character operators
 
			elseif ( $char2 == '<=' ) {
				$name = $char2;
				$op = EXPR_LESSEQ;
				$p += 2;
			} elseif ( $char2 == '>=' ) {
				$name = $char2;
				$op = EXPR_GREATEREQ;
				$p += 2;
			} elseif ( $char2 == '<>' || $char2 == '!=' ) {
				$name = $char2;
				$op = EXPR_NOTEQ;
				$p += 2;
			}
 
			// Finally the single-character operators
 
			elseif ( $char == '+' ) {
				++$p;
				if ( $expecting == 'expression' ) {
					// Unary plus
					$operators[] = EXPR_POSITIVE;
					continue;
				} else {
					// Binary plus
					$op = EXPR_PLUS;
				}
			} elseif ( $char == '-' ) {
				++$p;
				if ( $expecting == 'expression' ) {
					// Unary minus
					$operators[] = EXPR_NEGATIVE;
					continue;
				} else {
					// Binary minus
					$op = EXPR_MINUS;
				}
			} elseif ( $char == '*' ) {
				$name = $char;
				$op = EXPR_TIMES;
				++$p;
			} elseif ( $char == '/' ) {
				$name = $char;
				$op = EXPR_DIVIDE;
				++$p;
			} elseif ( $char == '(' )  {
				if ( $expecting == 'operator' ) {
					throw new ExprError('unexpected_operator', '(');
				}
				$operators[] = EXPR_OPEN;
				++$p;
				continue;
			} elseif ( $char == ')' ) {
				$lastOp = end( $operators );
				while ( $lastOp && $lastOp != EXPR_OPEN ) {
					$this->doOperation( $lastOp, $operands );
					array_pop( $operators );
					$lastOp = end( $operators );
				}
				if ( $lastOp ) {
					array_pop( $operators );
				} else {
					throw new ExprError('unexpected_closing_bracket');
				}
				$expecting = 'operator';
				++$p;
				continue;
			} elseif ( $char == '=' ) {
				$name = $char;
				$op = EXPR_EQUALITY;
				++$p;
			} elseif ( $char == '<' ) {
				$name = $char;
				$op = EXPR_LESS;
				++$p;
			} elseif ( $char == '>' ) {
				$name = $char;
				$op = EXPR_GREATER;
				++$p;
			} elseif ( $char == '^' ) { 
 				$name = $char; 
 				$op = EXPR_POW; 
 				++$p; 
			} else {
				throw new ExprError('unrecognised_punctuation', $char);
			}
 
			// Binary operator processing
			if ( $expecting == 'expression' ) {
				throw new ExprError('unexpected_operator', $name);
			}
 
			// Shunting yard magic
			$lastOp = end( $operators );
			while ( $lastOp && $this->precedence[$op] <= $this->precedence[$lastOp] ) {
				$this->doOperation( $lastOp, $operands );
				array_pop( $operators );
				$lastOp = end( $operators );
			}
			$operators[] = $op;
			$expecting = 'expression';
		}
 
		// Finish off the operator array
		while ( $op = array_pop( $operators ) ) {
			if ( $op == EXPR_OPEN ) {
				throw new ExprError('unclosed_bracket');
			}
			$this->doOperation( $op, $operands );
		}
 
		return implode( "<br />\n", $operands );
	}
 
	function doOperation( $op, &$stack ) {
		switch ( $op ) {
			case EXPR_NEGATIVE:
				if ( count( $stack ) < 1 ) throw new ExprError('missing_operand', $this->names[$op]);
				$arg = array_pop( $stack );
				$stack[] = -$arg;
				break;
			case EXPR_POSITIVE:
				if ( count( $stack ) < 1 ) throw new ExprError('missing_operand', $this->names[$op]);
				break;
			case EXPR_TIMES:
				if ( count( $stack ) < 2 ) throw new ExprError('missing_operand', $this->names[$op]);
				$right = array_pop( $stack );
				$left = array_pop( $stack );
				$stack[] = $left * $right;
					break;
			case EXPR_DIVIDE:
				if ( count( $stack ) < 2 ) throw new ExprError('missing_operand', $this->names[$op]);
				$right = array_pop( $stack );
				$left = array_pop( $stack );
				if ( $right == 0 ) throw new ExprError('division_by_zero', $this->names[$op]);
				$stack[] = $left / $right;
				break;
			case EXPR_MOD:
				if ( count( $stack ) < 2 ) throw new ExprError('missing_operand', $this->names[$op]);
				$right = array_pop( $stack );
				$left = array_pop( $stack );
				if ( $right == 0 ) throw new ExprError('division_by_zero', $this->names[$op]);
				$stack[] = $left % $right;
				break;
			case EXPR_PLUS:
				if ( count( $stack ) < 2 ) throw new ExprError('missing_operand', $this->names[$op]);
				$right = array_pop( $stack );
				$left = array_pop( $stack );
				$stack[] = $left + $right;
				break;
			case EXPR_MINUS:
				if ( count( $stack ) < 2 ) throw new ExprError('missing_operand', $this->names[$op]);
				$right = array_pop( $stack );
				$left = array_pop( $stack );
				$stack[] = $left - $right;
				break;
			case EXPR_AND:
				if ( count( $stack ) < 2 ) throw new ExprError('missing_operand', $this->names[$op]);
				$right = array_pop( $stack );
				$left = array_pop( $stack );
				$stack[] = ( $left && $right ) ? 1 : 0;
				break;
			case EXPR_OR:
				if ( count( $stack ) < 2 ) throw new ExprError('missing_operand', $this->names[$op]);
				$right = array_pop( $stack );
				$left = array_pop( $stack );
				$stack[] = ( $left || $right ) ? 1 : 0;
				break;
			case EXPR_EQUALITY:
				if ( count( $stack ) < 2 ) throw new ExprError('missing_operand', $this->names[$op]);
				$right = array_pop( $stack );
				$left = array_pop( $stack );
				$stack[] = ( $left == $right ) ? 1 : 0;
				break;
			case EXPR_NOT:
				if ( count( $stack ) < 1 ) throw new ExprError('missing_operand', $this->names[$op]);
				$arg = array_pop( $stack );
				$stack[] = (!$arg) ? 1 : 0;
				break;
			case EXPR_ROUND:
				if ( count( $stack ) < 2 ) throw new ExprError('missing_operand', $this->names[$op]);
				$digits = intval( array_pop( $stack ) );
				$value = array_pop( $stack );
				$stack[] = round( $value, $digits );
				break;
			case EXPR_LESS:
				if ( count( $stack ) < 2 ) throw new ExprError('missing_operand', $this->names[$op]);
				$right = array_pop( $stack );
				$left = array_pop( $stack );
				$stack[] = ( $left < $right ) ? 1 : 0;
				break;
			case EXPR_GREATER:
				if ( count( $stack ) < 2 ) throw new ExprError('missing_operand', $this->names[$op]);
				$right = array_pop( $stack );
				$left = array_pop( $stack );
				$stack[] = ( $left > $right ) ? 1 : 0;
				break;
			case EXPR_LESSEQ:
				if ( count( $stack ) < 2 ) throw new ExprError('missing_operand', $this->names[$op]);
				$right = array_pop( $stack );
				$left = array_pop( $stack );
				$stack[] = ( $left <= $right ) ? 1 : 0;
				break;
			case EXPR_GREATEREQ:
				if ( count( $stack ) < 2 ) throw new ExprError('missing_operand', $this->names[$op]);
				$right = array_pop( $stack );
				$left = array_pop( $stack );
				$stack[] = ( $left >= $right ) ? 1 : 0;
				break;
			case EXPR_NOTEQ:
				if ( count( $stack ) < 2 ) throw new ExprError('missing_operand', $this->names[$op]);
				$right = array_pop( $stack );
				$left = array_pop( $stack );
				$stack[] = ( $left != $right ) ? 1 : 0;
				break;
			case EXPR_ABS: 
 				if ( count( $stack ) < 1 ) throw new ExprError('missing_operand', $this->names[$op]); 
 				$arg = array_pop( $stack ); 
 				$stack[] = abs($arg); 
 				break; 
 			case EXPR_CEIL: 
 				if ( count( $stack ) < 1 ) throw new ExprError('missing_operand', $this->names[$op]); 
 				$arg = array_pop( $stack ); 
 				$stack[] = ceil($arg); 
 				break; 
 			case EXPR_FLOOR: 
 				if ( count( $stack ) < 1 ) throw new ExprError('missing_operand', $this->names[$op]); 
 				$arg = array_pop( $stack ); 
 				$stack[] = floor($arg); 
 				break; 
 			case EXPR_E: 
 				if ( count( $stack ) < 2 ) throw new ExprError('missing_operand', $this->names[$op]); 
 				$right = array_pop( $stack ); 
 				$left = array_pop( $stack ); 
 				// can never be NaN 
 				$stack[] = $left * pow(10,$right); 
 				break; 
 			case EXPR_POW: 
 				if ( count( $stack ) < 2 ) throw new ExprError('missing_operand', $this->names[$op]); 
 				$right = array_pop( $stack ); 
 				$left = array_pop( $stack ); 
 				$result = pow($left,$right); 
 				if (is_nan($result)) throw new ExprError('not_a_number', $this->names[$op]); 
 				$stack[] = $result; 
 				break; 
 			case EXPR_FLOAT_MOD: 
 				if ( count( $stack ) < 2 ) throw new ExprError('missing_operand', $this->names[$op]); 
 				$right = array_pop( $stack ); 
 				$left = array_pop( $stack ); 
 				if ( $right == 0 ) throw new ExprError('division_by_zero', $this->names[$op]); 
 				$stack[] = fmod($left,$right); 
 				break; 
 			case EXPR_SQRT: 
 				if ( count( $stack ) < 1 ) throw new ExprError('missing_operand', $this->names[$op]); 
 				$arg = array_pop( $stack ); 
 				$result = sqrt($arg); 
 				if (is_nan($result)) throw new ExprError('not_a_number', $this->names[$op]); 
 				$stack[] = $result; 
 				break; 
 			case EXPR_INT_DIVIDE: 
 				if ( count( $stack ) < 2 ) throw new ExprError('missing_operand', $this->names[$op]); 
 				$right = array_pop( $stack ); 
 				$left = array_pop( $stack ); 
 				if ( $right == 0 ) throw new ExprError('division_by_zero', $this->names[$op]); 
 				$stack[] = (int)($left / $right); 
 				break; 
			default:
				// Should be impossible to reach here.
				throw new ExprError('unknown_error');
		}
	}
}

[edit] ParserFunctions.php (extended)

<?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 (extended)',
	'url' => 'http://meta.wikimedia.org/wiki/ParserFunctions',
	'author' => 'Tim Starling, Carl Fürstenberg (AzaToth)',
	'description' => 'Enhance parser with mathematical and logical functions',
);
 
$wgHooks['LanguageGetMagic'][]       = 'wfParserFunctionsLanguageGetMagic';
 
class ExtParserFunctions {
	var $mExprParser;
	var $mTimeCache = array();
	var $mTimeChars = 0;
	var $mMaxTimeChars = 6000; # ~10 seconds
 
	function clearState() {
		$this->mTimeChars = 0;
		return true;
	}
 
	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 = '' ) {
		try {
			return $this->getExprParser()->doExpression( $expr );
		} catch(ExprError $e) {
			return $e->getMessage();
		}
	}
 
    function maxHook(&$parser) { 
                try {
 		$args = func_get_args(); 
         array_shift( $args ); 
 
         $exprParser = $this->getExprParser(); 
         foreach($args as $expr){ 
             $res = $exprParser->doExpression($expr); 
                 $result[] = $res; 
         }
         return max($result); 
         } catch(ExprError $e) {
			return $e->getMessage();
	 }
     } 
 
   function minHook(&$parser) { 
                try {
 		$args = func_get_args(); 
         array_shift( $args ); 
 
         $exprParser = $this->getExprParser(); 
         foreach($args as $expr){ 
             $res = $exprParser->doExpression($expr); 
                 $result[] = $res; 
         }
         return min($result); 
         } catch(ExprError $e) {
			return $e->getMessage();
	 }
     } 
 
	function ifexpr( &$parser, $expr = '', $then = '', $else = '' ) {
		try{
			if($this->getExprParser()->doExpression( $expr )) {
				return $then;
			} else {
				return $else;
			}
		} catch (ExprError $e){
			return $e->getMessage();
		}
	}
 
	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 '';
		}
	}
 
	/**
	 * Returns the absolute path to a subpage, relative to the current article
	 * title. Treats titles as slash-separated paths.
	 *
	 * Following subpage link syntax instead of standard path syntax, an 
	 * initial slash is treated as a relative path, and vice versa.
	 */
	public function rel2abs( &$parser , $to = '' , $from = '' ) {
 
		$from = trim($from);
		if( $from == '' ) {
			$from = $parser->mTitle->getPrefixedText();
		}
 
		$to = rtrim( $to , ' /' );
 
		// if we have an empty path, or just one containing a dot
		if( $to == '' || $to == '.' ) {
			return $from;
		}
 
		// if the path isn't relative
		if ( substr( $to , 0 , 1) != '/' &&
		 substr( $to , 0 , 2) != './' &&
		 substr( $to , 0 , 3) != '../' &&
		 $to != '..' )
		{
			$from = '';
		}
		// Make a long path, containing both, enclose it in /.../
		$fullPath = '/' . $from . '/' .  $to . '/';
 
		// remove redundant current path dots
		$fullPath = preg_replace( '!/(\./)+!', '/', $fullPath );
 
		// remove double slashes
		$fullPath = preg_replace( '!/{2,}!', '/', $fullPath );
 
		// remove the enclosing slashes now
		$fullPath = trim( $fullPath , '/' );
		$exploded = explode ( '/' , $fullPath );
		$newExploded = array();
 
		foreach ( $exploded as $current ) {
			if( $current == '..' ) { // removing one level
				if( !count( $newExploded ) ){
					// attempted to access a node above root node
					return wfMsgForContent( 'pfunc_rel2abs_invalid_depth', $fullPath );
				}
				// remove last level from the stack
				array_pop( $newExploded );
			} else {
				// add the current level to the stack
				$newExploded[] = $current;
			}
		}
 
		// we can now join it again
		return implode( '/' , $newExploded );
	}
 
	function ifexist( &$parser, $title = '', $then = '', $else = '' ) {
		$title = Title::newFromText( $title );
		return is_object( $title ) && $title->exists() ? $then : $else;
	}
 
	function time( &$parser, $format = '', $date = '' ) {
		global $wgContLang;
		if ( isset( $this->mTimeCache[$format][$date] ) ) {
			return $this->mTimeCache[$format][$date];
		}
 
		if ( $date !== '' ) {
			$unix = @strtotime( $date );
		} else {
			$unix = time();
		}
 
		if ( $unix == -1 || $unix == false ) {
			$result = wfMsgForContent( 'pfunc_time_error' );
		} else {
			$this->mTimeChars += strlen( $format );
			if ( $this->mTimeChars > $this->mMaxTimeChars ) {
				return wfMsgForContent( 'pfunc_time_too_long' );
			} else {
				$ts = wfTimestamp( TS_MW, $unix );
				if ( method_exists( $wgContLang, 'sprintfDate' ) ) {
					$result = $wgContLang->sprintfDate( $format, $ts );
				} else {
					if ( !class_exists( 'SprintfDateCompat' ) ) {
						require( dirname( __FILE__ ) . '/SprintfDateCompat.php' );
					}
 
					$result = SprintfDateCompat::sprintfDate( $format, $ts );
				}
			}
		}
		$this->mTimeCache[$format][$date] = $result;
		return $result;
	}
}
 
function wfSetupParserFunctions() {
	global $wgParser, $wgMessageCache, $wgExtParserFunctions, $wgMessageCache, $wgHooks;
 
	$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( 'time', array( &$wgExtParserFunctions, 'time' ) );
	$wgParser->setFunctionHook( 'max', array( &$wgExtParserFunctions, 'maxHook' ) );	 
 	$wgParser->setFunctionHook( 'min', array( &$wgExtParserFunctions, 'minHook' ) );	 
 	$wgParser->setFunctionHook( 'rel2abs', array( &$wgExtParserFunctions, 'rel2abs' ) );
 
	$wgMessageCache->addMessage( 'pfunc_time_error', "Error: invalid time" );
	$wgMessageCache->addMessage( 'pfunc_time_too_long', "Error: too many #time calls" );
	$wgMessageCache->addMessage( 'pfunc_rel2abs_invalid_depth', "Error: Invalid depth in path: \"$1\" (tried to access a node above the root node)" );
 
	$wgHooks['ParserClearState'][] = array( &$wgExtParserFunctions, 'clearState' );
}
 
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['time']    = array( 0, 'זמן',          'time' );
			$magicWords['rel2abs'] = array( 0, 'יחסי למוחלט',  'rel2abs' );
			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['time']    = array( 0, 'time' );
			$magicWords['max']     = array( 0, 'max' ); 
 			$magicWords['min']     = array( 0, 'min' ); 
			$magicWords['rel2abs'] = array( 0, 'rel2abs' );
	}
	return true;
}

[edit] See also

Personal tools