Summary: custom markup expression samples
Version: 2007-12-08
Prerequisites: PmWiki 2.2.0 beta

Markup Expression Parameters

Custom expressions may be added by other recipes by adding entries into the $MarkupExpr array. Each entry's key is the name of the function, the value is the code to be evaluated for that function (similar to the way $FmtPV works). By default, any arguments for the expression are placed into the $args array:

##  expressions like {(myfunc foo bar)}
$MarkupExpr['myfunc'] = 'myfunc($args[0], $args[1])';

The expression arguments are parsed using ParseArgs(), and the result of this parsing is available through the $argp array:

##  expressions like {(myfunc fmt=foo output=bar)}
$MarkupExpr['myfunc'] = 'myfunc($argp["fmt"], $argp["output"])';

Finally, if the code in $MarkupExpr contains '$params', then it is executed directly without any preprocessing into arguments, and $params contains the entire argument string, ie, before it is parsed into $args[]. Note that $params may contain escaped values representing quoted arguments and results of other expressions; these values may be un-escaped by using preg_replace($rpat, $rrep, $params).

Custom Markup Expressions


# (md5 "string") calculate the md5 hash of a string
$MarkupExpr['md5'] = 'md5($args[0])';

# (dec2hex num) convert decimal to hexadecimal number
$MarkupExpr['dec2hex'] = 'dechex($args[0])';

# (zp2 num) formats number with two digits after decimal point  
$MarkupExpr['zp2'] = 'sprintf("%02d", $args[0])';

# (numfmt num decimal) formats number with decimal points (digits after dec. point)
$MarkupExpr['numfmt'] = 'number_format($args[0], $args[1], ".", "")';

