r102931 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r102930‎ | r102931 | r102932 >
Date:02:33, 14 November 2011
Author:danwe
Status:deferred
Tags:
Comment:
Version 2.0rc, complete rewrite, fixes several old bugs related to the fact that there might be several Parser instances at the same time. This update binds variables to parser instances rather than having one global variables store as before. Also introduces '#var_final' parser function. Put under ISC license.
Modified paths:
  • /trunk/extensions/Variables/COPYING (added) (history)
  • /trunk/extensions/Variables/RELEASE-NOTES (modified) (history)
  • /trunk/extensions/Variables/Variables.i18n.magic.php (modified) (history)
  • /trunk/extensions/Variables/Variables.php (modified) (history)
  • /trunk/extensions/Variables/Variables_Settings.php (added) (history)

Diff [purge]

Index: trunk/extensions/Variables/RELEASE-NOTES
@@ -1,10 +1,42 @@
2 - Post svn Changelog:
3 - ===================
 2+ svn era Changelog:
 3+ ==================
44
 5+ * (trunk) -- Version 2.0rc by Daniel Werner
 6+ Version 2.0 almost is a complete rewrite of the extension, just the idea remains the
 7+ same. It's the attempt to get rid of several bugs caused by the fact that MediaWiki
 8+ is using several Parser objects. Therefore in v2 each Parser has its own Variables
 9+ store, so nothing will get mixed up anymore. Full compatbility is given, except perhaps
 10+ in cases where bugs were used intentionally.
 11+
 12+ New features and bugfixes:
 13+ - Inclusion of special pages in the middle of the page won't reset all defined
 14+ variables anymore.
 15+ - Variables should now be compatible with all other extensions, except for those still
 16+ using Parser::parse() recursivelly in any way (which should never be done!).
 17+ - For MW 1.12 and later, '#var' default value no longer gets expanded when not needed.
 18+ - Experimental new function '#var_final' which allows to insert the variables final
 19+ (last) value.
 20+ - Global configuration variable '$egVariablesDisabledFunctions' added.
 21+
 22+ Internal changes:
 23+ - Parser class member $mExtVariables now contains an instance of ExtVariables where
 24+ only variables for that parser are getting stored. They won't be deleted by other
 25+ Parser actions anymore (e.g. special page inclusion doesn't reset variables anymore)
 26+ - ExtVariables class now has public functions which should be used by other extensions
 27+ for getting and setting variables information.
 28+ - Removed global '$wgExtVariables' variable.
 29+ - 'Variables_Settings.php' file for configuration settings added.
 30+
 31+ Others:
 32+ - Put under 'ISC License' (public domain before).
 33+ - In case you are using Extension:Loops, you should update it as well to remain compatbility.
 34+
 35+
536 * November 13, 2011 -- Version 1.4 by Daniel Werner
637 - Cleanup for use with more current MW versions:
738 + 'ParserFirstCallInit' hook in use and no more global extension functions.
839 + State of the Art internationalization files added.
 40+ - Dropped support for MW before 1.12
941 - 'ExtVariables::VARIABLES' constant with version info added.
1042 - Put into mediawiki.org svn, 'RELEASE-NOTES' and 'README' files added.
1143
@@ -24,6 +56,8 @@
2557
2658 * March 28, 2009 -- Version 1.2 by Daniel Werner
2759 - '#varexists' function introduced
 60+ - parameter for default value for '#var' function in case the variable doesn't exist
 61+ or its value is just an empty string.
2862
2963 * December 5, 2008 -- Version 1.1 by user 'Xiloynaha'
3064 - '#vardefineecho' function introduced
Index: trunk/extensions/Variables/COPYING
@@ -0,0 +1,16 @@
 2+Copyright (c) 2006 - 2007 by Rob Adams
 3+Copyright (c) 2007 by Tom Hempel
 4+Copyright (c) 2008 by Xiloynaha
 5+Copyright (c) 2009 - 2011 by Daniel Werner < danweetz@web.de >
 6+
 7+Permission to use, copy, modify, and/or distribute this software for any
 8+purpose with or without fee is hereby granted, provided that the above
 9+copyright notice and this permission notice appear in all copies.
 10+
 11+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 12+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 13+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 14+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 15+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 16+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 17+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
Property changes on: trunk/extensions/Variables/COPYING
___________________________________________________________________
Added: svn:eol-style
118 + native
Index: trunk/extensions/Variables/Variables.i18n.magic.php
@@ -15,6 +15,7 @@
1616
1717 $magicWords['en'] = array(
1818 'var' => array( 0, 'var' ),
 19+ 'var_final' => array( 0, 'var_final' ),
1920 'vardefine' => array( 0, 'vardefine' ),
2021 'vardefineecho' => array( 0, 'vardefineecho' ),
2122 'varexists' => array( 0, 'varexists' ),
Index: trunk/extensions/Variables/Variables.php
@@ -3,13 +3,16 @@
44 /**
55 * 'Variables' introduces parser functions for defining page-scoped variables within
66 * wiki pages.
 7+ * Version 2.0 is a major re-write of the extension by Daniel Werner, as attempt to
 8+ * get rid of any problems related to the fact that there are multiple Parser instances
 9+ * for which 'Variables' extension should bring separate variable stores.
710 *
811 * Documentation: http://www.mediawiki.org/wiki/Extension:Variables
912 * Support: http://www.mediawiki.org/wiki/Extension_talk:Variables
1013 * Source code: http://svn.wikimedia.org/viewvc/mediawiki/trunk/extensions/Variables
1114 *
12 - * @version: 1.4
13 - * @license: Public domain
 15+ * @version: 2.0rc
 16+ * @license: ISC License
1417 * @author: Rob Adams
1518 * @author: Tom Hempel
1619 * @author: Xiloynaha
@@ -17,11 +20,6 @@
1821 *
1922 * @file Variables.php
2023 * @ingroup Variables
21 - *
22 - * @ToDo:
23 - * FIXME: Fixing bugs related to the fact that there are several Parser instances within the wiki
24 - * which all could trigger 'ParserFirstCallInit'. E.g. special page transclusion will clear
25 - * all variables defined before! Variables should have one store per Parser instance.
2624 */
2725
2826 if ( ! defined( 'MEDIAWIKI' ) ) { die( ); }
@@ -35,18 +33,24 @@
3634 'url' => 'http://www.mediawiki.org/wiki/Extension:Variables',
3735 );
3836
39 -$dir = dirname( __FILE__ );
40 -
4137 // language files:
42 -$wgExtensionMessagesFiles['Variables' ] = $dir . '/Variables.i18n.php';
43 -$wgExtensionMessagesFiles['VariablesMagic'] = $dir . '/Variables.i18n.magic.php';
 38+$wgExtensionMessagesFiles['Variables' ] = ExtVariables::getDir() . '/Variables.i18n.php';
 39+$wgExtensionMessagesFiles['VariablesMagic'] = ExtVariables::getDir() . '/Variables.i18n.magic.php';
4440
45 -unset ( $dir );
46 -
4741 // hooks registration:
48 -$wgHooks['ParserFirstCallInit'][] = 'ExtVariables::init';
 42+$wgHooks['ParserFirstCallInit' ][] = 'ExtVariables::init';
 43+$wgHooks['ParserClearState' ][] = 'ExtVariables::onParserClearState';
 44+$wgHooks['InternalParseBeforeLinks'][] = 'ExtVariables::onInternalParseBeforeLinks';
4945
 46+// Include the settings file:
 47+require_once ExtVariables::getDir() . '/Variables_Settings.php';
5048
 49+
 50+/**
 51+ * Extension class with basic extension information. This class serves as static
 52+ * class with the static parser functions but also als variables store instance
 53+ * as object assigned to a Parser object.
 54+ */
5155 class ExtVariables {
5256
5357 /**
@@ -56,53 +60,323 @@
5761 *
5862 * @var string
5963 */
60 - const VERSION = '1.4';
 64+ const VERSION = '2.0rc';
6165
62 - var $mVariables = array();
 66+ /**
 67+ * Internal store for variable values
 68+ *
 69+ * @private
 70+ * @var array
 71+ */
 72+ var $mVariables = array();
6373
6474 /**
 75+ * Array with all names of variables requested by '#var_final'. Key of the values is the
 76+ * stripSateId of the strip-item placed where the final var should appear.
 77+ *
 78+ * @since 2.0
 79+ *
 80+ * @private
 81+ * @var Array
 82+ */
 83+ var $mFinalizedVars = array();
 84+
 85+ /**
 86+ * Variables extensions own private StripState manager to manage '#final_var' placeholders
 87+ * and their replacement with the final var value or a defined default.
 88+ *
 89+ * @since 2.0
 90+ *
 91+ * @private
 92+ * @var StripState
 93+ */
 94+ var $mFinalizedVarsStripState;
 95+
 96+ /**
6597 * Sets up parser functions
6698 *
6799 * @since 1.4
68100 */
69 - static function init( Parser $parser ) {
70 - global $wgExtVariables, $wgHooks;
71 -
72 - $wgExtVariables = new self();
73 - $wgHooks['ParserClearState'][] = $wgExtVariables; // hooks registration
74 -
75 - $parser->setFunctionHook( 'vardefine', array( &$wgExtVariables, 'vardefine' ) );
76 - $parser->setFunctionHook( 'vardefineecho', array( &$wgExtVariables, 'vardefineecho' ) );
77 - $parser->setFunctionHook( 'var', array( &$wgExtVariables, 'varf' ) );
78 - $parser->setFunctionHook( 'varexists', array( &$wgExtVariables, 'varexists' ) );
79 -
 101+ public static function init( Parser &$parser ) {
 102+
 103+ /*
 104+ * store for variables per parser object. This will solve several bugs related to
 105+ * 'ParserClearState' hook clearing all variables early in combination with certain
 106+ * other extensions. (since v2.0)
 107+ */
 108+ $parser->mExtVariables = new self();
 109+
 110+ // SFH_OBJECT_ARGS available since MW 1.12
 111+ self::initFunction( $parser, 'var', array( __CLASS__, 'pfObj_var' ), SFH_OBJECT_ARGS );
 112+ self::initFunction( $parser, 'var_final' );
 113+ self::initFunction( $parser, 'vardefine' );
 114+ self::initFunction( $parser, 'vardefineecho' );
 115+ self::initFunction( $parser, 'varexists' );
 116+
80117 return true;
81118 }
 119+ private static function initFunction( Parser &$parser, $name, $functionCallback = null, $flags = 0 ) {
 120+ if( $functionCallback === null ) {
 121+ // prefix parser functions with 'pf_'
 122+ $functionCallback = array( __CLASS__, 'pf_' . $name );
 123+ }
 124+ global $egVariablesDisabledFunctions;
 125+
 126+ // register function only if not disabled by configuration:
 127+ if( ! in_array( $name, $egVariablesDisabledFunctions ) ) {
 128+ $parser->setFunctionHook( $name, $functionCallback, $flags );
 129+ }
 130+ }
82131
83 - function onParserClearState( &$parser ) {
84 - $this->mVariables = array(); //remove all variables to avoid conflicts with job queue or Special:Import
85 - return true;
 132+ /**
 133+ * Returns the extensions base installation directory.
 134+ *
 135+ * @since 2.0
 136+ *
 137+ * @return boolean
 138+ */
 139+ public static function getDir() {
 140+ static $dir = null;
 141+
 142+ if( $dir === null ) {
 143+ $dir = dirname( __FILE__ );
 144+ }
 145+ return $dir;
86146 }
87147
88 - function vardefine( &$parser, $expr = '', $value = '' ) {
89 - $this->mVariables[ $expr ] = $value;
90 - return '';
 148+
 149+ ####################
 150+ # Parser Functions #
 151+ ####################
 152+
 153+ static function pf_varexists( Parser &$parser, $varName = '', $exists=true, $noexists=false ) {
 154+ if( self::get( $parser )->varExists( $varName ) ) {
 155+ return $exists;
 156+ } else {
 157+ return $noexists;
 158+ }
 159+ }
 160+
 161+ static function pf_vardefine( Parser &$parser, $varName = '', $value = '' ) {
 162+ self::get( $parser )->setVarValue( $varName, $value );
 163+ return '';
 164+ }
 165+
 166+ static function pf_vardefineecho( Parser &$parser, $varName = '', $value = '' ) {
 167+ self::get( $parser )->setVarValue( $varName, $value );
 168+ return $value;
 169+ }
 170+
 171+ static function pfObj_var( Parser &$parser, $frame, $args) {
 172+ $varName = trim( $frame->expand( $args[0] ) ); // first argument expanded already but lets do this anyway
 173+ $varVal = self::get( $parser )->getVarValue( $varName, null );
 174+
 175+ // default applies if var doesn't exist but also in case it is an empty string!
 176+ if( $varVal === null || $varVal === '' ) {
 177+ // only expand argument when needed:
 178+ $defaultVal = isset( $args[1] ) ? trim( $frame->expand( $args[1] ) ) : '';
 179+ return $defaultVal;
 180+ }
 181+ return $varVal;
 182+ }
 183+
 184+ static function pf_var_final( Parser &$parser, $varName, $defaultVal = '' ) {
 185+ $varStore = self::get( $parser );
 186+
 187+ return self::get( $parser )->requestFinalizedVar( $parser, $varName, $defaultVal );
91188 }
92189
93 - function vardefineecho( &$parser, $expr = '', $value = '' ) {
94 - $this->mVariables[ $expr ] = $value;
95 - return $value;
 190+
 191+ ##############
 192+ # Used Hooks #
 193+ ##############
 194+
 195+ static function onInternalParseBeforeLinks( Parser &$parser, &$text ) {
 196+
 197+ $varStore = self::get( $parser );
 198+
 199+ // only do this if '#var_final' was used
 200+ if( $varStore->mFinalizedVarsStripState === null ) {
 201+ return true;
 202+ }
 203+
 204+ /*
 205+ * all vars are final now, check whether requested vars can be inserted for '#final_var' or
 206+ * if the default has to be inserted. In any case, adjust the strip item value
 207+ */
 208+ foreach( $varStore->mFinalizedVars as $stripStateId => $varName ) {
 209+
 210+ $varVal = $varStore->getVarValue( $varName, '' );
 211+ if( $varVal !== '' ) {
 212+ // replace strip item value with final variables value or registered default:
 213+ //$varStore->mFinalizedVarsStripState->general->setPair( $stripStateId, $varVal );
 214+
 215+ $varStore->stripStatePair( $stripStateId, $varVal );
 216+ }
 217+ }
 218+
 219+ /**
 220+ * Unstrip all '#var_final' strip-markers with their final '#var' or default values.
 221+ * This HAS to be done here and can't be done thru the normal unstrip process of MW.
 222+ * This because the default value as well as the variables value stil have to be rendered properly since they
 223+ * may contain links or even category links. On the other hand, they can't be parsed with Parser::recursiveTagParse()
 224+ * since this would parse wiki templates and functions which are intended as normal text, kind of similar to
 225+ * returning a parser functions value with 'noparse' => true.
 226+ * Also, there is no way to expand the '#var_final' default value here, just if needed, since the output could be an
 227+ * entirely different, e.g. if variables are used.
 228+ * This method also takes care of recursive '#var_final' calls (within the default value) quite well.
 229+ */
 230+ $text = $varStore->mFinalizedVarsStripState->unstripGeneral( $text );
 231+
 232+ /*
 233+ * Sanitize the whole thing, otherwise HTML and JS code injection would be possible.
 234+ * Basically the same is happening in Parser::internalParse() right before 'InternalParseBeforeLinks' hook is called.
 235+ */
 236+ $text = Sanitizer::removeHTMLtags(
 237+ $text,
 238+ array( &$parser, 'attributeStripCallback' ),
 239+ false,
 240+ array_keys( $parser->mTransparentTagHooks )
 241+ );
 242+ return true;
96243 }
 244+
 245+ /**
 246+ * This will clean up the variables store after parsing has finished. It will prevent strange things to happen
 247+ * for example during import of several pages or job queue is running for multiple pages. In these cases variables
 248+ * would become some kind of superglobals, being passed from one page to the other.
 249+ */
 250+ static function onParserClearState( Parser &$parser ) {
 251+ /**
 252+ * MessageCaches Parser clone will mess things up if we don't reset the entire object.
 253+ * Only resetting the array would unset it in the original object as well! This instead
 254+ * will break the entire reference to the object
 255+ */
 256+ $parser->mExtVariables = new self();
 257+
 258+ return true;
 259+ }
97260
98 - function varf( &$parser, $expr = '', $defaultVal = '' ) {
99 - if ( isset( $this->mVariables[ $expr ] ) && $this->mVariables[ $expr ] !== '' ) {
100 - return $this->mVariables[ $expr ];
 261+
 262+ ##################
 263+ # Private Helper #
 264+ ##################
 265+
 266+ /**
 267+ * Takes care of setting a strip state pair in MW 1.18 as well as in previous versions
 268+ */
 269+ protected function stripStatePair( $marker, $value ) {
 270+ global $wgVersion;
 271+ if( version_compare( $wgVersion, '1.17.99', '>' ) ) {
 272+ // MW 1.18alpha+
 273+ $this->mFinalizedVarsStripState->addGeneral( $marker, $value );
101274 } else {
102 - return $defaultVal;
 275+ $this->mFinalizedVarsStripState->general->setPair( $marker, $value );
103276 }
104277 }
105278
106 - function varexists( &$parser, $expr = '' ) {
107 - return array_key_exists( $expr, $this->mVariables );
 279+
 280+ ####################################
 281+ # Public functions for interaction #
 282+ ####################################
 283+ #
 284+ # public non-parser functions, accessible for
 285+ # other extensions doing interactive stuff
 286+ # with variables (like Extension:Loops)
 287+ #
 288+
 289+ /**
 290+ * Convenience function to return the variables extensions variables store connected
 291+ * to a certain Parser object. Each parser has its own store which will be reset after
 292+ * a parsing process [Parser::parse()] has finished.
 293+ *
 294+ * @param Parser &$parser
 295+ *
 296+ * @return ExtVariables by reference so we still have the right objecdt after 'ParserClearState'
 297+ */
 298+ public static function &get( Parser &$parser ) {
 299+ return $parser->mExtVariables;
108300 }
 301+
 302+ /**
 303+ * Defines a variable, accessible by getVarValue() or '#var' parser function
 304+ *
 305+ * @param string $varName
 306+ * @param string $value will be converted to string if no string is given
 307+ */
 308+ public function setVarValue( $varName, $value = '' ) {
 309+ $varName = trim( $varName );
 310+ $value = trim( $value );
 311+ $this->mVariables[ $varName ] = $value;
 312+ }
 313+
 314+ /**
 315+ * Returns a variables value or null if it doesn't exist.
 316+ *
 317+ * @param string $varName
 318+ * @param mixed $defaultVal
 319+ *
 320+ * @return string or mixed in case $defaultVal is being returned and not of type string
 321+ */
 322+ public function getVarValue( $varName, $defaultVal = null ) {
 323+ $varName = trim( $varName );
 324+ if ( $this->varExists( $varName ) ) {
 325+ return $this->mVariables[ $varName ];
 326+ } else {
 327+ return $defaultVal;
 328+ }
 329+ }
 330+
 331+ /**
 332+ * Checks whether a variable exists within the scope.
 333+ *
 334+ * @param string $varName
 335+ *
 336+ * @return boolean
 337+ */
 338+ public function varExists( $varName ) {
 339+ $varName = trim( $varName );
 340+ return array_key_exists( $varName, $this->mVariables );
 341+ }
 342+
 343+ /**
 344+ * Allows to unset a certain variable
 345+ *
 346+ * @param type $varName
 347+ */
 348+ public function unsetVar( $varName ) {
 349+ unset( $this->mVariables[ $varName ] );
 350+ }
 351+
 352+ /**
 353+ * Allows to register the usage of '#var_final'. Meaning a variable can be set as well
 354+ * as a default value. The return value, a strip-item then can be inserted into any
 355+ * wikitext processed by the same parser. Later that strip-item will be replaced with
 356+ * the final var text.
 357+ * Note: It's not possible to use the returned strip-item within other stripped text
 358+ * since 'Variables' unstripping will happen before the general unstripping!
 359+ *
 360+ * @param Parser $parser
 361+ * @param string $varName
 362+ * @param string $defaultVal
 363+ * @return string strip-item
 364+ */
 365+ function requestFinalizedVar( Parser &$parser, $varName, $defaultVal = '' ) {
 366+ if( $this->mFinalizedVarsStripState === null ) {
 367+ $this->mFinalizedVarsStripState = new StripState( $parser->mUniqPrefix );
 368+ }
 369+ $id = count( $this->mFinalizedVars );
 370+ /*
 371+ * strip-item which will be unstripped in self::onInternalParseBeforeLinks()
 372+ * In case the requested final variable has a value in the end, this strip-item
 373+ * value will be replaced with that value before unstripping.
 374+ */
 375+ $rnd = "{$parser->mUniqPrefix}-finalizedvar-{$id}-" . Parser::MARKER_SUFFIX;
 376+
 377+ $this->stripStatePair( $rnd, trim( $defaultVal ) );
 378+ $this->mFinalizedVars[ $rnd ] = trim( $varName );
 379+
 380+ return $rnd;
 381+ }
 382+
109383 }
Index: trunk/extensions/Variables/Variables_Settings.php
@@ -0,0 +1,29 @@
 2+<?php
 3+
 4+/**
 5+ * File defining the settings for the 'Variables' extension.
 6+ * More info can be found at http://www.mediawiki.org/wiki/Extension:Variables#Configuration
 7+ *
 8+ * NOTICE:
 9+ * =======
 10+ * Changing one of these settings can be done by copying and placing
 11+ * it in LocalSettings.php, AFTER the inclusion of 'Variables'.
 12+ *
 13+ * @file Variables_Settings.php
 14+ * @ingroup Variables
 15+ * @since 2.0
 16+ *
 17+ * @author Daniel Werner
 18+ */
 19+
 20+/**
 21+ * Allows to define functions which should not be available within the wiki.
 22+ *
 23+ * @example
 24+ * # disable '#var_final' and '#vardefineecho' functions:
 25+ * $egVariablesDisabledFunctions = array( 'var_final', 'vardefineecho' );
 26+ *
 27+ * @since 2.0
 28+ * @var array
 29+ */
 30+$egVariablesDisabledFunctions = array();
Property changes on: trunk/extensions/Variables/Variables_Settings.php
___________________________________________________________________
Added: svn:eol-style
131 + native

Status & tagging log