W3TPL/archive/code: Difference between revisions

From Woozle Writes Code
< W3TPL‎ | archive
Jump to navigation Jump to search
(→‎Code: *real* version 0.31)
m (→‎Code: v0.32)
Line 41: Line 41:
    @user.rights - returns marked list of current user's rights
    @user.rights - returns marked list of current user's rights
    "if (defined('__DIR__'))" properly invokes newer PHP constant if it's available
    "if (defined('__DIR__'))" properly invokes newer PHP constant if it's available
0.32 (Wzl) minor bugfix: use of undeclared var $doTbl in certain circumstances
  BUGS:
  BUGS:
2009-01-29 can't access upper-case-named POST variables
2009-01-29 can't access upper-case-named POST variables
Line 86: Line 87:
'author' => 'Woozle (Nick) Staddon',
'author' => 'Woozle (Nick) Staddon',
'url' => 'http://htyp.org/W3TPL',  
'url' => 'http://htyp.org/W3TPL',  
'version' => '0.31 2009-03-29 (alpha)'
'version' => '0.32 2009-05-26 (alpha)'
);
);


Line 898: Line 899:
}
}
$strWhere = $objArgs->GetExpr('where', TRUE);
$strWhere = $objArgs->GetExpr('where', TRUE);
} else {
    $doTbl = FALSE;
}
}
$strSort = $objArgs->GetExpr('sort', TRUE);
$strSort = $objArgs->GetExpr('sort', TRUE);

Revision as of 17:25, 26 May 2009

Preface

This is just a snapshot from my editor, but it does work except for the <w3tpl> tag, which is in development. Parser interactions may make it difficult to use the existing tags for complex applications, but you can get around many issues by using the "copy" attribute and $variable indirection (to be documented).

Code