# (numfmt4 num  decimal decpoint thousep) equivalent to php number_format; 
# formats number with decimal points, specify decimal point and thousands separator 
$MarkupExpr['numfmt4'] = 'number_format($args[0], $args[1], $args[2], $args[3])';
function ME_NumberFormat($val, $dec, $decpoint, $thousep) {
	return number_format($val, $dec, $decpoint, $thousep);

# (sum num num num)
# adds together the numerical values of arguments, eg. {(sum -2 one 6.04)} => 4.04
$MarkupExpr['sum'] = 'array_sum($args)';

# markup {(repeat + 10)} gives : ++++++++++
$MarkupExpr['repeat'] = 'str_repeat($args[0],$args[1])';

(inc num) increase (increment) num by 1

$MarkupExpr['inc'] = 'MxInc($args[0])';
function MxInc ($arg) {
	if(!is_numeric($arg)) return $arg;
	return $arg + 1;	

Simpler form, no check if argument is a number:
(add1 num) adds 1 to num. (sub1 num) subtracts 1 from num.

$MarkupExpr['add1'] = 'intval(@$args[0])+1';
$MarkupExpr['sub1'] = 'intval(@$args[0])-1';


Generate random keys, random passwords

$MarkupExpr['randk'] = 'randomkeys($args[0])';
function randomkeys($length)
     $pattern = "1234567890"; // 10
     $pattern .= "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; // 26
     $pattern .= "abcdefghijklmnopqrstuvwxyz"; // 26
   #  $pattern .= "aeiouaeiouaeiouaeiou"; // 20
   #  $pattern .= "#?!*-+="; // 7
     $key  = $pattern{rand(0,61)};
         $key .= $pattern{rand(0,61)};
     return $key;


Example: {(math 2.0*010 + 9/[2+[0<1]])} => 23
Perform mathematic operations in base 10. Putting quotes around the expression is optional, but without them you need to use [ ] instead of ( ). Boolean expressions return '1' for true and '' for false.
$MarkupExpr['math'] = 'ME_math(preg_replace($rpat, $rrep, $params))';
function ME_math($expr) {
  $expr = preg_replace(array('/[A-Za-z]\w*/', '/[^0-9.+-\/*%!()<>=]/',
                             '/(^|[^0-9.])0+([1-9.])/', '/(?<![!<>=])=/'),
                       array('0', '', '$1$2', '=='),
                                   array('(',')','<','>'), $expr));
  return eval("return ($expr);");

{(calc )}

Example: {(calc 'sqrt(7^2 + 9^2)')}
Extended calculator markup expression. Uses basic and extended math functions. Use + - * / ^ as operators, other PHP math functions with brackets and comma separated arguments: pow(), sqrt(), sin(), cos(), tan(), asin(), acos(), atan(), log(), max(), min(), abs(), ceil(), floor(), round(), rand(), fmod(), deg2rad(), rad2deg(); constant: pi.
$MarkupExpr['calc'] = 'MxCalc($args)';
function MxCalc($args) {
	global $MxCalcChars, $MxCalcFunc1, $MxCalcFunc2;
	//allowed characters (operators, digits, brackets etc) apart from function names below
	SDV($MxCalcChars, "/^[-+*,\\/% ><=()0-9.^]+$/");
	//math functions with single argument
	SDV($MxCalcFunc1, array(
	//math functions with 2 or more arguments, constants, and others
	SDV($MxCalcFunc2, array(
	$mathfn = array_merge($MxCalcFunc1, $MxCalcFunc2); 
	$arg = implode(" ", $args);
	//nothing to do
	if ($arg == '') return "empty input!";
	//check if math expression has only allowed characters and function names
	$str = $arg;
	foreach($mathfn as $fn) {
		$str = str_replace($fn, '', $str);
	if ($str!='' && !preg_match($MxCalcChars, $str, $m)) return "Input error!";
	//translate easy input
	$arg = str_replace('pi', M_PI, $arg);
	$arg = preg_replace("/(\\-?[\\d.]+)\\^(\\-?[\\d.]+)/", "pow($1, $2)", $arg);
	foreach($MxCalcFunc1 as $fn)
		$arg = preg_replace("/{$fn}\\s?(\\-?[\\d.]+)/", "{$fn}($1)", $arg);
	//do the work
	eval("\$res = $arg;");
	//provide for 0 as result from comparison operations
	if ($res=='')
		if (preg_match("/[><=]/", $arg))	$res = '0';
	return $res;

{(utime)} and {(udays)}

Input formatted date, like for instance 2008-02-26.
utime returns unix timestamp of the date input.
{(utime DATE)} is equivalent to {(ftime %s DATE)}
udays returns the unix timestamp converted to whole days.

# unix time, seconds since 1970-01-01
$MarkupExpr['utime'] = 'MxUTime($args[0])';
function MxUTime($arg) {
        $udate = date('U', strtotime($arg));
        if ($arg=='now' || $arg=='') $udate = time();
        return $udate;
# unix time as days since 1970-01-01 to midnight of DATE entered
$MarkupExpr['udays'] = 'MxUDays($args[0])';
function MxUDays($arg) {
        $udate = date('U', strtotime($arg));
        if ($arg=='now' || $arg=='') $udate = time();
        return floor($udate/86400);


{(catlist PAGENAME SEPARATOR)} extracts a list of full pagenames of all category links on a page.

  • use with no arguments: {(catlist)} returns comma separated list of full pagenames of all category links.
  • first argument PAGENAME: category list retuned is from page PAGENAME.
  • second argument: a different separator can be given (default is ,)
$MarkupExpr['catlist'] = 'CategoryList($pagename, $args[0], $args[1])';
function CategoryList($pn, $arg='', $sep=',') {
	global $CategoryGroup;
	if(!$arg=='') $pn = MakePageName($pn, $arg);
	if($sep==NULL) $sep = ",";
	$page = RetrieveAuthPage($pn, 'read', true);
	$pat = '/\\[\\[\\!(.*?)\\]\\]/';
	$text = preg_replace('/\\(:(.*?):\\)/' ,"", $page['text']);
	if(preg_match_all($pat, $text, $matches)) {
	foreach($matches[1] as $i=>$m)
		 $catlist[] = MakePageName($pn, $CategoryGroup.".".$m);
	return implode($sep, $catlist);


    $FmtPV['$CategoryList'] = 'CategoryList($pagename, $page)'; 
    function CategoryList($pagename, $page) { 
      preg_match_all('/(?:^|,)Category\.([^,]+)/', $page['targets'], $x); 
      $out = ''; 
      foreach ($x[1] as $n) { 
        $out .= "[[!$n]] "; 
      return $out; 


Example: {(atomic one 'two ' "{$Title}" )} => 'onetwo MarkupExpressionSamples'
Glues the parameters together unless one or more are blank, in which case returns an empty string. Remember to quote variables!
$MarkupExpr['atomic'] = 'ME_atomic($args)';
function ME_atomic($args) {
	if (empty($args)) return '';
	foreach($args as $a) if (empty($a)) return '';
	return implode('', $args);


Protects its content from being processed by the Markup Engine like [=...=], except that PageVariables are expanded.

  $MarkupExpr['keep'] = 'Keep($params)';

For example, {(keep [[$FullName]] )} displays literal [[Cookbook.MarkupExpressionSamples]] without creating a link.


Release Notes

If the recipe has multiple releases, then release notes can be placed here. Note that it's often easier for people to work with "release dates" instead of "version numbers".

See Also

  • MarkupExpressions (core)
  • MarkupExprPlus - a recipe adding a number of markup expressions
  • MarkupExpressions-Sandbox (Cookbook sandbox)
  • PowerTools - markup expressions for multi page processing incl. plist, pagelist, rename, pagecount, wordcount, trail, serialname, serial, newticket, sumdata, allptvs, random
  • MiscMX - Implement miscellaneous PHP functions by means of Markup Expressions
  • WikiSh - a scripting language (based on bash) built using markup expressions



See discussion at MarkupExpressionSamples-Talk

I put the Extended calculator markup expression in my wikis config.php and it gives this message: Parse error: syntax error, unexpected ')' in /mywikiaddress/local/config.php(276) : eval()'d code on line 1 What's wrong? I updated the code slightly. Please give more information of what you exactly did. It may be important to enclose the math argument string in single quotes inside the markup, as shown in the example. HansB March 07, 2008, at 04:51 AM

This calculator one is nice recipe but I got a problem with it. It gives results like 2,45 where is comma for decimals. But it don't accept comma for decimals as input. For input it requires dot for decimals. So it can't use the variables calculated by itself. Is there something to do to solve this problem?

I am facing the same problem as above question. The output is with comma for decimals, but I need a dot, because the result from a calc calculation is used further and the function round() gives an error, whenever there is a number with comma as decimal Was this problem already solved somewhere? Thank you, Marcel 2010-02-04

Addition: I just found that this seems to be a problem in case the pmwiki language is set to German in the config.php by XLPage('de','PmWikiDe.XLPage'); because deleted this line from my config file and the output us now having a dot. However, I would like to keep the German version AND be able to use the function of calc. I dont care, if I can use everything with dot or comma as decimal. Any idea how to solve this, or where to ask for this?

You have to change locale setting. Insert
after global statement and
before return $res; FrankM

zp2 is padding with up to two leading zeroes before the decimal point instead of showing to two places after

The zp2 doesn't seem to work as expected: "(zp2 num) formats number with two digits after decimal point".

This seems to work (I'm not a PHP programmer but got this with some googling):

$MarkupExpr['zp2'] = 'sprintf("%0.2f", $args[0])';

Regards, AllanGN

User notes? : If you use, used or reviewed this recipe, you can add your name. These statistics appear in the Cookbook listings and will help newcomers browsing through the wiki.