Wikimedia Forum/Archives/2008-11/GlobalContextVariables Extension

From Meta, a Wikimedia project coordination wiki

< return to Wikimedia Forum

Global Context Variables: Proposal and Discussions

Global Context Variables: Initial proposal[edit]

Initial proposals in French and English

Global Context Variables: French original (don't modify)

Please don't reply in the middle of this original text frame. You may insert your comments (in any language) or fix translation typos in the English section below.

Je propose de poster une spécification d'extension pour MediaWiki de façon à supporter ces variables de contexte, et je vais l'écrire sur Méta. Comme je ne connais rien à la façon d'écrire une telle extension, même si j'ai lu un peu de code source PHP de certaines, j'attendrai que quelqu'un qui s'y connait mieux l'implémente.

En revanche je voudrais documenter les exemples d'emploi et l'intérêt que cela aurait, notamment pour les Wiktionnaires qui gagneraient énormément à avoir ces variables de contexte.

Par exemple on n'aurait plus à préciser partout un paramètre de langue dans les appels de modèle, si la valeur de ce paramètre pouvait être lue directement dans une variable de contexte (au lieu d'un paramètre explicite à passer chaque fois) dont la valeur a été positionnée par un moddèle comme {{=fr=}} (sur le Wiktionnaire français) qui définirait la variable de contexte suivante : {{#v:set:lang|fr}} utilisée ensuite avec {{#v:get:lang}} ou modifiée et utilisée sur place avec {{v:get:lang|fr}}

Un raccourci possible, en supposant que l'action "get" soit celle par défaut : {{#v::lang}} pour lire et {{#v::lang|fr}} pour modifier et utiliser immédiatement.

Toujours dans cette extension, que je nommerais GlobalContextVariables et utilisant la syntaxe générale {{#v:action:variable|paramètres optionnels}}, les variables pourraient être des listes ou tableaux indicés, et gérées chacune comme une pile (dont on n'utilise par défaut que la valeur au sommet, en position 0 du tableau indicé ou de la liste, et les actions push et pop seraient définies pour modifier ces variables en permettant de les faire se comporter comme des variables locales de contexte.

Concernant les noms de variables, ils seraient restreint à des identifiants valides commençant par une lettre et faits de lettres ou chiffres, les autres caractères (notamment de ponctuation) restant réservés pour gérer des espaces de noms par exemple (on utiliserait le point "." ou ":" comme séparateur d'espace), ou pour lire des variables de contexte internes au reste de la MediaWiki si celui-ci définit des espaces de noms spéciaux pour ses propres extensions. En revanche il n'y aurait pas de limitation dans la valeur des variables (tout ce qu'on écrit en syntaxe wiki, y compris les appels de sous-modèles qui seraient évalués normalement, serait possible).

Quel intérêt pour nous ?

  • Il est évident : on gagnerait à avoir une variable de contexte contenant la langue de la section courante (ici on utiliserait la valeur obtenue par {{#v::lang}} (l'action par défaut, non indiquée entre les deux ":", est "get" pour lire la variable de contexte nommée ici "lang".
  • Le choix du préfixe {{#v: }} (qui peut aussi être {{#var: }} ou même seulement {{#: }} !) reste à définir avec l'équipe de méta où cette proposition d'extension pour MediaWiki pourrait être proposée, je nomme cette extension à l'avance "GlobalContextVariables".
  • Cela allégerait énormément l'utilisation de nos modèles, en n'ayant plus à passer le paramètre de langue partout, et en permettant de définir en une seul fois, au début de la section de langue, la clé de tri qu'utilise cette langue pour les catégorisations faites ensuite par les sous-modèles utilisés dans la section de langue.
  • Comme les variables de contexte sont modifiables, on peut les utiliser pour stocker un résultat intermédiare de calcul (par exemple pour faire des sommes dans un tableau, le total étant calculé et affiché en bas de tableau. Utilise pour simplifier l'édition des tableaux complexes de données sur Wikipédia.
  • On pourrait définir une variable de contexte {{#v::clédetri}}, définie en début de section de langue avec ou juste après {{=fr=}} (sur le Wiktionnaire français) par exemple, et contenant la clé de tri propre à cette langue, et les sous-modèles dans la section n'ont alors qu'à utiliser la valeur de cette variable de contexte pour utiliser la bonne clé de tri pour les catégories par langue que ces modèles insèrent automatiquement.
  • En terme de ressources sur le serveur on gagne énormément sur certaines pages car on peut stocker dans une variable de contexte l'évaluation d'un appel de sous-modèle pour éviter d'avoir à l'inclure et l'étendre à chaque appel. Les pages contenant pleins d'appels répétés à un modèle paramétré complexe sont alors simplifiées et évaluées bien plus vite puique le serveur ne fait le travail d'expansion qu'une seule fois, à l'endroit où la variable de contexte est définie.
  • En terme technique, le bloc des variables de contexte n'est rien d'autre qu'un tableau normal en PHP, indexé par le nom de la variable. Ce tableau est transmis en référence implicite lors de l'évaluation de tous les sous-modèles, sans qu'il soit nécessaire de le passer en paramètre explicite. (il faut que cette évaluation soit garantie pour se faire de haut en bas et pas dans le désordre, je ne suis plus sûr que ce soit le cas actuellement car le moteur de templates a été largement modifié et remanié avec des caches internes, l'ordre d'expansion n'étant peut-être plus garanti comme se faisant dans l'ordre, du début de lapage vers la fin).
  • Cela fait plein de paramètres embêtants à répéter dans le Wiktionnaire qui deviennent inutiles, et on gagne en cohérence globale, et en possibilité d'automatisation et de vérification ou d'insertion automatique par des robots.
  • etc...

En principe on commence par une spécification avant d'écrire le code. Elle doit être suffisamment générale pour être extensible, mais pas trop compliquée à utiliser pour les cas simples comme le notre.

Qu'en pensez vous ? Il serait bon de discuter ensemble de ce qu'on veut en faire, et de ce que l'extension pour MediaWiki devrait gérer (en garder en tête le fait que des extensions sont possibles pour d'autres utilisations hors du Wiktionnaire, y compris sur Wikipédia, Commons, Wikispecies où je vois pleins de possibilités, ou des wikis hébergés hors de la Fondation Wikimedia.

Je propose de poster cette discussion aussi sur Meta (si vous voulez vous pouvez y faire un tour et faire référence à ce message qui est une première ébauche de proposition d'extension.

verdy_p 12 novembre 2008 à 18:26 (UTC)

Je pense que ce serait utile, mais je doute fort de l'accueil : il y a une aversion constante, à juste titre, vers tout ce qui risque de réserver peu à peu l'édition sur Wikipedia (et autres projets) à une "élite" technique.
Mais ce serait peut-être mieux accepté si c'était limité à des variables standards connues du logiciel (par exemple une variable collation_order pouvant être mise dans les catégories pour choisir l'ordre de tri, comme dit dans la discussion précédente, en permettant de supprimer toutes les clés de tri). Lmaltier 12 novembre 2008 à 18:42 (UTC)
Tu noteras que justement, en évitant d'avoir à passer des tas de paramètres inutiles, cela rendrait globalement l'édition des articles bien plus facile. Les variables de contexte seraient en fait définies et utilisées en interne dans nos modèles existants ! Ca donne un code Wiki bien plus élégant, plus court, et plus facile à manipuler et plus compréhensible (y compris au travers des imports de données d'un wiki à l'autre).
Cela n'empêchera pas qu'il faut déjà être expert pour définir nos modèles actuels qui sont d'une complexité croissante justement parce qu'ils n'ont pas accès simplement au contexte de leur utilisation, autrement que par des paramètres explicites et des tas de tests... verdy_p 12 novembre 2008 à 18:49 (UTC)
D'autre part, ces variables de contexte peuvent simplifier énormément l'écriture des modèles complexes actuels, si on peut éviter d'avoir à référencer les valeurs de paramètres et les valeurs par défaut données, chaque fois avec une syntaxe très complexe qu'il faut répéter à chaque utilisation, alors qu'il suffirait de définir une variable de contexte au début du modèle, et utiliser la valeur stockée dans cette variable de contexte ensuite dans tout le reste du modèle ! Les modèles complexes où s'enchevètrent plein d'accolades imbriquées et de #if deviennent alors beaucoup plus faciles à écrire, lire, comprendre et maintenir (il n'est plus nécessaire de faire ces imbrications et de compter les acollades, sources de plein d'erreurs ! verdy_p 12 novembre 2008 à 18:58 (UTC)
Ces arguments peuvent être employés, on peut toujours essayer, mais je ne parlais pas du cas particulier du Wiktionnaire, mais en général. Lmaltier 12 novembre 2008 à 19:03 (UTC)
Justement je parle d'une façon générale: les modèles actuels dans tous les wikis sont d'une complexité croissante à cause justement de l'absence de telles variables qui les simplifierait énormément, y compris sur Wikipédia, Commons, WikiSpecies, WikiNews, WikiBooks... verdy_p 12 novembre 2008 à 19:11 (UTC)
Sûr (je n'ai lu que les premières et dernières lignes de ton message, je m'en excuse) mais les développeurs de WikiMedia sont contre l'installation de cette extension. Alors : fais-le !, mais ne te fais pas d'illusion. --Szyx 12 novembre 2008 à 19:06 (UTC)
Pour les variables purement locales à un modèle, une convention de nommage de ces variables de contexte (renforcée par l'extension elle-même) suffirait pour indiquer que ces variables ne sont pas passées en retour ni durant l'évaluation des sous-modèles utilisés, par exemple des variables locales dont le nom commence par "$" ou "_". Cela donnerait {{#v:set:_x|expression complexe}} au début du modèle pour définir la variable locale nommée ici "_x", puis ensuite de {{#v::_x}} pour utiliser ensuite la valeur déjà définie. verdy_p 12 novembre 2008 à 18:58 (UTC)
Et plus j'y pense et plus je suis persuadé que cette extension conduit à du code élégant et facile à maintenir et comprendre par plus de monde que les modèles actuels sur TOUS les projets Wikimedia ! verdy_p 12 novembre 2008 à 19:13 (UTC)

Global Context Variables: English translation

I propose to post a specification for a MediaWiki extension in order to support context variables, and I will write it on Meta. As I don't know much about the way to write such an extension, even if I have read some PHP code for some of these extensions, I will wait for someone better qualified to implement it.

However, I'd like to document some exampels of use and the interest about what such extension would provide, notably for the Wiktionaries that would gain a lot if they could use those context variables. But the extension is not specifically tuned to focus Wiktionary.

For example, we would no longer have to pass everywhere a language parameter when calling Wiktionary templates, if the value of this parameter could be read directly from a context variable (instead of an explicit parameter to pass each time) whose value would have been preset in a former call to another template like {{=fr=}} (on French Wiktionary) that would internally define the value of the following context variable: {{#v:set:lang|fr}}, then used with {{#v:get:lang}}, or (optionally) modified and reused directly in place with {{v:get:lang|fr}}

A possible shortcut, supposing that the action "get" is the one used by default for this extension : {{#v::lang}} to read the context variable named "lang" and {{#v::lang|fr}} to modify and use immediately the value set in this variable.

Still in this extension, that I would name GlobalContextVariables and using the general syntax {{#v:action:variable

Global Context Variables: Analysis and Discussions[edit]

Analysis and Discussions

Global Context Variables: The problem and why we need absolutely need this extension for large multilingual projects

  • The problem has been exposed about the problem of sorting specifically the articles (in the French Wiktionary, but I think it applies to all existing projects using the same architecture principles, including all Wiktionary editions) related to words that have an orthography shared by multiple languages exposed in the same page:
  • It is currently impossible (in the MediaWiki syntax currently supported) to generate more than one default sort key at end of the article, so that the grammatical subtemplates used in those multilanguages pages, will be able to select automatically the appropriate sort key to use when they are autocategorizing the article into a category specific to some language (e.g. Esperanto).
  • (Note that this problem also affects critically the Esperanto Wiktionary, that currently cannot cope correctly with other languages than Esperanto itself, and still manage, without extreme complications (for contributors), the language-specific categories, and expect that these categories will be sorted correctly without using the default Esperanto sort order. The same will be true for the Spanish, Catalan, Dutch or Swedish Wiktionaries that would like their own default tailored collation for their own language but without affecting the other languages they are exposing.)
  • The only possible solution, for now, would require an extension of all grammatical subtemplates that are performing autocategorization of words related to some language. These templates already have a language code parameter, but nothing is present to support the addition of another parameter to specify the sort key.
  • If those grammatical templates were extended to support additional parameters for the sort key that they must use, this will make the articles too complicate to edit (having to repeat the language code is already very harassing and error-prone).
  • Everything would be much simpler if the autocategorizing grammatical subtemplates were able to know in which context they are used. But there's absolutely no way to manage a context in our pages, outside of the template parameters.
  • So templates tend to become more and more complicate to use by contributors (and more complicate to maintain if they have to handle more parameters with their default values).
  • All what we need is a way to store, in a single place (notably within a template generating the title of the language section) the sort key associated to the article name for that language. This would require a single context variable that other subtemplates wouldjust have to read, after it has been set once.
  • Because the language code, the sort key, and so on will be already specified only once in the page (most probably by using a single utility template, to hide the technical details to most unaware contributors), and stored in a single context variable for this function (the language code, the sort key and so on) : all other templates used further down in the page will no longer need to have a specific parameter, that need to be specified each time on their invokation, and repeating the correct value needed for that language. This will simplify a lot the work on pages (notably in Wiktionary where most pages are multingual, especially those whose title use the Latin script.
  • Now consider what we are already doing elsewhere, outside Wiktionary, notably in Wikipedia and Commons : those extra template parameters that must be repeated at each invokation of the templates are a nightmare for editors: if they are forgotten, the default value that they use internally will generate the wrong code, and articles will be incorrectly categorized or will display the wrong information.
  • In addition, because parameters are not modifiable, the code to manage their default values tend to become extremely ugly (but they are still unable to provide another default behavior according to the context of use), when the number of combinations they must test and support is increased. This is one of the most important source of our too complex utility templates (those for general use in lots of pages), and this requires more ressources on the server to support those templates when generating pages. Many of those templates tend to have too many parameters, whose number and complexity tend to increase over time.

Let's solve the problem cleanly for the immediate future : please offer support for context variables to our pages. The current situation is not maintainable for the long medium term, and it already affects (in short term) large mature multilingual projects like Wiktionary and Commons (and Meta itself for the multilingual MediaWiki documentation or support). verdy_p 20:15, 13 November 2008 (UTC)[reply]

Global Context Variables: Preliminary analysis about complexity and cost on the server

You will immediately see that, in terms of complexity and memory cost on the server, the cost is transfered from the size in meory of the template parameters, to the size of the values stored in the array of context variables.

However, this is mitigated by the fact that the variables will store the result of the expression assigned to them, for cases where contect variables are needed: to store the result of complex expressions full of parameters that will finally not be used, and with the code of many #if and #switch case branches. The concept here is to have a simple value computed once, and then reused without having to worry about how they were computed or set (though subtemplates, or complex tests of default values, missing parameters, default values, and so on).

Reusing an already assigned context variable named "x" is just {{#v::x}} instead of {{{x}}} or more complex expressions where there are tests to see if the parameter is defined, or to set its default value. This does not make the code much more complicate to read, but this extension avoids the complexity of the many embedded braces.

It becomes possible to define the value to use for a variable by successive uses of {{#v:set:x}} instead of embedding them within complexly paired collections of {braces} (and no more need to count them, and much less confusion between the syntax of parameter expansion and template references (3 or 2 braces ?), because, in most cases, the "embedding style" can be avoided.

verdy_p 21:38, 12 November 2008 (UTC)[reply]

Global Context Variables: Monitoring and capping the memory size requirement on the server

In terms of memory space complexity, this means that the parameter sizes (given to the ParserFunction evaluating this MediaWiki extension) will be shorter than with the existing template parameter calls (no more need to use separate internal subtemplates to "factorize" the common code) or existing uses with embedded #if, #expr, and #switch...

What the server just need to monitor, is the total size of the values that are curerntly assigned in the current block of context variables, in addition to the separate total size of template parameters. I also think that the extension is quite simple to implement, and clear for users. It can also save a lot of new "magic" words, specific for some extensions (for example, localization templates).

It would be interesting to test it on a Test wiki containing a partial copy of Wikipedia using complex templates (for example those computing date elements, and selecting subpages to render within an article, depending on some magic value or explicit parameter specific to the page using it. It has already been seen that the most complex templates are those using #expr, or performing many #switch, whose case branches need to be maintained separately, despite they need to reuse the same complex expression.

verdy_p 21:38, 12 November 2008 (UTC)[reply]

Global Context Variables: Much less complexity for authors of complex templates

My proposal would simplify a lot those templates, and would reduce their total size a lot. In addition, it will be much more easier to edit them without so many errors as they exist today. Currently the horrors found in code are coming from the need to embed the #if's, and impossibility to reuse the values determined by those tests outside of the #if itself. This is really ugly, and this also results in a severe limitation for the expansion, if many embedded #if are then needed. Using context variables, you can just use successive #if's instead to compute the same value.

And most of the reusable code is written only in one place: where the variable is first set, but is still modifiable later in another separate #if. This simplifies, each time, the code to write to handle the default values and specific cases or exceptions controled by other template parameters (no need to duplicate the existing code to specialize it). Just insert a code using a context variable to modify it in situ.

verdy_p 21:38, 12 November 2008 (UTC)[reply]

Global Context Variables: The data/view separation principle in action

Finally this also allows the separation between the code that prepares the data, and the code that renders it (this is a good principle, including for HTML, just like with the separation of page content, and style in CSS): this is the document/view separation principle. Prepare the data in context variables, then render the computed variables directly without complication.

Clean and simple to understand. verdy_p 22:09, 12 November 2008 (UTC)[reply]

Global Context Variables: Interest for the collaboration across wikis

This extension will also facilitate a lot the reuse of code fragments across templates, and easier adaptation of currently too complex templates that need to be imported and adapted on another project. The code being more "linear" (no many embedded braces), it is simpler to fix in one place for such adaptation.

In other words, it will facilitate the collaboration across wikis, so that wikis with less experimented admins can more easily work on these templates when importing the useful (but complex) templates used on English Wikipedia or on Commons.

verdy_p 21:47, 12 November 2008 (UTC)[reply]

Global Context Variables: Scope of projects using this extension

I want to precise that it is not absolutely necessary to extend the scope of projets using this extension to all wikis using MediaWiki. What I propose is an optional extension, a ParserFunction, that acn be activated on selected projects that would benefit the most from it, with less dangers of misusing it.

In addition, some other restrictions for the extension itself could be enforced. Wiktionaries are such projects that would most probably use it first, but it may be experimented first on a Multilangual project like Meta or WikiSpecies (where the use of context variables would facilitate the edition of pages, that are built essentially using templates).

And I think that other external (less popular but better controlled) non-Wikimedia projects would like to have it and experiment it as well, and see the benefit they can take from it (notably for closed wikis that are publishing lots of data with automated scripts and complex data organisation, without suffering the burden of the required template parameters and of the too complex wiki code in their templates.

Global Context Variables: Implementation Summary[edit]

My opinion is that this extension is very simple to implement, because all the infrastructure is already there: the PHP code just need to be able to instanciate a single variable containining an empty associative array at the begining of analysis for storing the context variables that the ParserFunction will be able to reference to store the context variables and their values.

The ParserFunction code itself is quite simple to write: it just has to parse the parameters and take the indicated action, and use the single variable instanciated at the begining of the page. The ParserFunction will return the value expected by "get", or nothing for "set", and it will be alone to store and retreive data in this unique PHP array variable.

At end of parsing a page, the context variables block has no more use and can be dropped, just like all other PHP variables used in Mediawiki for parsing and generating a single page. I don't think that this has any implication elsewhere in the existing MediaWiki code, because it will use the existing mechanism for ParserFunctions, but I may be wrong (there are certainly lots of tricks in MediaWiki to handle special cases in some parser functions and their interaction with other components of MediaWiki or with other ParserFunctions). verdy_p 16:58, 14 November 2008 (UTC)[reply]

Global Context Variables: Implementation in PHP (alpha version)[edit]

This is just an alpha source code, to be fully tested and debugged. Note: the syntax implemented here varies a little compared to the discussion above as it uses:

{{#v:action|variable|optional value}} instead of:
{{#v:action:variable|optional value}}.

It currently does not implement any policy (except then trying to monitor and cap the memory needed for storing strings), does not enforce any naming convention. It implements some additional (very basic) non essential actions.

ContextVars.php
<?php
/**
 * Enhance parser with variables storing contextual information.
 *
 * @addtogroup Extensions
 * @author Philippe Verdy
 * LICENCE 2008-11-13: This source is licenced under the same terms as those
 * for the MediaWiki software source files package of the Wikimedia Foundation. 
 */
if (!defined('MEDIAWIKI')) {
    die('This file is an extension to the MediaWiki software and cannot be used standalone.\n');
}

$wgExtensionFunctions[] = 'wfSetupContextVars';
$wgExtensionCredits['parserhook'][] = array(
    'name' => 'ContextVariables',
    'version' => '0.9',
    'url' => 'http://meta.wikimedia.org/wiki/ContextVars',
    'author' => 'Philippe Verdy',
    'description' =>
'A function that allows pages and templates to store values in reusable variables' .
' that will be set in one place to store contextual information, and whose value' .
' can then be retrieved and reused on the rest of the Wiki code that follows.',
    'descriptionmsg' => 'contextVars_desc',
);
$wgExtensionMessagesFiles['ContextVars'] = dirname(__FILE__) . '/ContextVars.i18n.php';
$wgHooks['LanguageGetMagic'][] = 'wfContextVarsLanguageGetMagic';
$wgParserTestFiles[] = dirname(__FILE__) . '/funcsContextVars.txt';

// Define as 0 if Total Size not monitored
define(EXTCONTEXTVARS_MAX_TOTAL_SIZE, 0x40000); // 256 kibibytes should be enough for storing context info

// Load all internationalizable magic words (for functions or actions) from <ContextVars.i18n.magic.php>
function wfContextVarsLanguageGetMagic(&$magicWords, $langCode, $actions = 0) {
    require_once(dirname(__FILE__) . '/ContextVars.i18n.magic.php');
    $efWords = $actions ? efContextVarsActionWords($langCode) : efContextVarsWords($langCode);
    foreach($efWords as $magicWord => $magicTrans)
        $magicWords[$magicWord] = $magicTrans;
    return true;
}

// Token types for functions
define(EXTCONTEXTVARS_V,          0); // essential function
// Token types for actions
define(EXTCONTEXTVARS_GET,        1); // essential action
define(EXTCONTEXTVARS_SET,        2); // essential action, to monitor and cap
define(EXTCONTEXTVARS_UNSET,      3); // useful action, should be monitored
define(EXTCONTEXTVARS_ISSET,      4); // useful action
define(EXTCONTEXTVARS_ISENABLED,  5); // optional utility action, using 'get' action
define(EXTCONTEXTVARS_ISDISABLED, 6); // optional utility action, using 'get' action
define(EXTCONTEXTVARS_ENABLE,     7); // optional utility action, using 'set' action
define(EXTCONTEXTVARS_DISABLE,    8); // optional utility action, using 'set' action

// Extension declaration and loading
function wfSetupContextVars() {
    global $wgParser, $wgExtContextVars, $wgHooks;

    $wgExtContextVars = new ExtContextVars;
    // Check for SFH_OBJECT_ARGS capability
    if (defined('MW_SUPPORTS_PARSERFIRSTCALLINIT')) {
        $wgHooks['ParserFirstCallInit'][] = array(&$wgExtContextVars, 'registerParser');
    } else {
        if (class_exists('StubObject') && !StubObject::isRealObject($wgParser)) {
            $wgParser->_unstub();
        }
        $wgExtContextVars->registerParser($wgParser);
    }
    $wgHooks['ContextVarsClearState'][] = array(&$wgExtContextVars, 'clearState');
}

// Builds an internationalizable message by delayed (on demand) loading of <ContextVars.i18n.php>
class ExtContextVarsError extends Exception {
    public function __construct($msg, $parameter = '') {
        wfLoadExtensionMessages('ContextVars');
        $this->message = wfMsgForContent("contextvars_$msg", htmlspecialchars($parameter));
    }
}

// Main class
class ExtContextVars {
    // The list of context variables that have been set.
    var $mContextVars = array();
    // Basic monitoring and capping of total storage size.
    var $mTotalSize = 0;

    function getActionTokens($langCode) {
        var $words = array();
        wfContextVarsLanguageGetMagic(&$words, $langCode, 1);
        // assign token ids to action names and translations
        var $actionTokens = array();
        function mapActions(&$actionTokens, $words, $token) {
            foreach($words as $word) {
                if ($word !== 0) $actionTokens[$word] = $token;
            }
        }
        mapActions($actionTokens, 'get'       , EXTCONTEXTVARS_GET);
        mapActions($actionTokens, 'set'       , EXTCONTEXTVARS_SET);
        mapActions($actionTokens, 'unset'     , EXTCONTEXTVARS_UNSET);
        mapActions($actionTokens, 'isset'     , EXTCONTEXTVARS_ISSET);
        mapActions($actionTokens, 'isenabled' , EXTCONTEXTVARS_ISENABLED);
        mapActions($actionTokens, 'isdisabled', EXTCONTEXTVARS_ISDISABLED);
        mapActions($actionTokens, 'enable'    , EXTCONTEXTVARS_ENABLE);
        mapActions($actionTokens, 'disable'   , EXTCONTEXTVARS_DISABLE);
        return $actionTokens;
    }
    var $actionTokens = getActionTokens('en');
    
    // Register parser function hooks
    function registerParser(&$parser) {
        if (defined(get_class($parser) . '::SFH_OBJECT_ARGS')) {
            // These functions accept DOM-style arguments
            $parser->setFunctionHook('v', array(&$this, 'vObj'), SFH_OBJECT_ARGS);
        } else {
            $parser->setFunctionHook('v', array(&$this, 'vHook'));
        }
        return true;
    }

    function clearState(&$parser) {
        $this->mContextVars = array();
        // Basic monitoring and capping of total storage size.
        $this->mTotalSize = 0;
        return true;
    }

    // Perform 'get' action: Get and return the value stored in the named context variable.
    // Also used by 'isenabled' and 'isdisabled' actions (using implicit default values).
    // Return the optional default value if this context variable is not set.
    // Throws an error if no valid variable name is given or variable is not set and no default value given.
    function vGet($action, $var, $value) {
        if (!isset($var) || $var == '')
            throw new ExtContextVarsError('var_name_required_for', $action);
        if (!isset($this->mContextVars[$var])) {
            if (!isset($value))
                throw new ExtContextVarsError('value_needed_for_var', $var);
            // Remove HTML-style comments
            // Not necessary in versions where StringUtils::delimiterReplace didn't exist
            if (is_callable(array('StringUtils', 'delimiterReplace'))) {
              $value = StringUtils::delimiterReplace('<'.'!--', '--'.'>', '', $value);
            }
            return $value; // default value from parameter
        } else {
            return $mContextVars[$var];
        }
    }

    // Perform 'set' action: Set the value of the named context variable, and return this value.
    // Also used by 'enable' and 'disable' actions (using implicit values).
    // Throws an error if no valid variable name is given.
    function vSet($action, $var, $value) {
        if (!isset($var) || $var == '')
            throw new ExtContextVarsError('var_needed_for_action', $action);
        if (!isset($value)) {
            throw new ExtContextVarsError('value_needed_for_var', $var);
        // Remove HTML-style comments.
        // Not necessary in versions where StringUtils::delimiterReplace didn't exist.
        if (is_callable(array('StringUtils', 'delimiterReplace'))) {
          $value = StringUtils::delimiterReplace('<'.'!--', '--'.'>', '', $value);
        }
        // TODO: access restrictions.
        // Basic monitoring and capping of total storage size.
        $delta = strlen($value) - (isset($this->mContextVars[$var]) ? strlen($this->mContextVars[$var]) : 0);
        if (EXTCONTEXTVARS_MAX_TOTAL_LENGTH > 0) {
            if ($mTotalSize + $delta > EXTCONTEXTVARS_MAX_TOTAL_LENGTH)
                throw new ExtContextVarsError('memory_overflow', $var);
        }
        $this->mTotalSize += $delta;
        return $this->mContextVars[$var] = $value;
    }

    // Perform 'unset' action: Unset the named context variable.
    // Throws an error if no valid variable name is given.
    function vUnset($action, $var) {
        if (!isset($var) || $var == '')
            throw new ExtContextVarsError('var_needed_for_action', $action);
        if (!isset($this->mContextVars[$var])) {
            throw new ExtContextVarsError('var_is_not_set', $var);
        // Basic monitoring and capping of total memory size.
        $this->mTotalSize -= strlen($this->mContextVars[$var]);
        unset($this->mContextVars[$var]);
    }

    // Perform 'isset' action: Check if a value is stored in a named context variable.
    // Return 1 if this context variable is set, 0 otherwise.
    // Throws an error if no valid variable name is given.
    function vIsSet($action, $action, $var, $value) {
        if (!isset($var) || $var == '')
            throw new ExtContextVarsError('var_needed_for_action', $action);
        return !isset($this->mContextVars[$var]);
    }

    // Main parser function
    function vParse(&$parser, $action = '', $var = '', $value) {
        # Remove HTML-style comments.
        # Not necessary in versions where StringUtils::delimiterReplace didn't exist.
        if (is_callable(array('StringUtils', 'delimiterReplace'))) {
          $action = StringUtils::delimiterReplace('<'.'!--', '--'.'>', '', $action);
          $var = StringUtils::delimiterReplace('<'.'!--', '--'.'>', '', $var);
        }
        try {
            if ($action == '') {
                $actionToken = EXTCONTEXTVARS_GET; // default action
            } else if (!isset($this->actionTokens[$action])) {
                throw new ExtContextVarsError('unrecognized_action', $action);
            } else
                $actionToken = $this->actionTokens[$action];
            }
            switch ($actionToken) {
            // constant
            case EXTCONTEXTVARS_GET: // Requires an existing valid variable name.
                // An exception is thrown if var is not set and no default value given.
                if (isset($value)) return vSet($action, $var, $value);
                return vGet($action, $var, $value);
            case EXTCONTEXTVARS_SET: // Requires any valid variable name and a value.
                vSet($action, $var, $value);
                return '';
            case EXTCONTEXTVARS_UNSET: // Requires an existing valid variable name.
                vUnset($action, $var);
                return '';
            case EXTCONTEXTVARS_ISSET: // Requires any valid variable name.
                return vIsSet($action, $var);
            case EXTCONTEXTVARS_ISENABLED: // Requires any valid variable name.
                // Utility: Return 1 when var is set with a non zero value.
                // When var is not set: does not throw exception, return 0.
                return !!vGet($action, $var, 0);
            case EXTCONTEXTVARS_ISDISABLED: // Requires any valid variable name.
                // Utility: Return 1 when var is set with a zero value.
                // When var is not set: does not throw exception, return 0.
                return !vGet($action, $var, 1);
            case EXTCONTEXTVARS_ENABLE: // Requires any valid variable name.
                return vSet($action, $var, 1);
            case EXTCONTEXTVARS_DISABLE: // Requires any valid variable name.
                return vSet($action, $var, 0);
            }
        } catch (ExtContextVarsError $e) {
            return '<strong class="error">' . $e->getMessage() . '</strong>';
        }
    }

    // Parser function entry using classic semantics
    function vHook(&$parser, $action = '', $var = '', $value) {
        return vParse(&$parser, $action, $var, $value);
    }

    // Parser function entry using object semantics
    function vObj(&$parser, $frame, $args) {
        return vParse(&$parser,
            isset($args[0]) ? $args[0] : '', // action
            isset($args[1]) ? $args[1] : null, // var
            isset($args[2]) ? $args[2] : null); // value
    }
}
ContextVars.i18n.php
<?php
/**
 * Internationalisation file for extension ContextVars.
 *
 * @addtogroup Extensions
 * @author Philippe Verdy
 * LICENCE 2008-11-13: This source is licenced under the same terms as those
 * for the MediaWiki software source files package of the Wikimedia Foundation. 
 */

$messages = array();

/** Message documentation (for helping translators on BetaWiki)
 * @author Philippe Verdy
 */
$messages['qqq'] = array(
    'contextvars_desc' => 'Short description of the ContextVars extension, shown on [[Special:Version]].',
    'contextvars_unrecognized_action'   =>
        '* Error message displayed  when an unknown action is specified before the context variable.\n' .
        '* $1 is replaced by the actual action string specified in the wiki source.\n',
    'contextvars_var_needed_for_action' =>
        '* Error message displayed when an action requires the name of a context variable.\n' .
        '* $1 is replaced by the action that could not be performed.\n',
    'contextvars_value_needed_for_var'  =>
        '* Displayed when no value parameter is specified an action is specified before the context variable.\n' .
        '* $1 is replaced by the actual variable name specified in the wiki source.\n',
    'contextvars_var_is_not_set'        =>
        '* Displayed when no value an unknown action is specified before the context variable.\n' .
        '* $1 is replaced by the actual variable name specified in the wiki source.\n',
);

/** English
 * @author Philippe Verdy
 */
$messages['en'] = array(
    'contextvars_desc' => 'Enhance parser with variables storing contextual information.',
    'contextvars_unrecognized_action'   => 'Error: Unrecognized action "$1" on context variables.',
    'contextvars_var_needed_for_action' => 'Error: Context variable name needed for action "$1".',
    'contextvars_value_needed_for_var'  => 'Error: No value specified, context variable "$1" is not set.',
    'contextvars_var_is_not_set'        => 'Error: Context variable "$1" is not set.',
);

/** French
 * @author Philippe Verdy
 */
$messages['en'] = array(
    'contextvars_desc' => 'Améliore le parseur avec des variables stockant des informations contextuelles.',
    'contextvars_unrecognized_action'   => 'Erreur : action "$1" non reconnue sur les variables de contexte.',
    'contextvars_var_needed_for_action' => 'Erreur : nom de variable de contexte nécessaire pour l’action "$1".',
    'contextvars_value_needed_for_var'  => 'Erreur : pas de valeur indiquée, variable de contexte "$1" non définie.',
    'contextvars_var_not_set'           => 'Erreur : variable de contexte "$1" non définie.',
);
ContextVars.i18n.magic.php
<?php
/**
 * Get translated magic function words for extension ContextVars, if available.
 *
 * @param string $lang Language code
 * @return array
 * @addtogroup Extensions
 * @author Philippe Verdy
 * LICENCE 2008-11-13: This source is licenced under the same terms as those
 * for the MediaWiki software source files package of the Wikimedia Foundation. 
 */
function efContextVarsWords($lang) {
    $words = array();

    /** English
     * @author Philippe Verdy
     */
    $words['en'] = array(
        'v'   => array(0, 'v'),
    );

    /** French
     * @author Philippe Verdy
     */
    $words['en'] = array(
        'v'   => array(0, 'v'),
    );

    # 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($words[$lang]))
        ? $words['en']
        : array_merge($words['en'], $words[$lang]);
}

/**
 * Get translated magic action words for extension ContextVars, if available.
 *
 * @param string $lang Language code
 * @return array
 * @addtogroup Extensions
 * @author Philippe Verdy
 * LICENCE 2008-11-13: This source is licenced under the same terms as those
 * for the MediaWiki software source files package of the Wikimedia Foundation. 
 */
function efContextVarsActionWords($lang) {
    $words = array();

    /** English
     * @author Philippe Verdy
     */
    $words['en'] = array(
        'get'        => array(0, 'get'),
        'set'        => array(0, 'set'),
        'unset'      => array(0, 'unset'),
        'isset'      => array(0, 'isset'),
        'isenabled'  => array(0, 'isenabled'),
        'isdisabled' => array(0, 'isdisabled'),
        'enable'     => array(0, 'enable'),
        'disable'    => array(0, 'disable'),
    );

    /** French
     * @author Philippe Verdy
     */
    $words['fr'] = array(
        'get'        => array(0, 'lit',        'get'),
        'set'        => array(0, 'met'         'set'),
        'unset'      => array(0, 'ôte',        'unset'),
        'isset'      => array(0, 'estmis',     'isset'),
        'isenabled'  => array(0, 'estactif',   'isenabled'),
        'isdisabled' => array(0, 'estinactif', 'isdisabled'),
        'enable'     => array(0, 'active',     'enable'),
        'disable'    => array(0, 'désactive',  'disable'),
    );

    # 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($words[$lang]))
        ? $words['en']
        : array_merge($words['en'], $words[$lang]);
}