<php><?php /*

HISTORY:

0.01 (Wzl) Mainly proof-of-concept; 0.02 (Wzl) Kluge to let <xploop> pull #var value under MW <1.12

	0.03 (Wzl) <func> and related tags seem to be working

0.04 (Wzl) Some debugging; now works with v1.12 {{#tag}} function and template parameters (but not very well) names are always lowercased because sometimes the parser does it for you names are always trimmed, because sometimes the parser includes extra spaces 0.05 (Wzl) Added variable indirection ($); removed now-redundant "namer" attribute from <let> can we do something similar with pre-parsing? (i.e. a character to indicate the need for it -- @(stuff to parse)) <if> can now make more sense, i.e. using actual values instead of assuming variable names 0.06 (Wzl) xploop now using variable indirection; removed listvar parameter 0.07 (Wzl) Execution trace in <dump> 0.08 (Wzl) <trace> to set trace options; "input" option in <call> 0.09 (Wzl) <echo> tag; <call> does not output its contents 0.10 (Wzl) Code runs ok; still writing <w3tpl> tag 0.11 (Wzl) Added detection of page-protection, and "raw" attribute for <echo> 0.12 (Wzl) Fixed minor incompatibilities with MW 1.10 0.13 (Wzl) <for> tag seems to be working, at least for simple stuff 0.14 (Wzl) <load> tag fails gracefully when title doesn't exist 0.15 (Wzl) Support for brackets in variable names, to reference arrays 0.16 (Wzl) No SQL on unprotected pages; "limit" attribute for retrieving partial data 0.17 (Wzl) Removed some undefined-var warnings which pop up under certain mysterious circumstances 0.18 (Wzl) Lots of under-the-hood changes; var names are now parsed by the class clsW3VarName, which may later get renamed to clsW3Var and integrated with the full-blown parser to be written. There's still some ambiguity about the exact circumstances under which <let> overwrites existing data and when it operates on the variable's current value. 0.19 (Wzl) $wgW3TPLSettings['raw-ok'] now allows raw output even if page not protected 0.20 (Wzl) added "pre" as a deprecated alias for "parse", for backwards compatibility 0.21 (Wzl) merged changes from other version 0.19 (accidental fork): "user.name" and "user.dbkey" system data 0.22 (Wzl) user.can.[action] 0.23 (Wzl) added "file=" attribute to <dump> tag; $wgW3_opt_fpLogs 0.24 (Wzl) wrote code for access to non-MW dbs via "db=" in <for> tag 0.25 (Wzl) patch for pre-5.3.0 PHP; @title.url 0.26 (Wzl) 'vars' option for <let> copied from <echo>, but it doesn't seem to work 0.27 (Wzl) @title.full 0.30 (Wzl) removed all echo-level tracking (see NOTES: ECHO DEPTH); added "var" attribute to <echo> 0.31 (Wzl) @user.rights - returns marked list of current user's rights "if (defined('__DIR__'))" properly invokes newer PHP constant if it's available 0.32 (Wzl) minor bugfix: use of undeclared var $doTbl in certain circumstances

BUGS:

2009-01-29 can't access upper-case-named POST variables $wgW3_func needs to be an array, so you can do <call...><arg><arg></call> and then pass the return value as an argument to another function (2008-10-15: in retrospect, I'm not sure how making it an array would help, though functions probably do need to have the ability to return values)

TO DO:

An alternate <call> syntax might be good, where the function name and arguments are given explicitly: <run func=funcName arg1=value arg2=value input=arg3>this will get passed as the value of arg3</run>

HISTORY:

2009-02-13 (Wzl) $wgOptCP_SubstFinish "]" -> "$]" so links and other bracketed stuff don't confuse the var parser

ELEMENTS:

<hide>: Runs the parser on everything in between the tags, but doesn't display the result. Useful for doing a lot of "programmish" stuff, so you can format it nicely and comment it without messing up your display <let>, <get>: much like PageVars extension, but using XML tags instead of {{#parser}} functions <func>: defines a function which can be called with arguments later <arg>: optional method of passing arguments to a function <call>: call a previously defined function <dump>: show list of all variables (with values) and functions (with code) <if>, <else>: control structure <xploop list="\demarcated\list" repl=string-to-replace sep=separator></xploop>: Same as {{#xploop}}, but uses varname instead of $s$ TO-DO <w3tpl></w3tpl>: The language itself parser should later be optimized for execution time by using PHP intrinsic string fx instead of PHP-code loop

NOTES:
   ECHO DEPTH: Keeping track of different levels of echo should be unnecessary.
     The code inside a control structure (func, if, loop, etc.) isn't parsed *until it's used*.
     I'm now (2009-03-06) testing this theory by brutally stripping out all the echo levels...
  • /

if (defined( '__DIR__' )) {

 $fpThis = __DIR__;

} else {

 $fpThis = dirname(__FILE__);
 require_once('StringTemplate.php');	// can this be made load-on-demand? It's only used by <echo>

}

$wgAutoloadClasses['clsStringTemplate'] = $fpThis.'/StringTemplate.php'; $wgAutoloadClasses['clsDatabase'] = $fpThis.'/data.php'; $wgOptCP_SubstStart = '[$'; $wgOptCP_SubstFinish = '$]';

$wgExtensionCredits['other'][] = array( 'name' => 'W3TPL', 'description' => 'Woozle\'s Wacky Wiki Text Processing Language', 'author' => 'Woozle (Nick) Staddon', 'url' => 'http://htyp.org/W3TPL', 'version' => '0.32 2009-05-26 (alpha)' );

define('ksFuncInit','efW3TPLInit');

//Avoid unstubbing $wgParser on setHook() too early on modern (1.12+) MW versions, as per r35980 if ( defined( 'MW_SUPPORTS_PARSERFIRSTCALLINIT' ) ) {

       $wgHooks['ParserFirstCallInit'][] = ksFuncInit;

} else { // Otherwise do things the old fashioned way

       $wgExtensionFunctions[] = ksFuncInit;

} $wgHooks['LanguageGetMagic'][] = 'efW3_LanguageGetMagic'; $wgHooks['ParserAfterTidy'][] = 'efW3_ParserAfterTidy'; $wgHooks['OutputPageBeforeHTML'][] = 'efW3_OutputPageBeforeHTML';

function efW3TPLInit() {

       global $wgParser;

global $wgExtW3TPL; global $wgW3RawOk;

// hook in <tag>-style functions:

       $wgParser->setHook( 'hide',	'efW3Hide' );
       $wgParser->setHook( 'let',	'efW3Let' );
       $wgParser->setHook( 'get',	'efW3Get' );
       $wgParser->setHook( 'echo',	'efW3Echo' );
       $wgParser->setHook( 'dump',	'efW3Dump' );
       $wgParser->setHook( 'trace',	'efW3Trace' );
       $wgParser->setHook( 'if',	'efW3If' );
       $wgParser->setHook( 'else',	'efW3Else' );
       $wgParser->setHook( 'for',	'efW3For' );
       $wgParser->setHook( 'func',	'efW3Func' );
       $wgParser->setHook( 'call',	'efW3Call' );
       $wgParser->setHook( 'arg',	'efW3Arg' );
       $wgParser->setHook( 'load',	'efW3Load' );
       $wgParser->setHook( 'save',	'efW3Save' );
       $wgParser->setHook( 'xploop',	'efW3Xploop' );
       $wgParser->setHook( 'w3tpl',	'efW3TPLRender' );
       return true;

} function efW3_LanguageGetMagic( &$magicWords, $langCode = "en" ) {

   switch ( $langCode ) {
       default:
           $magicWords['w3xploop']	= array ( 0, 'w3xploop' );
           $magicWords['w3xpcount']	= array ( 0, 'w3xpcount' );
   }
   return true;

}


function TrueFalse($iVar) { return $iVar?'TRUE':'FALSE'; } function W3VarExists($iName) { global $wgW3Vars;

$strName = strtolower($iName); return isset($wgW3Vars[$strName]); } function W3KillVar($iName) { global $wgW3Vars;

$strName = strtolower($iName); unset($wgW3Vars[$strName]); } function W3SetVar($iName, $iValue, $iAppend = FALSE) { global $wgW3Vars, $wgW3_doTrace_vars;

$strName = strtolower($iName); if ($iAppend && isset($wgW3Vars[$strName])) { $wgW3Vars[$strName] .= $iValue; if ($wgW3_doTrace_vars) { W3AddTrace(' $['.ShowHTML($strName).'] += ['.$iValue.'] => ['.$wgW3Vars[$strName].']'); } } else { $wgW3Vars[$strName] = $iValue; if ($wgW3_doTrace_vars) { W3AddTrace(' $['.ShowHTML($strName).'] = ['.$iValue.']'); } } } function W3GetSysData($iName) { global $wgParser,$wgTitle,$wgUser,$wgRequest; global $wgW3_doTrace_vars; global $wgW3_data;

$strName = strtolower($iName); $strParts = explode('.', $strName); $strParam = $strParts[1]; switch ($strParts[0]) { case 'title': switch ($strParam) { case 'id': $out = $wgTitle->getArticleID(); break; case 'full': // namespace:subject $out = $wgTitle->getPrefixedText(); break; case 'subject': // just the part after the namespace $out = $wgTitle->getText(); break; case 'url': $out = $wgTitle->getFullURL(); break; } break; case 'row': $strSet = $strParts[1]; // dataset name $strFld = $strParts[2]; // field name // This is a horrible kluge necessitated by the 2 different ways of accessing data in <for> if (is_array($wgW3_data[$strSet])) { $out = $wgW3_data[$strSet][$strFld]; } else { $out = $wgW3_data[$strSet]->$strFld; } W3AddTrace('DATA['.$strSet.']['.$strFld.'] => ['.$out.']'); break; case 'mem': $out = memory_get_usage(TRUE); break; case 'user': switch ($strParam) { case 'login':

			$out = $wgUser->getName();

break; case 'dbkey': $out = $wgUser->getTitleKey(); break; case 'id': $out = $wgUser->getID(); break; case 'can': $out = $wgUser->isAllowed($strParts[2]); break; case 'rights': $arrRights = $wgUser->getRights(); $out = ; foreach ($arrRights as $key=>$val) { $out .= '\\'.$val; } break; } break; case 'query': if ($strParam == ) { # TO DO: return raw query } else { $out = $wgRequest->getText($strParam); } break; case 'post': if ($strParam) { if (isset($_POST[$strParam])) { $out = $_POST[$strParam]; } else { $out = NULL; } } W3AddTrace(' GET POST ['.ShowHTML($strParam).']: ['.$out.']'); break; case 'env': if ($strParam) { if (isset($_ENV[$strParam])) { $out = $_ENV[$strParam]; } else { $out = NULL; } } break; } if ($wgW3_doTrace_vars) { W3AddTrace(' GETSYSDATA-'.$strParts[0].' ['.ShowHTML($iName).']: ['.$out.']'); } return $out; } function W3SetSysData($iName,$iValue) { global $wgOut; global $wgW3Trace_indent;

W3AddTrace(' SETSYSDATA ['.$iName.':'.$iValue.']'); $strName = strtolower($iName); $strParts = explode('.', $strName); switch ($strParts[0]) { case 'catg': $wgW3Trace_indent++; W3AddTrace('CATG'); $wgW3Trace_indent--; $wgOut->mCategoryLinks = array(); break; } } function W3GetExpr($iName) { // check expression for $, meaning it's actually a reference to a variable // If found, return value of variable - otherwise return original string. $objVar = new clsW3VarName($iName); $objVar->Trace(); $objVar->Fetch(); $strOut = $objVar->Value; return $strOut;

} function W3GetVal($iName,$iIndex=NULL) { // gets value of given variable // checks function arguments, if function is defined $objVar = new clsW3VarName(); $objVar->ParseName($iName); if (!is_null($iIndex)) { $objVar->SetIndex($iIndex); } $objVar->Trace(); $objVar->Fetch(); $strVal = $objVar->Value; return $strVal; } function W3GetEcho() { global $wgW3_echoBuffer;

$out = $wgW3_echoBuffer; $wgW3_echoBuffer = NULL; return $out; } function W3AddEcho($iVal) { global $wgW3_echoBuffer;

$wgW3_echoBuffer .= $iVal; } function W3AddTrace($iLine,$iInd=0) { global $wgW3Trace, $wgW3Trace_indents, $wgW3Trace_indent; global $wgW3_doTrace; global $wgW3_TraceCount;

if ($wgW3_doTrace) { if ($wgW3_TraceCount < 1000) { $wgW3Trace[] = $iLine; $wgW3Trace_indents[] = $wgW3Trace_indent; $wgW3Trace_indent += $iInd; } $wgW3_TraceCount++; } /**/ } function W3Status_RawOk() { global $wgTitle; global $wgW3_func; global $wgW3TPLSettings;

if ($wgW3TPLSettings['raw-ok']) { $isProt = TRUE; } else { $isProt = $wgTitle->isProtected ('edit'); } if (!$isProt) { if (is_object($wgW3_func)) { $isProt = $wgW3_func->isOkRaw; } } W3AddTrace('IS RAW ok in ['.$wgTitle->getFullText().']: '.TrueFalse($isProt)); return $isProt; } function W3Status_SQLOk() { global $wgTitle; global $wgW3_func;

$isProt = $wgTitle->isProtected ('edit'); if (!$isProt) { if (is_object($wgW3_func)) { $isProt = $wgW3_func->isOkSQL; } } W3AddTrace('IS SQL allowed in ['.$wgTitle->getFullText().']: '.TrueFalse($isProt)); return $isProt; } function efW3Hide( $input, $args, $parser ) { $parser->recursiveTagParse( $input ); return NULL; } function W3Let_scalar( $iVar, $iArgs, $input, $parser ) { global $wgRequest; global $wgW3_func; global $wgOptCP_SubstStart, $wgOptCP_SubstFinish;

$strRepl = W3GetExpr($iArgs->GetVal('repl')); $strWith = W3GetExpr($iArgs->GetVal('with')); $doRepl = !is_null($strRepl) || !is_null($strWith); $doAppend = $iArgs->Exists('append');

// TRACING: $strTrace = ' - LET scalar:'; if (!is_null($strRepl)) { $strTrace .= ' repl=“'.$strRepl.'”'; } if (!is_null($strWith)) { $strTrace .= ' repl=“'.$strWith.'”'; } if ($doAppend) { $strTrace .= ' APPEND'; } W3AddTrace($strTrace);

if ($iArgs->Exists('val')) { $iVar->Value = $iArgs->GetExpr('val'); $strTrace = ' - LET VAL: expr=['.ShowHTML($iVar->Value).']'; W3AddTrace($strTrace); } elseif ($iArgs->Exists('arg')) { $strCopy = $iArgs->vArgs['arg']; // don't do any indirection from user input (possible security hole) $parser->disableCache(); $iVar->Value = $wgRequest->getVal($strCopy); // , $strDefault) -- maybe add feature later } elseif ($iArgs->Exists('farg')) { if (is_null($wgW3_func)) { W3AddTrace(' - ERROR: no function active to provide arg ['.$strName.']'); } else { $strName = strtolower($iArgs->GetExpr('farg')); if ($wgW3_func->HasArg($strName)) { $iVar->Value = $wgW3_func->ArgVal($strName); W3AddTrace(' - ARG['.$strName.'] => “'.$iVar->Value.'”'); } else { W3AddTrace(' - ERROR: function ['.$wgW3_func->Name.'] has no argument named ['.$strName.'].'); } } } elseif ($iArgs->Exists('chr')) { $iVar->Value = chr($iArgs->GetExpr('chr')); }

// AT THIS POINT, $this->Value is loaded with the value we want to operate on.

// later, we may want inc/dec to imply self-operation if there is no other input... // but this needs to be thought through carefully. For now, require "self" to increment self.

// do processing on current value: if ($iArgs->Exists('inc')) { $iVar->Value++; } if ($iArgs->Exists('dec')) { $iVar->Value--; }

if ($iArgs->Exists('parse') || $iArgs->Exists('pre')) { // restoring "pre" for backwards compatibility $iVar->Value = $parser->recursiveTagParse($iVar->Value); } if ($iArgs->Exists('vars')) { W3AddTrace(' LET VARS before ['.ShowHTML($iVar->Value).']'); $objTplt = new clsStringTemplate_w3tpl($wgOptCP_SubstStart,$wgOptCP_SubstFinish); $objTplt->Value = $iVar->Value; $iVar->Value = $objTplt->Replace(); W3AddTrace(' LET VARS after ['.ShowHTML($iVar->Value).']'); } if ($iArgs->Exists('ucase')) { $iVar->Value = strtoupper($iVar->Value); } if ($iArgs->Exists('lcase')) { $iVar->Value = strtolower($iVar->Value); } if ($iArgs->Exists('ucfirst')) { $iVar->Value = ucfirst($iVar->Value); } if ($iArgs->Exists('lcfirst')) { $iVar->Value = lcfirst($iVar->Value); } if ($iArgs->Exists('trim')) { $iVar->Value = trim($iVar->Value); } if ($iArgs->Exists('len')) { $strLen = $iArgs->GetExpr('len'); if (is_numeric($strLen)) { $iVar->Value = substr($iVar->Value,0,$strLen); } } if ($doRepl) { if (is_null($strRepl)) { $strRepl = $input; } elseif (is_null($strWith)) { $strWith = $input; } $doIncluding = TRUE; if ($iArgs->Exists('before')) { // replace everything before the mark // TODO $doIncluding = FALSE; } if ($iArgs->Exists('after')) { // replace everything after the mark // TODO $doIncluding = FALSE; } if ($iArgs->Exists('including')) { $doIncluding = TRUE; } if ($doIncluding) { $strRes = str_replace($strRepl,$strWith,$iVar->Value); } W3AddTrace('LET REPLACE ['.$strRepl.'] WITH ['.$strWith.'] => ['.$strRes.'] in “'.$iVar->Value.'”'); $iVar->Value = $strRes; } W3AddTrace('LET ['.$iVar->Name.'] ← “'.$iVar->Value.'”');

// AT THIS POINT, we have the semi-final value to be stored // -- if it's being appended, then get the old value and prepend it to the new:

if ($doAppend) { $valNew = $iVar->Value; // save the newly-calculated value off to one side

W3AddTrace(' - APPEND “'.$valNew.'”'); $iVar->Fetch(); // restore the prior value $iVar->Value .= $valNew; // append the new value to the prior value } } function W3Let_array ( $iVar, $iArgs, $input, $parser ) { $doSort = $iArgs->Exists('sort');

if ($doSort) { $iVar->DoSort($iArgs->Exists('rev'),$iArgs->Exists('val')); } } function efW3Let( $input, $args, $parser ) {

       global $wgRequest;

global $wgW3Vars,$wgW3_func;

$strCopy = NULL; $objArgs = new W3HookArgs($args); $strNameRaw = $objArgs->GetVal('name');

// trim whitespace and normalize name: $strName = strtolower(trim($strNameRaw)); $objVar = new clsW3VarName(); $objVar->ParseName($strName); // resolve any indirection (e.g. $var)

if (isset($args['index'])) { $strIdx = W3GetExpr($args['index']); $objVar->SetIndex($strIdx); } $strName = $objVar->Name; W3AddTrace('LET name=['.$strNameRaw.'] parsed to ['.$strName.']');

if (isset($args['null'])) { // if "null" option, then nothing else matters $objVar->Value = NULL; } else { // "copy" option works for any data type: if ($objVar->CheckCopy($objArgs)) { // do nothing; work already done } else { if (is_null($input) or isset($args['self'])) { $objVar->Fetch(); W3AddTrace(' - from self: ['.$objVar->Value.']'); } else { $objVar->Value = $input; W3AddTrace(' - from input: ['.$objVar->Value.']'); } } if ($objVar->IsArray()) { W3Let_array ( $objVar, $objArgs, $input, $parser ); } else { W3Let_scalar ( $objVar, $objArgs, $input, $parser ); } }

$objVar->Store();

// (optional) print the results: if (isset($args['echo'])) { return $objVar->Value; } else { return NULL; } /**/ } function efW3Get( $input, $args, $parser ) {

       global $wgRequest,$wgOut;

$objArgs = new W3HookArgs($args); W3AddTrace('GET:'); $doRaw = FALSE;

if (isset($args['default'])) { $strDefault = $args['default']; } else { $strDefault = NULL; } if (isset($args['val'])) { $strVal = $args['val']; $strVal = W3GetExpr($strVal); // check for redirections } elseif (isset($args['arg'])) { $parser->disableCache(); $strVal = $wgRequest->getVal($strName, $strDefault); } else { $strName = strtolower($args['name']); W3AddTrace(' - name=['.$strName.']');

$strIdx = $objArgs->GetExpr('index',TRUE); $strVal = W3GetVal($strName,$strIdx); } if (isset($args['codes'])) { $strVal = ShowHTML($strVal); } else { $doRaw = FALSE; if (isset($args['raw'])) { if (W3Status_RawOk()) { $doRaw = TRUE; } } if (!$doRaw) { $strVal = $parser->recursiveTagParse($strVal); } } if (isset($args['isolate'])) { $strVal = IsolateOutput($strVal); } if (isset($args['len'])) { $strVal = substr($strVal,0,$args['len']); } if (isset($args['ucase'])) { $strVal = strtoupper($strVal); } if (isset($args['lcase'])) { $strVal = strtolower($strVal); } return $strVal; } function efW3Echo( $input, $args, $parser ) { global $wgOptCP_SubstStart,$wgOptCP_SubstFinish;

W3AddTrace('ECHO:');

if (isset($args['chr'])) { $valIn = chr($args['chr']); } else if (isset($args['var'])) { $valIn = W3GetVal($args['var']); } else { $valIn = $input; }

if (isset($args['strip'])) { $out = ShowHTML($valIn); } $doRaw = FALSE; if (isset($args['raw'])) { if (W3Status_RawOk()) { $doRaw = TRUE; } } $doNow = isset($args['now']); W3AddTrace(' input:['.ShowHTML($valIn).']'); $out = $valIn;

if (isset($args['vars'])) { W3AddTrace(' VARS before ['.ShowHTML($out).']'); $objTplt = new clsStringTemplate_w3tpl($wgOptCP_SubstStart,$wgOptCP_SubstFinish); $objTplt->Value = $out; $out = $objTplt->Replace(); W3AddTrace(' VARS after ['.ShowHTML($out).']'); }

if ($doRaw) { // no further processing } else { $out = $parser->recursiveTagParse($out); W3AddTrace(' PARSING returned ['.ShowHTML($out).']'); } if (isset($args['isolate'])) { $out = IsolateOutput($out); }

W3AddTrace(' output: ['.ShowHTML($out).'] ECHO}'); if ($doNow) { return $out; } else { W3AddEcho($out); } } function efW3Dump( $input, $args, $parser ) { global $wgW3Vars, $wgW3_funcs; global $wgW3_doTrace, $wgW3_doTrace_vars; // tracing options global $wgW3Trace, $wgW3Trace_indents; // tracing data global $wgW3_TraceCount; global $wgW3_opt_fpLogs;

$out = '

    '; $doPost = isset($args['post']); // show posted data $doTrace = isset($args['trace']); // show trace log $doVars = isset($args['vars']); // show all variables $doFuncs = isset($args['funcs']); // show function definitions $doMem = isset($args['mem']); // show memory usage $wgW3_doTrace = $doTrace; $wgW3_doTrace_vars = $doTrace && $doVars; if ($doPost) { $out .= '
  • Posted::
      '; foreach ($_POST AS $key => $value) { $out .= ("\n
    • [$key]: [$value]"); } $out .= '
    ';

    }

    if ($doMem) {

    $out .= '
  • Memory usage before: '.memory_get_usage(TRUE).' bytes'; } if ($input != ) { $out .= $parser->recursiveTagParse($input); } if ($doMem) { $out .= '
  • Memory usage after: '.memory_get_usage(TRUE).' bytes'; } if ($doVars) { if (is_array($wgW3Vars)) { $out .= '
  • Variables:
      '; foreach ($wgW3Vars as $name => $value) { if (is_array($value)) { $out .= '
    • ['.$name.']: array'; $out .= '
        '; foreach ($value as $akey => $aval) { $out .= '
      • '.$name.'['.$akey.'] = ['.ShowHTML($aval).']'; } $out .= '
      ';

      } else {

      $out .= '
    • ['.$name.'] = ['.$value.']'; } } $out .= '
    ';

    } else {

    $out .= '
  • No variables set'; } } if ($doFuncs) { if (is_array($wgW3_funcs)) { $out .= '
  • Functions:
      '; foreach ($wgW3_funcs as $name => $obj) { $out .= '
    • '.$obj->dump(); } $out .= '
    ';

    } else {

    $out .= '
  • No functions defined'; } } if ($doTrace) { if (is_array($wgW3Trace)) { $out .= '
  • Trace ('.$wgW3_TraceCount.' events):
      '; $indCur = 0; foreach ($wgW3Trace as $idx => $line) { $indLine = $wgW3Trace_indents[$idx]; if ($indLine > $indCur) { $out .= '-'.$indLine.'-
        '; } elseif ($indLine < $indCur) { $out .= '
      ';

      } $indCur = $indLine; /**/

      $out .= '
    • '.$idx.' '.$line; } $out .= '
    ';

    } else {

    $out .= '
  • '.$wgW3_TraceCount.' trace events'; } } $out .= '

';

if (isset($args['file'])) { $strFile = $args['file']; // dump to a file instead of screen $fh = fopen($wgW3_opt_fpLogs.$strFile, 'a'); // $dt = new DateTime(); // $outPfx = $dt->format('Y-m-d H:i:s') . "\n"; $outPfx = date('Y-m-d H:i:s'); if (isset($args['msg'])) { $outPfx .= ' - '.$args['msg']; } $outPfx .= '
'; $qb = fwrite($fh, $outPfx.$out); fclose($fh); if (isset($args['hide'])) { return NULL; } else { return "$qb bytes logged to $strFile."; } } else { return $out; } } /* function efW3Trace( $input, $args, $parser ) { global $wgW3_doTrace_vars;

$wgW3_doTrace_vars = isset($args['assigns']); }

  • /

function efW3If( $input, $args, $parser ) { global $wgW3_ifFlag,$wgW3_ifDepth;

$doHide = isset($args['hide']); // only output <echo> sections

$ifFlag = false; if (!$wgW3_ifDepth) { $wgW3_ifDepth = 0; } if (isset($args['flag'])) { $strName = $args['flag']; $strVal = W3GetVal($strName); if (is_null($strVal) || ($strVal == )) { $ifFlag = FALSE; $dbgType = 'blank'; } else if (is_numeric($strVal)) { $ifFlag = ($strVal != 0); $dbgType = 'numeric'; } else { $ifFlag = TRUE; $dbgType = ; }

$strTrace = ; if ($wgW3_ifDepth != 0) { $strTrace .= '.'.$wgW3_ifDepth; } $strTrace .= ' flag='.$strName; $strTrace .= ' val='.'['.$strVal.']';

W3AddTrace('IF'.$strTrace.' != 0: ['.TrueFalse($ifFlag).']:'.$dbgType); } elseif (isset($args['comp'])) { // We need to be able to pass either constants or variables via these parameters, so use GetExpr not GetVal $strName = $args['comp']; $strVal1 = W3GetExpr($strName);

$strTrace = ; if ($wgW3_ifDepth != 0) { $strTrace .= '.'.$wgW3_ifDepth; }

$strTrace .= ' ('.$strName.'="'.$strVal1.'")'; $strName = $args['with']; $strVal2 = W3GetExpr($strName); $strTrace .= ' ('.$strName.'="'.$strVal2.'")'; if (isset($args['pre'])) { $wgW3_ifDepth++; $strVal1 = $parser->recursiveTagParse($strVal1); $strVal2 = $parser->recursiveTagParse($strVal2); $wgW3_ifDepth--; // $strVal1 = $parser->replaceVariables($strVal1); // $strVal2 = $parser->replaceVariables($strVal2);

} $ifFlag = ($strVal1 == $strVal2); W3AddTrace('IF'.$strTrace.':'.TrueFalse($ifFlag)); } if (isset($args['not'])) { $ifFlag = !$ifFlag; // invert the flag } $wgW3_ifFlag[$wgW3_ifDepth] = $ifFlag; if ($ifFlag) { $wgW3_ifDepth++; $out = $parser->recursiveTagParse($input); $wgW3_ifDepth--; } else { $out = NULL; }

if ($doHide) { $out = W3GetEcho(); return $out; } else { return $out; } } function efW3Else( $input, $args, $parser ) { global $wgW3_ifFlag, $wgW3_ifDepth;

$doHide = isset($args['hide']); // only output <echo> sections

$ifFlag = $wgW3_ifFlag[$wgW3_ifDepth]; W3AddTrace(' ELSE('.$wgW3_ifDepth.'): ['.$ifFlag.']'); if ($ifFlag) { W3AddTrace('ELSE skipped'); $out = NULL; } else { W3AddTrace('ELSE executed'); $wgW3_ifDepth++; $out = $parser->recursiveTagParse($input); $wgW3_ifDepth--; W3AddTrace('ELSE: OUT = ['.$out.']('.ShowHTML($out).')'); }

if ($doHide) { $out = W3GetEcho(); return $out; } else { return $out; } } function efW3For( $input, $args, $parser ) { /*

TO DO: There needs to be a descendent data class which can handle MW databases so we can

switch between internal and external data just by selecting the appropriate class. As it is, there's a lot of untidy and almost-duplicate code for each case.

  • /

global $wgW3_data; global $wgW3Vars; global $wgW3DBs;

$objArgs = new W3HookArgs($args);

W3AddTrace('FOR:'); $doHide = isset($args['hide']); // only output <echo> sections $doArr = isset($args['array']); if ($doArr) { $strArr = $args['array']; } if (W3Status_SQLOk()) { // for now, only look for SQL stuff if page is protected // TO DO: display error message on unprotected pages if SQL is used $doTbl = isset($args['table']); if ($doTbl) { $strTbl = $args['table']; } $strWhere = $objArgs->GetExpr('where', TRUE); } else { $doTbl = FALSE; } $strSort = $objArgs->GetExpr('sort', TRUE); $strLimit = $objArgs->GetExpr('limit', TRUE); $strName = $objArgs->GetVal('name'); // name of variable for storing data $strEmpty = $objArgs->GetVal('empty'); // string to return if there is no data if ($doTbl) { if (isset($args['db'])) { $useMWDB = FALSE; $strDBName = $objArgs->GetVal('db'); $strDBSpec = $wgW3DBs[$strDBName]; $dbr = new clsDatabase($strDBSpec); $dbr->Open(); $sqlWhat = $strTbl; // TO DO: make sure table exists } else { $useMWDB = TRUE; $dbr =& wfGetDB( DB_SLAVE ); if ($dbr->tableExists($strTbl)) { $sqlWhat = $strTbl; } else { } } if ($strWhere != ) { // $sqlWhere = $dbr->addQuotes($strWhere); $sqlWhere = $strWhere; // TODO: need some way to harden against injection attack } $sqlFull = 'SELECT * FROM '.$sqlWhat; if ($sqlWhere) { $sqlFull .= ' WHERE '.$sqlWhere; } if ($strSort) { $sqlFull .= ' ORDER BY '.$strSort; } if ($strLimit) { $sqlFull .= ' LIMIT '.$strLimit; } W3AddTrace(' - SQL=['.$sqlFull.']');

$out = NULL;

if ($useMWDB) { try { // $res = $dbr->query($sqlWhat,$sqlWhere); $res = $dbr->query($sqlFull); } catch (Exception $e) { $sqlSim = 'SELECT * FROM '.$sqlWhat; if ($sqlWhere) { $sqlSim .= ' WHERE '.$sqlWhere; } $out = "W3TPL encountered a database error - ".$dbr->lastError()." - from this SQL:\n* ".$sqlSim; return $parser->recursiveTagParse($out); } W3AddTrace(' - rows: '.$dbr->numRows( $res )); if ($dbr->numRows( $res ) <= 0) { $dbr->freeResult( $res ); return $parser->recursiveTagParse($strEmpty); }

while( $row = $dbr->fetchObject ( $res ) ) { W3AddTrace('FOR: row->['.$strName.']'); $wgW3_data[$strName] = $row; $strParsed = $parser->recursiveTagParse($input); if ($doHide) { $out .= W3GetEcho(); W3AddTrace(' - FOR echo: ['.ShowHTML($out).']'); } else { $out .= $strParsed; W3AddTrace(' - FOR parse: ['.ShowHTML($out).']'); } } $dbr->freeResult( $res ); } else { $res = $dbr->_api_query($sqlFull); if (mysql_num_rows( $res ) <= 0) { return $parser->recursiveTagParse($strEmpty); } while ($row = mysql_fetch_assoc($res)) { $wgW3_data[$strName] = $row; $strParsed = $parser->recursiveTagParse($input); if ($doHide) { $out .= W3GetEcho(); W3AddTrace(' - FOR echo: ['.ShowHTML($out).']'); } else { $out .= $strParsed; W3AddTrace(' - FOR parse: ['.ShowHTML($out).']'); } } } //$dbr->freeResult( $res ); // this actually causes *more* memory to be used //unset($wgW3_data); // and so does this! } if ($doArr) { if (isset($args['index'])) { $strIdxName = $args['index']; } //$wgW3Vars[$strArr][0] = 'zero'; $arr = $wgW3Vars[$strArr]; if (is_array($arr)) { $idx = 0; foreach ($arr as $name => $value) { $idx++; if ($strLimit) { if ($idx > $strLimit) { break; } } $strTrace = 'FOR: row->['.$strArr.']'; if ($strIdxName) { $wgW3Vars[$strIdxName] = (string)$name; $strTrace .= ' INDEX=['.$name.']=>['.$strIdxName.']'; } $strParsed = $parser->recursiveTagParse($input); W3AddTrace($strTrace); if (!isset($out)) { $out = NULL; } if ($doHide) { $out .= W3GetEcho(); W3AddTrace('FOR echo: ['.ShowHTML($out).']'); } else { $out .= $strParsed; W3AddTrace('FOR parse: ['.ShowHTML($out).']'); } } } else { W3AddTrace('FOR array=['.$strArr.']: not an array!'); } } return $out; } function efW3Func( $input, $args, $parser ) { /*

Function output is not sent to web output because typically we want to be able to format it nicely,
 which usually means lots of extra blank lines and indents.
  • /

global $wgW3_funcs;

$pcnt = 0; $funcArgs = NULL; // declare var in case there are no args foreach ($args as $name => $value) { if ($pcnt) { /* The parser apparently sets the argument's value to its name if no value is specified. This is a sort of bug for this purpose, but maybe it makes sense in other contexts. The real way to get around it is to use <w3tpl> block syntax instead of the <func> tag.

  • /

if ($value != $name) { $funcArgs[$name] = $value; } else { $funcArgs[$name] = null; } } else { $funcName = strtolower($name); } $pcnt++; } if (isset($funcName)) { W3AddTrace('FUNC “'.$funcName.'”'); $objFunc = new clsW3Function($parser,$funcName,$funcArgs,$input); $wgW3_funcs[$funcName] = $objFunc; } else { W3AddTrace('FUNC: function name not set! input=['.$input.']'); } return NULL; } function efW3Call( $input, $args, $parser ) { global $wgW3_funcs,$wgW3_func;

W3AddTrace('{CALL:'); $pcnt = 0; $strTrace = NULL; // $wgW3_echoOutput = NULL; foreach ($args as $name => $value) { $strName = strtolower(trim($name)); if ($pcnt) { $strVal = W3GetExpr($value); $strTrace .= $objFunc->LoadArg($strVal,$strName); $strTrace .= ' '; $strTrace .= ')'; } else { $funcName = $strName; W3AddTrace(' - (CALL) '.$funcName); $objFunc = $wgW3_funcs[$funcName]; $wgW3_func = $objFunc; if (!is_object($objFunc)) { $out = 'W3TPL ERROR: Function "'.$funcName.'" is undefined.'; return $out; } $objFunc->ResetArgs(); } $pcnt++; } if ($input) { // $strTrace .= ' INPUT: {'.ShowHTML($input).'}'; $res = $parser->recursiveTagParse($input); // $strTrace .= ' PARSED: {'.ShowHTML($res).'}'; } $out = $objFunc->execute(); W3AddTrace($strTrace.' CALL}'); $out = W3GetEcho(); $wgW3_func = NULL; // no active function (is this still used?) return $out; } function efW3Arg( $input, $args, $parser ) { global $wgW3_func;

if (isset($args['name'])) { $name = $args['name']; } else { $name = ; } if (isset($args['pre'])) { $value = $parser->recursiveTagParse($input); } else { $value = $input; } $strTrace = ' +ARG: '.$wgW3_func->LoadArg($value,$name); W3AddTrace($strTrace); } function efW3Load( $input, $args, $parser ) { /* NOTE: Some pages apparently don't create the parser; if this code needs to run on one of those pages, then this may need to create $wgParser if it doesn't exist. For now, we assume optimistically.

  TO DO:

Add "let=" option to assign page contents to a variable Make parsing optional for protected pages

  • /

global $wgTitle, $wgOut;

$strTitle = $args['page']; W3AddTrace('LOAD: page={'.$strTitle.'}'); $strTitle = W3GetExpr($strTitle); $doEcho = isset($args['echo']);

W3AddTrace('LOAD -> {'.$strTitle.'}');


$objTitle = Title::newFromText($strTitle); if (is_object($objTitle)) { if (stripos($strTitle, 'special:')===0) { /* title is Specialpage; you'd think there would be a general page-loading method which handles these, but I haven't been able to find it. */ W3AddTrace('LOAD SpecialPage ID='.$objTitle->getArticleID()); //$txtContent = SpecialPage::capturePath($objTitle);


$wgTitleOld = $wgTitle; $wgOutOld = $wgOut; $wgOut = new OutputPage;

$ret = SpecialPage::executePath( $objTitle, FALSE ); if ( $ret === true ) { $ret = $wgOut->getHTML(); } $wgTitle = $wgTitleOld; $wgOut = $wgOutOld;

//SpecialPage::executePath( $objTitle, false ); $txtContent = $ret; if ($txtContent === FALSE) { W3AddTrace('LOAD: Content not loadable'); } else { W3AddTrace('LOAD: Content has '.strlen($txtContent).' bytes'); } } else { W3AddTrace('LOAD Title ID='.$objTitle->getArticleID()); $objArticle = new Article($objTitle); $txtContent = $objArticle->getContent(); W3AddTrace('LOAD: page (Title ID='.$objTitle->getArticleID().') has '.strlen($txtContent).' bytes, starting with: '.substr($txtContent,0,40)); } $txtContent .= $input; // any additional input to be parsed in page's context

// eventually we will have a way to retrieve the contents via a variable // until then, the "raw" option is just for debugging if (isset($args['raw'])) { $out = $txtContent; } else { if (isset($args['local'])) { // parse title in its own context, not in the parent page's context W3AddTrace(' - LOAD as LOCAL');

// temporarily replace $wgTitle with the page we're parsing $objTitleOuter = $wgTitle; $wgTitle = $objTitle; $out = $parser->recursiveTagParse($txtContent); $wgTitle = $objTitleOuter; // restore $wgTitle's original value

} else { $out = $parser->recursiveTagParse($txtContent); } if (isset($args['nocat'])) { // clear out any categories added by parsing of loaded text $parser->mOutput->mCategories = array(); //$parser->mOutput->setCategoryLinks(NULL); } } } else { $out = 'Title ['.ShowHTML($strTitle).'] does not exist.'; // change this to a proper system message at some point } if (isset($args['let'])) { $strName = $args['let']; W3SetVar($strName, $out); } W3AddTrace('LOAD END'); if ($doEcho) { return $out; } else { return NULL; } } class w3ArticleEdit { private $vArticle; private $vContent; private $vSummary; private $vFlags; /**/ public function __construct($iArticle, $iContent, $iSummary, $iFlags) { $this->vArticle = $iArticle; $this->vContent = $iContent; $this->vSummary = $iSummary; $this->vFlags = $iFlags; }

public function Exec() { $ok = $this->vArticle->doEdit( $this->vContent, $this->vSummary, $this->vFlags ); $strStatus = $ok?'ok':'fail - no save'; return $ok; } } function efW3Save( $input, $args, $parser ) { global $wgW3_edit_queue;

$strTitle = $args['page']; W3AddTrace('SAVE: page={'.$strTitle.'}'); $strTitle = W3GetExpr($strTitle); W3AddTrace('SAVE -> {'.$strTitle.'}');

$txtSummary = NULL; $intFlags = EDIT_DEFER_UPDATES; // does this prevent the parser from getting confused? if (isset($args['text'])) { $txtContent = W3GetExpr($args['text']); } else { $txtContent = ; } if (isset($args['insert'])) { $txtContent = W3GetExpr($args['insert']).$txtContent; } if (isset($args['append'])) { $txtContent .= W3GetExpr($args['append']); } if (isset($args['comment'])) { $txtSummary = $args['comment']; } else { $intFlags = $intFlags | EDIT_AUTOSUMMARY; } if (isset($args['minor'])) { $intFlags = $intFlags | EDIT_MINOR; }

$objTitle = Title::newFromText($strTitle); $ok = FALSE; $strStatus = 'fail - no article ['.$strTitle.']'; if (is_object($objTitle)) { $objArticle = new Article($objTitle); if (is_object($objArticle)) { $wgW3_edit_queue[] = new w3ArticleEdit($objArticle,$txtContent,$txtSummary,$intFlags); $ok = TRUE; $strStatus = 'ok'; } else { $ok = FALSE; $strStatus = 'fail - could not load ['.$strTitle.']'; } /* $objArticle = new Article($objTitle); $ok = $objArticle->doEdit( $txtContent, $txtSummary, $intFlags ); $strStatus = $ok?'ok':'fail - no save';

  • /

} if (isset($args['ok'])) { $strName = $args['ok']; W3SetVar($strName, $ok); } if (isset($args['status'])) { $strName = $args['status']; W3SetVar($strName, $strStatus); } } function efW3Xploop( $input, $args, $parser ) { global $wgW3Vars;

$objArgs = new W3HookArgs($args);

$strListRaw = $objArgs->GetVal('list'); $strList = W3GetExpr($strListRaw); $strTok = $objArgs->GetVal('repl'); $doEcho = isset($args['echo']);

if (isset($args['var'])) { $strVar = strtolower($args['var']); } else { $strVar = NULL; } $sepStr = $objArgs->GetVal('sep');

if ($strTok) { // doing a straight token replacement $strTrace = 'XPLOOP replace ('.ShowHTML($strTok).') ← “'.ShowHTML($strList).'”'; } else { // setting variable value $strTrace = 'XPLOOP set ('.ShowHTML($strVar).') ← “'.ShowHTML($strList).'”'; } if ($strList != $strListRaw) { $strTrace .= '←“'.$strListRaw.'”'; } W3AddTrace($strTrace); if (isset($args['parselist'])) { $strList = $parser->recursiveTagParse( $strList ); }

$tok = substr ( $strList, 0, 1); // token for splitting if ($tok) { $tks = substr ( $strList, 1 ); // tokenized string $list = explode ( $tok, $tks ); // split the string $out = ; } else { return NULL; } if ($strTok) { // doing a straight token replacement foreach ($list as $value) { if ($out) { $out .= $sepStr; } $out .= str_replace( $strTok, $value, $input ); } $out = CharacterEscapes::charUnesc( $out, array(), $parser ); $out = $parser->recursiveTagParse( $out ); } else { foreach ($list as $value) { if ($out) { $out .= $sepStr; } $wgW3Vars[$strVar] = $value; W3AddTrace(' - XP: ['.$strVar.'] <- ['.$value.']'); $out .= $parser->recursiveTagParse( $input ); } } if ($doEcho) { return $out ; } else { $out = W3GetEcho(); return $out; } }

function efW3TPLRender( $input, $args, $parser ) { global $w3step;

if (isset($args['nocache'])) { $parser->disableCache(); } if (isset($args['step'])) { $w3step = true; // show each line of code before it is executed } if ($input == ) { $out = NULL; } else { $out = $input;

if (isset($args['pre'])) { $out = $parser->recursiveTagParse( $out ); } if (!isset($args['notpl'])) { $out = ActualRender($out,$args); }

  1. $out = ActualRender($out);

if (isset($args['post'])) { $out = $parser->recursiveTagParse( $out ); } } return $out; } /*

Each of these classes is initialized by passing it the remaining text
They each parse that text into an array of objects, and return any remaining text for the caller to finish parsing.
  • /

abstract class clsW3Code { private $vText;

public function __construct($iText) { $this->vText = $iText; } public function Dump() { return $vText; } } abstract class clsW3Code_sub extends clsW3Code { // Code with ending delimiter, after which remaining text must be parsed by caller private $vCloser; public $strRemain;

public function __construct($iText,$iCloser=) { $this->vText = $iText; $this->vCloser = $iCloser; } } class clsW3Code_body extends clsW3Code { private $vLines;

public function parse() { $strToParse = $this->vText;

$objChunk = NULL; while ($strToParse != ) { $objLine = new clsW3Code_line($strToParse); $strToParse = $objLine->strRemain; $this->vLines[] = $objLine; } } public function Dump() {

$out = '

    '; foreach ($this->vLines as $line) { $out .= '
  • '.$line->Dump(); } $out .= '

';

return $out; } } class clsW3Code_line extends clsW3Code_sub { private $vTokens;

public function parse() { $strToParse = $this->vText; $isDone = FALSE; $objChunk = NULL; for ($i = 0; ($ch=substr($strToParse,$i,1)) && !$isDone; $i++) { $doAdd = TRUE; // TRUE = add this character to the token buffer $isTok = FALSE; // TRUE = a token has been completed; save it and clear buffer switch ($ch) { // whitespace case ' ': // space case "\t": // tab $isTok = TRUE; $doAdd = FALSE; // don't include unquoted whitespace break; case '"': $isTok = TRUE; $doAdd = FALSE; // don't include paren in token $objChunk = new clsW3_qstring(substr($input,$i+1)); $strToParse = $objChunk->strRemain; break; case '(': $isTok = TRUE; // start new token $doAdd = FALSE; // don't include paren in token $objChunk = new clsW3Code_line(substr($input,$i+1),')'); $strToParse = $objChunk->strRemain; break; case '{': $isTok = TRUE; // start new token $doAdd = FALSE; // don't include braces in token $objChunk = new clsW3Code_body(substr($input,$i+1),'}'); $strToParse = $objChunk->strRemain; break; case ';': $isTok = TRUE; // start new token $doAdd = FALSE; // don't include braces in token $isDone = TRUE; break; case "\n": $doAdd = false; // don't include linebreaks in $clause break; default: $isDone = ($ch == $this->vCloser); } if ($doAdd) { $token .= $ch; } if ($isTok) { // TRUE = a token has been completed; save it and clear buffer if ($token) { $objTok = new clsW3_token($token); $this->vTokens[] = $objTok; } $token = NULL; } if (is_object($objChunk)) { $this->vTokens[] = $objChunk; $objChunk = NULL; } } $this->strRemain = substr($strToParse,$i+1); } } class clsW3_token extends clsW3Code {

} class clsW3_phrase extends clsW3Code { private $vList;

} /* class clsW3Token_rawexp extends clsW3Token { public __construct($iText) { } }

  • /

class clsW3_qstring extends clsW3Code { public function parse() { $isDone = FALSE; for ($i = 0; ($ch=substr($input,$i,1)) && !$isDone; $i++) { switch ($ch) { case '\\': $inEsc = true; break; case '"': $isDone = !$inEsc; } } } }


function ActualRender($input) { // break the code up into separate commands if ($doLine) { // TRUE = line is complete //W3AddTrace('TPL: LINE=['.$strDbgLine.']'); $out .= $cmdObj->execute($lines); } // final semicolon not required: if ($line) { $out .= $cmdObj->execute($line,$clause); } return $out; }

/*

TOKEN TYPES - for future use:

() phrase {} brace phrase non-quoted expression quoted string

  • /

class clsW3ParsedLine { private $tokens;

public function __construct($iTokens) { $this->tokens = $iTokens; } } class clsW3ParsedChunk { private $lines; // array of clsW3ParsedLines

public function __construct($iLines) { $this->lines = $iLines; } }

class clsW3Command { private $statemt; private $clause;

public function clsW3Command($iStatemt=, $iClause=) { $this->init($iStatemt, $iClause); } public function init($iStatemt, $iClause) { $this->statemt = trim($iStatemt); $this->clause = $iClause; } public function execute($iStatemt, $iClause) { // init + exec $this->init($iStatemt, $iClause); return $this->exec(); } public function exec() { global $w3step;

$strStmt = $this->statemt; if ($w3step) { $out = '
CMD: {'.$strStmt.'} CLAUSE: {'.$this->clause.'}'; } /* A command is in any of these forms: variable action-operator expression; expression; keyword (expression) {clause} [keyword (expression) {clause} ... ]; An expression is in any of these forms: function(expression[, expression[, ...]]) variable compare-operator expression variable constant A constant is either a quoted string or a number A variable is invoked by name


  • /

// parse the command: for ($i = 0; $ch=substr($strStmt,$i,1); $i++) { $isArgs = FALSE; if ($isTok) { //if (!$inQuo) { if ($cmd == ) { $cmd = $phrase; $phrase = ; } } elseif ($isArgs) { $args = $phrase; $phrase = ; } else { $phrase .= $ch; } } W3AddTrace('TPL layer 2: CMD=['.$cmd.'] ARGS=['.$args.'] leftover=['.$phrase.']'); // return $out; } } // TO DO: Change name to ShowMarkup, because it handles HTML, wikitext, and partly-parsed wikitext function ShowHTML($iText, $iRepNewLine=TRUE) {

/*/ // DEBUGGING - explode string by inserting space between each character: $cpLen = strlen($iText); $out2 = ; for ($i = 0; $i <= $cpLen; $i++) { $out2 .= substr($iText,$i,1).' '; } $out = $out2; /**/ // $out = 'LENGTH='.strlen($iText);

$out = $iText; $out = str_replace ( '<','<',$out ); $out = str_replace ( '>','>',$out); $out = str_replace ( '[','[',$out ); $out = str_replace ( ']',']',$out ); $out = str_replace ( chr(7),'^G',$out); $out = str_replace ( chr(127),'del',$out); if ($iRepNewLine) { $out = str_replace ( chr(10),'^J',$out); $out = str_replace ( chr(13),'^M',$out); } return $out; }

class clsStringTemplate_w3tpl extends clsStringTemplate { // This version can be used if the values are in an associative array protected function GetValue($iName) { return W3GetVal($iName); } }

/* class w3expr { private $text; abstract function Parse($iTask); } */

class clsW3Function { var $vParser; var $isOkRaw, $isOkSQL; var $Name, $vParams, $vCode; var $vArgs, $vArgIdx;

public function __construct($iParser, $iName, $iParams, $iCode) { $this->vParser = $iParser; $this->Name = $iName; $this->vParams = $iParams; $this->vCode = $iCode; $this->isOkRaw = W3Status_RawOk(); $this->isOkSQL = W3Status_SQLOk(); } public function dump() { $out = ''.$this->Name.'('; if (is_array($this->vParams)) { $pcnt = 0; foreach ($this->vParams as $name => $value) { if ($pcnt) { $out .= ', '; } $out .= ''.$name.''; if (isset($value)) { $out .= '='.$value; } $pcnt++; } } $out .= ') {'; $strCode = ShowHTML($this->vCode,FALSE);

$out .= '

'.$strCode.'

}';

return $out; } public function ResetArgs() { $this->vArgIdx = 0; unset($this->vArgs); } public function LoadArg($iValue,$iName=) { if ($iName) { $strName = strtolower($iName); } else { $strName = $this->ParamName($this->vArgIdx); $this->vArgIdx++; } $this->vArgs[$strName] = $iValue; return '['.$strName.']=“'.$iValue.'”'; } public function HasArg($iName) { return isset($this->vArgs[$iName]); } public function ArgVal($iName) { return $this->vArgs[$iName]; } public function ParamName($iIndex) { $keys = array_keys($this->vParams); $key = $keys[$iIndex]; return $key; } public function execute() { // set variables from passed arguments

$useArgs = FALSE; if (isset($this->vArgs)) { if (is_array($this->vArgs)) { $useArgs = TRUE; } } if ($useArgs) { foreach ($this->vArgs as $name => $value) { if (W3VarExists($name)) { $oldVars[$name] = W3GetVal($name); } W3SetVar($name, $value); } } // parse (execute) the function code $out = $this->vParser->recursiveTagParse( $this->vCode ); // restore original variables (old value if any, or remove from list) if ($useArgs) { foreach ($this->vArgs as $name => $value) { if (isset($oldVars[$name])) { W3SetVar($name, $oldVars[$name]); } else { W3KillVar($name); } } } return $out; } }

class clsW3VarName { /*

  • An *expression* can be:
    • a literal value - "this is a string"
    • a reference to a variable - "$theVar"
    • a reference to a special function - @row.fieldname
  • A variable reference can be:
    • scalar value - "$aScalar"
    • array index - "$anArray[index_expression]" where "index_expression" is an expression

We automatically find the value of all inner elements as needed, but leave the outermost unresolved in order to allow for different operations depending on context.

  • /

public $Expr; // code-expression to parse public $Name; // final name of variable or function being referenced public $Index; // (optional) index into variable array public $Value; // loaded value of variable or function, for operations public $isFunc;

public function __construct($iExpr = NULL) { $this->isFunc = FALSE; W3AddTrace('clsW3VarName: init=['.$iExpr.']'); if (!is_null($iExpr)) { $this->ParseExpr($iExpr); } }

public function ParseExpr($iExpr) { global $wgW3_doTrace_vars; global $wgW3Trace_indent;

$wgW3Trace_indent++; $strExpr = $iExpr; if ($wgW3_doTrace_vars) { W3AddTrace(' {PARSE-EXPR ['.ShowHTML($iExpr).']'); } $chFirst = substr($strExpr,0,1); switch ($chFirst) { case '$': $strRef = strtolower(substr($strExpr,1)); $this->ParseName($strRef); break; case '@': $strRef = strtolower(substr($strExpr,1)); $this->ParseName($strRef); $this->isFunc = TRUE; break; default: // it's a literal, so the string is the value $this->Value = $strExpr; } if ($wgW3_doTrace_vars) { W3AddTrace(' => ['.ShowHTML($strExpr).'] EXPR}'); } $wgW3Trace_indent--; return $strExpr; }

public function ParseName($iName) { global $wgW3_doTrace_vars; global $wgW3Trace_indent; $strName = $iName; $wgW3Trace_indent++; if ($wgW3_doTrace_vars) { W3AddTrace('{PARSE-NAME ['.ShowHTML($iName).']'); }

if (substr($strName,0,1) == '@') { $this->isFunc = TRUE; $this->Name = strtolower(substr($strName,1)); W3AddTrace(' - is func ['.$this->Name.']'); } else { if (substr($strName, -1) == ']') { // name includes index offset $idxOpen = strpos($strName,'['); if ($idxOpen) { $idxShut = strpos($strName,']',$idxOpen); $vIndex = substr($strName,$idxOpen+1,$idxShut-$idxOpen-1); $strTrace = 'INDEX['.$vIndex.'] (@'.$idxOpen.'-'.$idxShut.' in ['.$strName.']) -> ['; $strTrace .= $vIndex.']'; $objIdx = new clsW3VarName($vIndex); $strName = substr($strName,0,$idxOpen); $objIdx->Fetch(); // calculate value of index expression $this->Index = $objIdx->Value; $strTrace .= ' NEW NAME=['.$strName.']'; W3AddTrace($strTrace); } } $objVar = new clsW3VarName($strName); $objVar->Fetch(); $this->Name = $objVar->Value; } if ($wgW3_doTrace_vars) { W3AddTrace('name:['.ShowHTML($this->Name).'] = val:['.ShowHTML($this->Value).'] NAME}'); } $wgW3Trace_indent--; /**/ }

public function SetIndex($iValue) { $this->Index = $iValue; W3AddTrace('clsW3VarName.SetIndex of ('.$this->Name.') to ['.$iValue.']'); } public function IsElem() { // is element of an array? return !is_null($this->Index); } public function IsVar() { return !is_null($this->Name) && !$this->isFunc; } public function IsArray() { return is_array($this->Value); } public function Fetch() { global $wgW3Vars;

$strName = strtolower($this->Name); if ($this->isFunc) { $this->Value = W3GetSysData($this->Name); } elseif ($this->IsVar()) { if ($this->IsElem()) { $strIndex = $this->Index; if (isset($wgW3Vars[$strName][$strIndex])) { $this->Value = $wgW3Vars[$strName][$strIndex]; W3AddTrace('clsW3VarName.Fetch ('.$strName.')['.$strIndex.']=>“'.$this->Value.'”'); } else { $this->Value = NULL; W3AddTrace('clsW3VarName.Fetch ('.$strName.')['.$strIndex.']=>NULL'); } } else { if (isset($wgW3Vars[$strName])) { $this->Value = $wgW3Vars[$strName]; W3AddTrace('clsW3VarName.Fetch ('.$strName.')=>“'.$this->Value.'”'); } else { $this->Value = NULL; W3AddTrace('clsW3VarName.Fetch ('.$strName.')=>NULL'); } } } else { // literal value - already set. } W3AddTrace('clsW3VarName.Fetch: value=['.$this->Value.']'); $this->Trace(); }

public function Store() { global $wgW3Vars;

$strName = $this->Name; $strVal = $this->Value; if ($this->isFunc) { W3SetSysData($strName,$strVal); } elseif ($strName) { if ($this->isElem()) { $strIndex = $this->Index; $wgW3Vars[$strName][$strIndex] = $strVal; W3AddTrace('clsW3VarName.Store: '.$strName.'['.$strIndex.'] ← “'.$strVal.'”'); } else { $wgW3Vars[$strName] = $strVal; W3AddTrace('clsW3VarName.Store: ['.$strName.'] ← “'.$strVal.'”'); } $this->Trace(); } else { W3AddTrace('clsW3VarName.Store: ERROR: no name for value “'.$strVal.'”'); $this->Trace(); } }

public function DoSort($iRev=FALSE, $iVal=FALSE) { if (is_array($this->Value)) { if ($iVal) { // sort by value if ($iRev) { $ok = arsort($this->Value); } else { $ok = asort($this->Value); } } else { if ($iRev) { $ok = arsort($this->Value); } else { $ok = ksort($this->Value); } } if ($ok) { W3AddTrace('LET sort ['.$this->Name.'] OK'); } else { W3AddTrace('LET sort ['.$this->Name.'] ERROR: failed.'); } } else { W3AddTrace('LET sort ERROR: ['.$this->Name.'] is not an array.'); } }

public function CheckCopy($iArgs) { if ($iArgs->Exists('copy')) { $strSrce = $iArgs->GetVal('copy'); $this->Value = W3GetVal($strSrce); W3AddTrace('CheckCopy from ['.$strSrce.']'); // show raw input $this->Trace(); // show result return TRUE; } else { return FALSE; } }

public function Trace() { if (is_array($this->Value)) { $strVal = 'array!'; } else { $strVal = $this->Value; }

W3AddTrace('clsW3VarName.Trace: '. 'Name=['.$this->Name.'] '. 'Val=['.$strVal.'] '. 'Index=['.$this->Index.'] '. TrueFalseHTML('is var',$this->IsVar()).' '. TrueFalseHTML('is element',$this->IsElem()).' '. TrueFalseHTML('is func',$this->isFunc) ); } }

function TrueFalseHTML($iName, $iVal) { if ($iVal) { return ''.$iName.''; } else { return ''.$iName.''; } }

class W3HookArgs { var $vArgs;

public function __construct($iArgs) { $this->vArgs = $iArgs; } public function Exists($iName) { return isset($this->vArgs[$iName]); } public function GetVal($iName) { if (isset($this->vArgs[$iName])) { return $this->vArgs[$iName]; } else { return NULL; } } public function GetExpr($iName, $iTrace = FALSE) { if (isset($this->vArgs[$iName])) { $strArg = $this->vArgs[$iName]; $strOut = W3GetExpr($strArg); if ($iTrace) { W3AddTrace(' - '.strtoupper($iName).'=['.$strArg.'] -> ['.$strOut.']'); } return $strOut; } else { return NULL; } } }

/* class W3TPL_fx { function runXploop ( &$parser, $inStr = , $inTplt = '$s$', $inSep = ) { $tok = substr ( $inStr, 0, 1); // token for splitting if ($tok) { $tks = substr ( $inStr, 1 ); // tokenized string $list = explode ( $tok, $tks ); // split the string $sep = CharacterEscapes::charUnesc( $inSep, array(), $parser ); $tplt = CharacterEscapes::charUnesc( $inTplt, array(), $parser ); $out = ; foreach ($list as $value) { // $lcnt++; if ($out) { $out .= $sep; } $out .= str_replace( '$s$', $value, $tplt ); } return $parser->recursiveTagParse($out); // return array($out, 'noparse' => false, 'isHTML' => false); } else { return NULL; } } function runXpCount ( &$parser, $inStr = ) { $tok = substr ( $inStr, 0, 1); return substr_count($inStr, $tok); } function runLet ( &$parser, $iName = , $iVal = ) { global $wgW3Vars;

$strName = strtolower($iName); $strName = W3GetVal($strName); // check for indirection ($) $wgW3Vars[$strName] .= $iVal; } function runGet ( &$parser, $iName = ) { $strName = strtolower($iName); $strVal = W3GetVal('$'.$strName); return $parser->recursiveTagParse($strVal); } }

  • /

/*

Code for preventing raw "isolated" output from being further processed by the parser output stage
See http://www.mediawiki.org/wiki/Manual:Tag_extensions#How_can_I_avoid_modification_of_my_extension.27s_HTML_output.3F
  • /

$wgW3_Markers = array(); define('ksW3_parser_marker_start','@@W3TPL##--'); define('ksW3_parser_marker_stop','--##LPT3W@@'); function IsolateOutput($iText) { global $wgW3_Markers;

$markCount = count($wgW3_Markers); $mark = ksW3_parser_marker_start.$markCount.ksW3_parser_marker_stop; $wgW3_Markers[$markCount] = $iText; return $mark; }

function efW3_ParserAfterTidy(&$parser, &$text) { // find markers in $text // replace markers with actual output global $wgW3_Markers;

// replace markers with isolated text: $k = array(); for ($i = 0; $i < count($wgW3_Markers); $i++) $k[] = ksW3_parser_marker_start . $i . ksW3_parser_marker_stop; $text = str_replace($k, $wgW3_Markers, $text);

return true; } function efW3_OutputPageBeforeHTML(&$out, &$text) { global $wgW3_edit_queue; // execute deferred edits:

if (is_array($wgW3_edit_queue)) { foreach ($wgW3_edit_queue as $key=>$obj) { $obj->Exec(); } } return TRUE; }</php>