*/ if (!defined('PmWiki')) exit(); /* Copyright 2007-2010 by D.Faure (dominique.faure@gmail.com) and Patrick R. Michaud (pmichaud@pobox.com) Thanks to Steve Levithan for the unvaluable regex trick (http://blog.stevenlevithan.com/archives/mimic-atomic-groups/). This file would surely made be part of PmWiki; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This script extends the original MarkupExpressions recipe (core built- in since version 2.2.0-beta43) essentially by enabling *real* expression nesting (ie. able to handle arithmetic operations). It also defines various extra functions ranging from basic math to advanced string manipulation. The operations defined by this recipe include: add, sub, mul, div, mod - arithmetic operators rev - reverse string rot13 - rotate characters urlencode, urldecode - url formatting reg_replace - regexp find/replace wikiword - build a wikiword from a string test - evaluate a wiki condition if - conditionally returns its arguments sprintf - string formatting nomarkup - keep text from a markup string The original "ftime" expression is also patched to enable some of the format specifier missing on Win32 platforms. The recipe also handle some configuration flags applying deeper modifications to the original script behavior: (!!!disabled in this revision!!!) $EnableExprMultiline - handle of multiline expressions $EnableExprVarManip - enable variable manipulation through "set" and "setq" extra functions See http://www.pmwiki.org/wiki/Cookbook/MarkupExprPlus for more info. Revised to be compatible with PHP 5.5, by Hans Bracker (version 2014-12-03) */ $RecipeInfo['MarkupExprPlus']['Version'] = '2014-12-03'; SDVA($MarkupExpr, array( 'add' => 'MEP_arith("+", preg_replace($rpat, $rrep, $params))', 'sub' => 'MEP_arith("-", preg_replace($rpat, $rrep, $params))', 'mul' => 'MEP_arith("*", preg_replace($rpat, $rrep, $params))', 'div' => 'MEP_arith("/", preg_replace($rpat, $rrep, $params))', 'mod' => '0 + ($args[0] % $args[1])', 'rev' => 'strrev($args[0])', 'urlencode' => 'rawurlencode($args[0])', 'urldecode' => 'rawurldecode($args[0])', 'rot13' => 'str_rot13($args[0])', 'ftime' => 'MEP_ftime($args, $argp)', 'wikiword' => 'PPRA($GLOBALS["MakePageNamePatterns"],' . 'PPRE($rpat, $rrep, $params))', 'reg_replace' => 'MEP_reg_replace($args[0], $args[1], $args[2])', 'test' => 'CondText($pagename, "if " . preg_replace($rpat, $rrep, $params), "TRUE") ? "1" : "0"', 'if' => '($args[0]) ? $args[1] : (($args[2]) ? $args[2] : "")', 'sprintf' => 'call_user_func_array("sprintf", $args)', 'nomarkup' => 'preg_replace("/(<[^>]+>|\r\n?|\n\r?)/", "",' . ' MarkupToHTML($pagename, $args[0], array("escape" => 0)))', )); /* if(IsEnabled($EnableExprMultiline, 0)) SDVA($MEP_Cfg, array( 'MarkupRe' => '/\\{(\\(\\w+\\b.*?\\))\\}/se', 'KeepRe' => '/([\'"])(.*?)\\1/se', 'FuncRe' => '/\((?=((\\w+)(\\s[^()]*)?))\1\)/s', 'ParseArgs' => 'MEP_ParseArgs', )); SDVA($MEP_Cfg, array( 'MarkupRe' => '/\\{(\\(\\w+\\b.*?\\))\\}/e', 'KeepRe' => '/([\'"])(.*?)\\1/e', 'FuncRe' => '/\((?=((\\w+)(\\s[^()]*)?))\1\)/', 'ParseArgs' => 'ParseArgs', )); if(IsEnabled($EnableExprVarManip, 0)) { DisableMarkup('{('); Markup('{(+', isset($MarkupTable['{$?|!@~var}']) ? '<{$?|!@~var}' : '<{$var}', $MEP_Cfg['MarkupRe'], "MarkupExpressionPlus(\$pagename, PSS('$1'))"); SDVA($MarkupExpr, array( 'set' => 'MEP_setvar($pagename, true, @$args[0], @$args[1], $argp)', 'setq' => 'MEP_setvar($pagename, false, @$args[0], @$args[1], $argp)', )); } */ /* else { $MarkupTable['{(']['pat'] = $MEP_Cfg['MarkupRe']; $MarkupTable['{(']['rep'] = "MarkupExpressionPlus(\$pagename, PSS('$1'))"; } */ /* function MarkupExpressionPlus($pagename, $expr) { global $EnableExprVarManip, $MarkupTable, $KeepToken, $KPV, $MarkupExpr, $MEP_Cfg; if(IsEnabled($EnableExprVarManip, 0)) { if(isset($MarkupTable['{$?|!@~var}'])) while(preg_match($MarkupTable['{$?|!@~var}']['pat'], $expr)) $expr = preg_replace($MarkupTable['{$?|!@~var}']['pat'], $MarkupTable['{$?|!@~var}']['rep'], $expr); while(preg_match($MarkupTable['{$var}']['pat'], $expr)) $expr = preg_replace($MarkupTable['{$var}']['pat'], $MarkupTable['{$var}']['rep'], $expr); } $rpat = "/$KeepToken(\\d+P)$KeepToken/e"; $rrep = '$KPV[\'$1\']'; $expr = preg_replace($MEP_Cfg['KeepRe'], "Keep(PSS('$2'),'P')", $expr); # $expr = preg_replace('/\\(\\W/e', "Keep(PSS('$2'),'P')", $expr); while (preg_match($MEP_Cfg['FuncRe'], $expr, $match)) { @list($repl, $dummy, $func, $params) = $match; $code = @$MarkupExpr[$func]; ## if not a valid function, save this string as-is and exit if (!$code) break; ## if the code uses '$params', we just evaluate directly if (strpos($code, '$params') !== false) { $out = eval("return ({$code});"); if ($expr == $repl) { $expr = $out; break; } $expr = str_replace($repl, $out, $expr); continue; } ## otherwise, we parse arguments into $args before evaluating $argp = $MEP_Cfg['ParseArgs']($params); $x = @$argp['#']; $args = array(); while ($x) { list($k, $v) = array_splice($x, 0, 2); if ($k == '' || $k == '+' || $k == '-') $args[] = $k.preg_replace($rpat, $rrep, $v); } ## fix any quoted arguments foreach ($argp as $k => $v) if (!is_array($v)) $argp[$k] = preg_replace($rpat, $rrep, $v); $out = eval("return ({$code});"); if ($expr == $repl) { $expr = $out; break; } $expr = str_replace($repl, Keep($out, 'P'), $expr); } return preg_replace($rpat, $rrep, $expr); } */ function MEP_ParseArgs($x, $optpat = '(?>(\\w+)[:=])') { $z = array(); preg_match_all("/($optpat|[-+])?(\"[^\"]*\"|'[^']*'|\\S+)/s", $x, $terms, PREG_SET_ORDER); foreach($terms as $t) { $v = preg_replace('/^([\'"])?(.*)\\1$/', '$2', $t[3]); if ($t[2]) { $z['#'][] = $t[2]; $z[$t[2]] = $v; } else { $z['#'][] = $t[1]; $z[$t[1]][] = $v; } $z['#'][] = $v; } return $z; } function MEP_nums($params) { $params = preg_split('/\\s+/', $params); $args = array(); foreach($params as $p) { list($d) = sscanf($p, "%g"); if(is_numeric($d)) $args[] = $d; } return $args; } ## MEP_arith handles {(add ...)}, {(sub ...)}, {(mul ...)} and {(div ...)} ## expressions. ## function MEP_arith($op, $params) { $expr = implode(")$op(", MEP_nums($params)); return $expr ? eval("return 0 + ($expr);") : ''; } function MEP_strftime($fmt, $ts = null, $lc = null, $gm = false) { if (!$ts) $ts = time(); if(strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { $date = $gm ? 'gmdate' : 'date'; $map = array( '%C' => sprintf("%02d", $date("Y", $ts) / 100), '%D' => '%m/%d/%y', '%e' => sprintf("%' 2d", $date("j", $ts)), '%g' => sprintf("%02d", $date("o", $ts) % 100), '%G' => $date("o", $ts), '%h' => '%b', '%n' => "\n", '%r' => $date("h:i:s", $ts) . " %p", '%R' => $date("H:i", $ts), '%t' => "\t", '%T' => '%H:%M:%S', '%u' => ($w = $date("w", $ts)) ? $w : 7, '%V' => $date("W", $ts), '%z' => substr($date("O", $ts), 0, 3) . ":" . substr($date("O", $ts), 3), ); $fmt = str_replace(array_keys($map), array_values($map), $fmt); } ## make sure we have %F available for ISO dates $fmt = str_replace(array('%F', '%s'), array('%Y-%m-%d', $ts), $fmt); $strftime = $gm ? 'gmstrftime' : 'strftime'; if(isset($lc)) { $oldlc = setlocale(LC_ALL, '0'); setlocale(LC_ALL, $lc); } $ret = $strftime($fmt, $ts); if(isset($lc)) setlocale(LC_ALL, $oldlc); return $ret; } ## MEP_ftime handles {(ftime ...)} expressions. ## function MEP_ftime($args, $argp = NULL) { global $TimeFmt, $Now, $FTimeFmt; ## get the format string if(@$argp['fmt']) $fmt = $argp['fmt']; else if(strpos(@$args[0], '%') !== false) $fmt = array_shift($args); else if(strpos(@$args[1], '%') !== false) list($fmt) = array_splice($args, 1, 1); else { SDV($FTimeFmt, $TimeFmt); $fmt = $FTimeFmt; } ## determine the timestamp if(isset($argp['when'])) list($time, $x) = DRange($argp['when']); else if(@$args[0] > '') list($time, $x) = DRange(array_shift($args)); else $time = $Now; ## get the locale $locale = isset($argp['lc']) ? $argp['lc'] : array_shift($args); return MEP_strftime($fmt, $time, $locale); } ## MEP_reg_replace handles {(reg_replace /regexp/opt replacement string)} ## function MEP_reg_replace($pat, $repl, $str) { if(!preg_match('/^(.)([^\\1]*)(\\1)(\\w*)$/', $pat, $m)) return $str; $m[4] = str_replace('e', '', $m[4]); return preg_replace(implode('', array_slice($m, 1)), $repl, $str); } /* ## MEP_setvar handles {(set ...)}/{(setq ...)} expressions. ## function MEP_setvar($pagename, $parm, $arg0, $arg1, $argp = NULL) { global $FmtPV; $n = (@$argp['var']) ? $argp['var'] : $arg0; $v = (@$argp['value']) ? $argp['value'] : $arg1; if($n) $FmtPV["\${$n}"] = "'" . str_replace("'", "\\'", $v) . "'"; return $parm ? $v : ''; } */