Mastering MediaWiki's Parser Functions: A Developer's Guide
Why parser functions matter
Jump‑in: why parser functions matter
Imagine you’re tweaking a wiki that lives on the edge of a bustling community—people drop in, edit, and expect instant, tidy results. You could hand‑craft every template, but that’s like painting each brick individually. Parser functions are the mortar: they let you compute, branch, and format on the fly, without spawning a gazillion templates.
Got a “last‑updated” stamp that should respect the viewer’s timezone? Need to pull the page title, split it, and feed parts into a navigation box? All that, and more, lives the curly braces that MediaWiki has been whispering about since version 1.7.
Getting the extension on board
- Check
LocalSettings.php. If you haven’t already, addwfLoadExtension( 'ParserFunctions' );. - Run
php maintenance/update.php(or hitSpecial:Versionin your browser to confirm it’s loaded). - Optional: tweak
$wgPFEnableStringFunctionsif you want the#replaceand#explodegoodies.
Don’t forget to clear the cache after a fresh install—otherwise the wiki will act like it never saw those functions, and you’ll be scratching your head wondering why “{{#if}}” just prints literally.
Basic building blocks
At its core, a parser function looks like {{#func:param1|param2|…}}. The “#” signals “this is a function, not a variable”. The most common starters are:
{{#if:{{{show|}}}|Yes|No}}
{{#ifexist:Help:Parser_functions|Exists|Missing}}
{{#ifeq:{{{lang|en}}}|fr|Bonjour|Hello}}
Notice the triple braces {{{…}}}—they’re placeholders for template parameters. If you’re new to them, think of them as “fill‑in‑the‑blank” slots that the caller decides the content of.
Branching without a compass
Ever tried to write a single template that works for both “draft” and “published” pages? The #if family saves you a boatload of duplication.
{{#if:{{{draft|}}}
| {{#subpagelist:namespace=Talk|title={{FULLPAGENAME}}}}
| {{#subpagelist:namespace=0|title={{FULLPAGENAME}}}}
}}
Here the function checks whether draft is set. If it is, we list talk‑subpages; otherwise we list article subpages. The syntax can feel a bit cramped, but once you get the rhythm, it’s as smooth as a well‑oiled bike chain.
String gymnastics
String manipulation used to be a pain point before the StringFunctions module (enabled via $wgPFEnableStringFunctions = true;). Now you can split, replace, and trim without a single Scribunto module.
{{#replace:{{{title|}}}|_|\ }}That tiny line swaps underscores for spaces—handy when you’re pulling a page title into a human‑readable heading.
Or, to grab the last segment of a slash‑separated path:
{{#explode:{{FULLPAGENAME}}|/|{{#vardefine:parts|{{#explode:{{FULLPAGENAME}}|/}}}}}}
{{#vardefine:last|{{#arrlast:{{#var:parts}}}}}}
{{#var:last}}
Sure, it looks like a cryptic recipe, but once you’ve baked it a couple of times you’ll appreciate the power—no need to dive into Lua for simple splits.
Arithmetic and date tricks
When you need to calculate ages or add days to a deadline, #expr is your go‑to. It supports basic math, bitwise ops, and even mod (the remainder operator).
{{#expr: ( {{#time:Y}} - {{#time:Y|{{{birthdate|}}}}} ) }}That one‑liner spits out the age based on a birthdate param formatted as YYYY-MM-DD. If you’re feeling fancy, combine it with #time to format future dates:
{{#time:{{#expr:{{#time:Y}}+1}}-{{#time:m}}-{{#time:d}}}}
Result: the same month‑day next year. Handy for “renewal” notices that never get stale.
URL‑and‑title decoding
MediaWiki stores page titles in a URL‑encoded form when you fetch them via #urlencode or #urlunencode. This is useful when you want to embed a link inside a parameter that itself is part of a URL.
{{#urlencode:{{FULLPAGENAME}}}}
Couple that with #titleparts to dissect a title into namespace, main title, and fragment. Great for generating breadcrumbs without a custom extension.
{{#titleparts:{{FULLPAGENAME}}|1}} – namespace
{{#titleparts:{{FULLPAGENAME}}|2}} – base title
{{#titleparts:{{FULLPAGENAME}}|3}} – section (if any)
Performance: keep it lean
Parser functions are evaluated on every page view, which means a heavy‑handed #ifexist in a top‑level template can add noticeable latency. A few tricks help keep the wiki snappy:
- Cache results. Use
{{#vardefine}}to store intermediate values; this avoids recomputing the same expression multiple times in the same render. - Prefer static data. When you need a list of categories, consider using
#showfrom theParserFunctionsextension rather than pulling the whole list via an API call inside a template. - Limit recursion. Deeply nested templates that each call parser functions can blow the parser stack. MediaWiki caps recursion at 100 levels, but hitting that limit will throw a “too many levels of recursion” error.
Security hygiene
Because parser functions can be used to fetch page content (#ifexist, #titleparts, etc.), a mischievous user could try to expose private pages or overload the parser with massive loops. Here’s what to watch:
- Disable
#ifexiston wikis that host confidential material. Set$wgPFAllowDisplayTitle = false;if you don’t need dynamic titles. - Turn on
$wgEnableJavaScriptTest—it won’t stop parser misuse, but it will flag templates that call too many functions. - Audit templates regularly. A simple “{{#expr:{{#var:x}}*{{#var:x}}}}” can be turned into a denial‑of‑service if
xis user‑controlled and astronomically large.
In short, treat parser functions like any other code: keep an eye on input, validate where possible, and don’t hand out super‑powers to just anyone.
Real‑world showcase: a dynamic “Featured article” banner
Suppose your wiki wants to highlight the article of the week, pulling the title from a page Template:Featured. A compact solution lives entirely in wikitext:
{{#vardefine:fa|{{#ifexist:Template:Featured|{{#subpagelist:namespace=Template|title=Featured|limit=1}}|None}}}}
{{#if:{{#var:fa}}|
{{#ifexist:{{#var:fa}}|
{{#titleparts:{{#var:fa}}|2}}
{{#ifexist:{{#var:fa}}/Banner|
[[File:{{#var:fa}}/Banner|thumb|center]]
}}
| No featured article today.}}
| No featured article today.}}
What’s happening?
- We grab the first subpage of
Template:Featured(that’s the article name). - If it exists, we extract its base title and optionally pull an image named
ArticleName/Banner. - If anything’s missing, a friendly fallback shows up.
All of that without a single line of PHP or Lua. The page automatically updates when editors edit Template:Featured or upload a new banner image.
Debugging tips – when things go sideways
Parser functions don’t throw stack traces; they just output the raw call if something’s off. To diagnose, wrap your suspect call in #iferror (available via the ParserFunctions extension) and ship a helpful message.
{{#iferror:{{#:{{{num|}}}+1}}|Oops, {{#var:num}} isn’t a number!|Result: {{#expr:{{{num|}}}+1}}}}
Now a typo like “abc” won’t break the whole page; you’ll see a gentle “Oops…” cue instead. Another handy tool is Special:ExpandTemplates—paste your wikitext there and watch the engine resolve every function step by step.
Beyond the basics: mixing with Scribunto
When you outgrow pure parser functions, Lua modules (#invoke) can take the baton. The trick is to keep the heavy lifting in Lua while letting parser functions handle the simple conditionals and formatting.
{{#invoke:Stats|display|page={{FULLPAGENAME}}}}
{{#ifexist:{{#invoke:Stats|hasData|page={{FULLPAGENAME}}}}|…|No data}}
Here Stats returns a boolean flag (via #return) that the surrounding parser function checks. This pattern reduces the number of template calls, speeds up rendering, and keeps the wikitext readable.
Final thoughts – a toolbox, not a crutch
Parser functions are like a Swiss‑army knife: you can carve out a quick solution, but you shouldn’t rely on it for every job. When the logic starts to feel like a maze of pipes, consider a dedicated extension or a Lua module. Still, for the everyday “show this if that”, “format this date”, or “split that title”, they’re unbeatable.
If you’re still on the fence, try sprinkling a few #if checks into an existing template you already maintain. Watch the page load a tad faster (thanks to reduced template churn) and enjoy the satisfaction of seeing a single wikitext line replace a whole cascade of sub‑templates. That’s the sweet spot where Mastery meets practicality.